gim是乙個即時通訊伺服器,**全部使用golang完成。主要功能:
支援tcp,websocket接入
離線訊息同步
多業務接入
單聊,群聊,以及超大群聊天場景
支援服務水平擴充套件
資料庫:mysql+redis
通訊框架:grpc
長連線通訊協議:protocol buffers
日誌框架:zap
1.首先安裝mysql,redis
2.建立資料庫gim,執行sql/create_table.sql,完成初始化表的建立(資料庫包含提供測試的一些初始資料)
3.修改config下配置檔案,使之和你本地配置一致
4.分別切換到cmd的tcp_conn,ws_conn,logic目錄下,執行go run main.go,啟動tcp連線層伺服器,websocket連線層伺服器,邏輯層伺服器
專案所有的proto協議在gim/public/proto/目錄下
1.tcp.proto
長連線通訊協議
2.logic_client.ext.proto
對客戶端(android裝置,ios裝置)提供的rpc協議
3.logic_server.ext.proto
對業務伺服器提供的rpc協議
4.logic.int.proto
對conn服務層提供的rpc協議
5.conn.int.proto
對logic服務層提供的rpc協議
專案結構遵循
api: 服務對外提供的grpc介面
cmd: 服務啟動入口
config: 服務配置
internal: 每個服務私有**
pkg: 服務共有**
sql: 專案sql檔案
test: 長連線測試指令碼
1.tcp_conn
維持與客戶端的tcp長連線,心跳,以及tcp拆包粘包,訊息編譯碼
2.ws_conn
維持與客戶端的websocket長連線,心跳,訊息編譯碼
3.logic
裝置資訊,使用者資訊,群組資訊管理,訊息**邏輯
遵循lv的協議格式,乙個訊息包分為兩部分,訊息位元組長度以及訊息內容。 這裡為了減少記憶體分配,拆出來的包的記憶體復用讀快取區記憶體。
拆包流程:
1.首先從系統快取區讀取位元組流到buffer
2.根據包頭的length欄位,檢查報的value欄位的長度是否大於等於length
3.如果大於,返回乙個完整包(此包記憶體復用),重複步驟2
4.如果小於,將buffer的有效位元組前移,重複步驟1
首先解釋一下,什麼是讀擴散,什麼是寫擴散
讀擴散簡介:群組成員傳送訊息時,先建立乙個會話,都將這個訊息寫入這個會話中,同步離線訊息時,需要同步這個會話的未同步訊息
優點:每個訊息只需要寫入資料庫一次就行,減少資料庫訪問次數,節省資料庫空間
缺點:乙個使用者有n個群組,客戶端每次同步訊息時,要上傳n個序列號,伺服器要對這n個群組分別做訊息同步
寫擴散簡介:在群組中,每個使用者維持乙個自己的訊息列表,當群組中有人傳送訊息時,給群組的每個使用者的訊息列表插入一條訊息即可
優點:每個使用者只需要維護乙個序列號和訊息列表
缺點:乙個群組有多少人,就要插入多少條訊息,當群組成員很多時,db的壓力會增大
普通群組:
採用寫擴散,群組成員資訊持久化到資料庫儲存。支援訊息離線同步。
超大群組:
採用讀擴散,群組成員資訊儲存到redis,不支援離線訊息同步。
長連線登入
離線訊息同步
心跳訊息單發
c1.d1和c1.d2分別表示c1使用者的兩個裝置d1和d2,c2.d3和c2.d4同理
小群訊息**
c1,c2.c3表示乙個群組中的三個使用者
大群訊息**
系統中的錯誤一般可以歸類為兩種,一種是業務定義的錯誤,一種就是未知的錯誤,在業務正式上線的時候,業務定義的錯誤的屬於正常業務邏輯,不需要列印出來, 但是未知的錯誤,我們就需要列印出來,我們不僅要知道是什麼錯誤,還要知道錯誤的呼叫堆疊,所以這裡我對grpc的錯誤進行了一些封裝,使之包含呼叫堆疊。
func wraperror(err error) error這樣,不僅可以拿到錯誤的堆疊,錯誤的堆疊也可以跨rpc傳輸,但是,但是這樣你只能拿到當前服務的堆疊,卻不能拿到呼叫方的堆疊,就比如說,a服務呼叫 b服務,當b服務發生錯誤時,在a服務通過日誌列印錯誤的時候,我們只列印了b服務的呼叫堆疊,怎樣可以把a服務的堆疊列印出來。我們在a服務呼叫的地方也獲取 一次堆疊。s := &spb.status,
},} return status.fromproto(s).err()
}// stack 獲取堆疊資訊
func stack() string
} return build.string()
}
func wraprpcerror(err error) error像這樣,就可以獲取完整一次呼叫堆疊。 錯誤列印也沒有必要在函式返回錯誤的時候,每次都去列印。因為錯誤已經包含了堆疊資訊e, _ := status.fromerror(err)
s := &spb.status,
},} return status.fromproto(s).err()
}func interceptor(ctx context.context, method string, req, reply inte***ce{}, cc *grpc.clientconn, invoker grpc.unaryinvoker, opts ...grpc.calloption) error
var logicintclient pb.logicintclient
func initlogicintclient(addr string)
logicintclient = pb.newlogicintclient(conn)
}
// 錯誤的方式然後,我們在上層統一列印就可以if err != nil
// 正確的方式
if err != nil
func startserverextserver := grpc.newserver(grpc.unaryinterceptor(logicclientextinterceptor))
pb.registerlogicclientextserver(extserver, &logicclientextserver{})
err = extserver.serve(extlisten)
if err != nil
}func logicclientextinterceptor(ctx context.context, req inte***ce{}, info *grpc.unaryserverinfo, handler grpc.unaryhandler) (resp inte***ce{}, err error) ()
resp, err = handler(ctx, req)
logger.logger.debug("logic_client_ext_interceptor", zap.any("info", info), zap.any("ctx", ctx), zap.any("req", req),
zap.any("resp", resp), zap.error(err))
s, _ := status.fromerror(err)
if s.code() != 0 && s.code() < 1000
return
}
func getctx() context.context
mysql多伺服器 mysql 多伺服器例項
測試環境是在windows下。不同系統啟動和停止命令有所差異 1 修改配置檔案 把需要配置的選項都設定成不一樣的 需要修改的地方有 client password your password port 3308 經過測試,這個埠改不改都沒什麼問題,但是還是改了保險點吧 socket d servic...
mysql多伺服器 單伺服器多mysql伺服器
mkdir home mysql servers mysql330 p useradd g mysql mysql3307 s bin nologin d home mysql servers mysql3307 tar xf mysql.tar.gz cd mysql configure pref...
伺服器系統支援
網路作業系統 nos 是網路的心臟和靈魂,是向網路計算機提供網路通訊和網路資源共享功能的作業系統。它是負責管理整個網路資源和方便網路使用者的軟體的集合。由於網路作業系統是執行在伺服器之上的,所以有時我們也把它稱之為伺服器作業系統。網路作業系統與執行在工作站上的單使用者作業系統 如windows98等...