開源庫GameNetty—讓編寫Socket服務像HTTP一樣簡單
技術標籤:Nettyjavasocketnettytcp開源
在java領域,Netty已經成為編寫Socket服務的首選。稍有遺憾的是,相比Spring MVC,Netty的使用門檻還是比較高的;要用好Netty,必需掌握好多執行緒、Socket、底層位元組操作等技能,而且相關程式的除錯難度也比較大。
本人使用Netty從事遊戲服務開發近5年,不斷打磨底層網路框架,誕生了GameNetty這個工具庫,權當本人對這個領域一點微不足道的回饋吧。
原始碼地址:https://github.com/longhuihu/game-netty
GameNetty目前用於公司的多個專案,非常穩定。當然受限於使用場景,肯定還有很多不足之處,期望得到同行的批評指正。
一、用途
GameNetty是一個使用TCP協議的網路通訊開源庫,它內部使用了Netty(4.1.50),讓使用者不必深入理解Netty也能輕鬆地編寫TCP通訊程式。
功能特點:
- 支援tcp通訊,遮蔽了Netty的複雜性,尤其是ByteBuf的使用;
- 靈活的訊息格式,使用者可自由決定使用二進位制或文字訊息格式;
- 支援訊息加密和壓縮,內建了RC4加密和GZIP壓縮功能,使用者可擴充套件;
- 支援websocket;
- 實現了Client—Proxy—Server(客戶端—遊戲閘道器—遊戲服)三層網路結構,在Proxy端做了大量效能優化;
一句話總結,使用GameNetty來構建TCP服務,就像使用Spring MVC構建基於HTTP服務一樣簡單
GameNetty最適用的場景是使用TCP或WebSocket的網路遊戲,它原本就是脫胎於遊戲專案;後續也廣泛用於服務程序之間的通訊,尤其當你的業務場景簡單,不想依賴額外的RPC,MQ框架時。總之,那些需要頻繁傳遞網路訊息的場景,GameNetty都可能派上用場。
二、概念
GameNetty內的概念儘量與Netty保持一致,比如,xxxAcceptor代表TCP監聽端,xxxConnector代表TCP連線的發起端,xxxChannel代表一條TCP連線(本質是Netty Channel的包裝器)。
xxxCodec代表編解碼器,訊息的編碼和解碼邏輯一般緊密關聯,所以GameNetty不再將它們分開。
GameNetty功能類大體被分成了Client和Proxy兩組,Client意指和客戶端相關,用來支援客戶端發起連線(Connector),伺服器監聽客戶端連線請求(Acceptor)。
Proxy意指代理伺服器,遊戲行業也經常稱為閘道器(Gate)伺服器,包括代理連線後端服務的Connector和後端接受連線的Acceptor。Proxy的存在直接支援了網路遊戲常見的Client—Proxy—Server三層網路結構。
如果客戶端直連伺服器,專案使用GameNetty的方式如下:
------------------------- -------------------------
| ClientConnector | <--ClientMessage--> | ClientSocketAcceptor |
| (ClientConnectChannel)| | (ClientAcceptedChannel)|
------------------------ --------------------------
Client Side Server Side
如果採用Client—Proxy—Server結構,專案使用GameNetty的方式如下:
------------------------- ------------------------
| ClientConnector | <--ClientMessage--> | ClientSocketAcceptor |
| (ClientConnectChannel)| | (ClientAcceptedChannel)|
------------------------ | | ------------------------
| ProxyAcceptor | <--ProxyMessage--> | ProxyAcceptor |
| (ProxyAcceptedChannel) | |(ProxyAcceptedChannel) |
------------------------- -------------------------
Client Side Proxy Side Server Side
如果使用websocket,將ClientSocketAcceptor替換成ClientWebSocketAcceptor即可。
三、訊息,編解碼,加密,壓縮
ClientMessage
GameNetty裡,客戶端核和服務端之間的通訊訊息被抽象為ClientMessage,它包含三個欄位:
- head—訊息頭
最大8位元組,使用者可選擇0,1,2,4,8,因此簡單起見,定義為long型別。
訊息頭的存在對三層結構的Proxy有重要意義,Proxy可以通過head得到必要的資訊,而避免對訊息體解碼;
- buf—訊息體ByteBuf
此欄位用於Proxy效能優化,Proxy可以直接轉發buf,從而避免對訊息重新編碼;
- body—訊息體物件
它是訊息體解碼後的資料物件,Proxy端可能不需要對所有訊息解析出body,在Server端,一般都需要解析body。
ProxyMessage
Proxy與Server之間的通訊訊息被抽象為ProxyMessage,它包含兩部分:ClientMessage和ProxyHead。
ClientMessage是從客戶端接收的訊息,而ProxyHead則是Proxy新增的額外資訊,就像Http代理會向Http請求插入額外頭部欄位一樣。
ProxyHead沒有強制的型別需求,簡單情況下String就足夠。
編碼
GameNetty對ClientMessage的編碼格式已經做了規定:[length(4位元組)]+[head(0~8位元組)]+[body],其中length部分是訊息完整編碼長度(包含自身)。
body部分的編解碼由使用者來實現,對應介面是MessageBodyCodec。
ProxyMessage的編碼格式也是類似的, [length(4位元組)]+[proxy head(0~8位元組)]+[ClientMessage],其中length部分是訊息完整編碼長度(包含自身)。
ProxyHead的部分的編解碼由使用者實現,對應介面是ProxyHeaderCodec。
加密,壓縮
GameNetty通過BodyTransformer機制支援對訊息的加密、壓縮,使用者可以新增一個或多個BodyTransformer,對訊息體做任意轉換。
GameNetty內建了RC4BodyEnDecryptor,GzipBody(Un)Compressor等BodyTransformer。
四、會話
GameNetty支援會話(SessionInterface介面),所謂Session,是用來標記客戶端與服務端之間的一個Channel,使用者可以在Session上附加額外的資訊;
在Client—Proxy—Server三層架構中,Session物件在Proxy上,session id具備全域性唯一性,Server通過session id來穿透Proxy與Client通訊。
五、示例
執行示例是上手GameNetty最快的方式,com.game.netty.sample下有三個示例,這些示例的功能是類似的:客戶端向服務端傳送一個訊息,服務端返回一個響應,
並且有一定概率在幾秒種後向客戶端主動推送一個訊息。
- socket
模擬最簡單的C/S模式,執行ClientStarter(客戶端,可多個例項)和ServerStarter(服務端)即可;
- websocket
socket的websocket版本;
- proxy
模擬Client—Proxy—Server結構,需同時執行ClientStarter(客戶端,可多個例項),ProxyStarter(代理端),LogicServerStarter(服務端);
該示例展示了Proxy服務如何轉發Client訊息到指定的LogicServer,而後者可以通過session與指定Client通訊。
如果通過Intellij匯入程式碼,執行示例,需要配置Run Configuration->選中include dependencies with provide scope。
六、使用
建議maven匯入,參考示例使用.
<dependency>
<groupId>com.github.longhuihu</groupId>
<artifactId>game-netty</artifactId>
<version>1.0</version>
</dependency>
七、聯絡人
個人部落格:https://blog.csdn.net/longhuihu