golang實現的長連線服務
我最近分享了我用golang實現的長連線服務,希望對大家有用。
connsvr 長連線服務(http://github.com/simplejia/connsvr)
功能
- 支援tcp自定義協議長連線
- 支援http協議長連線(long poll機制,ajax掛上去後等待資料返回)
- 每個使用者建立一個連線,每個連線唯一對應一個使用者,使用者可以同時加入多個房間
- 推送資料時,可以不給房間內特定的一個使用者推資料,適用於:前端假寫資料,長連線服務幫過濾掉這條訊息
- 接收到上行資料後,同步轉發給相應業務處理服務,可通過conf/conf.json配置pubs節點,connsvr將資料通過http方式路由到後端業務處理服務,然後透傳結果到客戶端
- 支援定期拉取遠端伺服器配置資料
- 根據遠端配置資訊,可給客戶端下發訊息拉取方式的指令,目前支援:
- 推送通知,然後客戶端主動拉後端服務,適用於:對訊息一致性要求高,這種方式,可以保證使用者拉到完整的訊息列表,不會由於推送失敗而丟訊息,後端服務需要提供客戶端資料拉取的完整功能
- 推送整條訊息,客戶端不用拉,適用於:對訊息一致性要求不高,丟一兩條沒關係,這種方式,基本只需要有connsvr就夠了,而且對connsvr的要求不高
- 推送通知,然後客戶端來connsvr拉訊息,適用於:對訊息一致性上有要求,但允許在瞬間訊息量比較大的情況下丟掉部分老的訊息,這種方式,基本只需要有connsvr就夠了,對connsvr要求較高
實現
- 啟用一個協程用於接收後端push資料,啟用若干個協程用於管理房間使用者,使用者被hash到對應協程
- 每個協程需要通過管道接收資料,包括:加入房間,退出房間,推送訊息
- 每個使用者連線啟一個讀協程
- 無鎖
特點
- 通訊協議足夠簡單高效
- 服務設計儘量簡化,通用性好
協議
http長連線
** 加入房間 **
http://xxx.xxx.com/enter%3Frid ... 3Dxxx
請求引數說明:
rid: 房間號
uid: 使用者id
sid: session_id,區分同一uid不同連線,[可選]
callback: jsonp回撥函式,[可選]
返回資料說明:
[callback(][json body][)]
示例如下: cb({"body":"hello world","cmd":"2","rid":"r1","sid":"","subcmd":"0","uid":"r2"})
** 拉取訊息 **
http://xxx.xxx.com/msgs%3Frid% ... 3Dxxx
請求引數說明:
rid: 房間號
uid: 使用者id
sid: session_id,區分同一uid不同連線,[可選]
subcmd: 用於區分不同業務,有效資料:1~255之間
mid: 客戶端讀到的最後一條訊息,沒有傳空
callback: jsonp回撥函式,[可選]
返回資料說明:
[callback(][json body][)]
示例如下: cb({"body":["hello world"],"cmd":"5","rid":"r1","sid":"","subcmd":"0","uid":"r2"})
test資料夾有個ajax長輪詢示例:ajax.html,使用方式如下:
- 首先配置host: 127.0.0.1 connsvr.com
- 啟動connsvr: ./connsvr -env dev
- 瀏覽器裡開啟ajax.html
- 執行包含push訊息的測試用例:go test -env dev -v -run=TestTcp
經過上面幾步,瀏覽器內容會更新成如下:
{"body":"hello world","cmd":"99","ext":"{\"GetMsgKind\":2}","rid":"r1","sid":"0.3209966821165452","subcmd":"0","uid":"u1"} refresh time: 上午2:05:59
重複執行測試用例,你能看到訊息在更新(refresh time顯示的時間在變)
tcp自定義協議長連線(包括收包,回包)
Sbyte+Length+Cmd+Subcmd+UidLen+Uid+SidLen+Sid+RidLen+Rid+BodyLen+Body+ExtLen+Ext+Ebyte
Sbyte: 1個位元組,固定值:0xfa,標識資料包開始
Length: 2個位元組(網路位元組序),包括自身在內整個資料包的長度
Cmd: 1個位元組,
* 0x01:心跳
* 0x02:加入房間
* 0x03:退出房間
* 0x04:上行訊息
* 0x05:拉取訊息列表
* 0xff:標識服務異常
Subcmd: 1個位元組,路由不同的後端介面,見conf/conf.json pubs和msgs節點,
* pubs代表上行訊息配置,中轉給業務方資料示例如下:uid=u1&rid=r1&cmd=99&subcmd=0&body=hello,直接把後端返回傳回client
* msgs代表拉訊息列表配置,中轉給業務方資料示例如下:uid=u1&rid=r1&cmd=99&subcmd=0,返回給client示例如下:["xxx", "yyy"]
UidLen: 1個位元組,代表Uid長度
Uid: 使用者id,對於app,可以是裝置id,對於瀏覽器,可以是登陸使用者id
SidLen: 1個位元組,代表Sid長度
Sid: session_id,區分同一uid不同連線,對於瀏覽器,可以是生成的隨機串,瀏覽器多視窗,多標籤需單獨生成隨機串
RidLen: 1個位元組,代表Rid長度
Rid: 房間id
BodyLen: 2個位元組(網路位元組序),代表Body長度
Body: 和業務方對接,connsvr會中轉給業務方
ExtLen: 2個位元組(網路位元組序),代表Ext長度
Ext: 擴充套件欄位,當來自於connsvr時,目前支援如下:
{
"GetMsgKind": 1 // 1: 推送通知,然後客戶端主動拉後端服務 2: 推送整條訊息,客戶端不用拉 3: 推送通知,然後客戶端來connsvr拉訊息
}
Ebyte: 1個位元組,固定值:0xfb,標識資料包結束
注1:上行資料包長度,即Length大小,限制4096位元組內(可配置),下行不限
注2:當connsvr服務處理異常,比如呼叫後端服務失敗,返回給client的資料包,Cmd:0xff
注3:當Cmd為0x05時,客戶端到connsvr拉取訊息列表,當connsvr訊息為空時,connsvr為根據conf/conf.json msgs節點配置路由到後端服務拉取訊息列表
後端push協議格式(udp)
Cmd+Subcmd+UidLen+Uid+SidLen+Sid+RidLen+Rid+BodyLen+Body+ExtLen+Ext:
Cmd: 1個位元組,經由connsvr直接轉發給client
Subcmd: 1個位元組,經由connsvr直接轉發給client
UidLen: 1個位元組,代表Uid長度
Uid: 指定排除的使用者uid
SidLen: 1個位元組,代表Sid長度
Sid: 指定排除的使用者session_id,當沒有傳入Sid時,只匹配uid
RidLen: 1個位元組,代表Rid長度
Rid: 房間id
BodyLen: 2個位元組(網路位元組序),代表Body長度
Body: 和業務方對接,connsvr會中轉給client
ExtLen: 2個位元組(網路位元組序),代表Ext長度
Ext: 擴充套件欄位,目前支援如下:
{
"MsgId": “1234” // 標識本條訊息id
}
注:資料包長度限制50k內
使用方法
- 配置檔案:conf.json (json格式,支援註釋),可以通過傳入自定義的env及conf引數來重定義配置檔案裡的引數,如:./connsvr -env dev -conf='hport=80;clog.mode=1',多個引數用
;
分隔 - 建議用cmonitor做程式啟動管理
- api資料夾提供的程式碼用於後端服務給connsvr推送訊息的,實際是通過clog服務分發的
- connsvr的上報資料,比如本機ip定期上報(用於更新待推送伺服器列表),連線數、推送用時上報,等等,這些均是通過clog服務中轉實現,所以我提供了clog的handler,均在testdata目錄裡:相應要修改clog的conf.json部分如下:
"connsvr/logbusi_report": [
{
"handler": "connreporthandler",
"params": {
"redis": {"addrtype": "ip", "addr": ":6379"}
}
}
],
"connsvr/logbusi_stat": [
{
"handler": "connstathandler",
"params": {}
}
],
"demo/logbusi_push": [
{
"handler": "connpushhandler",
"params": {
"redis": {"addrtype": "ip", "addr": ":6379"}
}
}
]
相關文章
- Gopusher 一個通用的長連線服務Go
- 基於TCP長連線實現的帶QOS的訊息傳輸服務KTMTTCP
- Golang 連線池的幾種實現案例Golang
- Node.js 連線到 Spring Eureka 實現服務發現Node.jsSpring
- 用 Golang 實現百萬級 Websocket 服務GolangWeb
- 用 golang 去實現類似 swoole 的 websocket 服務 ?GolangWeb
- SSH服務連線
- 連線SD-WAN以實現安全的網路加速服務——VecloudCloud
- golang兩種資料庫連線池實現Golang資料庫
- Golang快速實現一個簡單RPC服務GolangRPC
- golang實現tcp客戶端服務端程式GolangTCP客戶端服務端
- golang快速實現服務端網頁截圖Golang服務端網頁
- IoT雲服務連線性的方式
- mysql怎麼連線服務MySql
- prometheus監控golang服務實踐PrometheusGolang
- 簡單談談服務間的連線
- golang 實現連結串列爽不爽?Golang
- Node.js 服務連線 MySQLNode.jsMySql
- Windows遠端連線Docker服務WindowsDocker
- 生活服務商家如何實現從“網紅”到“長紅”的長效增長?
- golang連線MySQL時候的連線池設定GolangMySql
- 短連結服務Octopus的實現與原始碼開放原始碼
- Node.js 服務連線 MongoDB 處理最佳實踐Node.jsMongoDB
- http的長連線和短連線HTTP
- 長連線和短連線的使用
- [場景設計]短連線服務
- 使用 SAP Cloud SDK 連線 OData 服務Cloud
- 公有云專線直連服務-ElinkcloudCloud
- Golang服務端面經Golang服務端
- Vue+WebSocket 實現頁面實時重新整理長連線VueWeb
- Swoole MySQL 連線池的實現MySql
- mysql的jdbc連線java實現MySqlJDBCJava
- NodeJs服務註冊與服務發現實現NodeJS
- 長連線和短連線
- Golang SQL連線池梳理GolangSQL
- golang consul-grpc 服務註冊與發現GolangRPC
- Docker實現服務發現Docker
- etcd實現服務發現
- java netty 實現 websocket 服務端和客戶端雙向通訊 實現心跳和斷線重連 完整示例JavaNettyWeb服務端客戶端