?

基于Netty的RPC通信系統的編解碼技術研究

2017-11-20 23:44韓星劉姣周淑君
電腦知識與技術 2017年26期
關鍵詞:編解碼序列化

韓星+劉姣+周淑君

摘要:近年來,面向服務的體系架構(SOA)逐漸成為構建大中型分布式系統的主流方式,遠程過程調用(RPC)在其中起著舉足輕重的作用。Netty作為一個基于事件驅動的、異步的網絡應用框架,能夠快捷高效的實現分布式系統間的遠程服務調用。該文對Netty編解碼器進行分析和研究,并結合消息序列化,提出了一種性能和可靠性更高的編解碼方法。

關鍵詞:netty;編解碼;序列化;遠程過程調用;消息協議

中圖分類號:TP311.5 文獻標識碼:A 文章編號:1009-3044(2017)26-0104-02

Abstract: In recent years, service oriented architecture (SOA) has gradually become the mainstream way to build large and medium-sized distributed systems. Remote procedure call (RPC) plays an important role in it. Netty, as an event driven and asynchronous network application framework, can quickly and efficiently realize remote service invocation among distributed systems. In this paper, the Netty codec is analyzed and studied, and a method of encoding and decoding with higher performance and reliability is proposed combining with message serialization.

Key words: netty;serialization; codec; remote procedure call; message protocol

SUN公司在2002年推出了JDK1.4,基于Java的Socket通信開始支持非阻塞I/O,系統性能和可靠性均得到了很大的提高。但早期的API和類庫依然存在一些不完善的地方,特別是對文件系統的處理能力非常薄弱。直到2007年JDK1.7發布,升級后的NIO2.0提供了異步文件通道和異步套接字通道的實現,文件處理能力有了進一步的提升[1-2]。盡管NIO的吞吐量和可靠性相對于傳統的BIO(同步阻塞式IO)有了質的飛躍,但其類庫和API十分繁雜,使用起來非常困難[3]。再加上粘包拆包、斷線重連等可靠性處理的工作量和復雜度都非常大,因此不建議使用NIO原生API進行通信系統的開發。為了簡化NIO網絡編程,一些開源組織發布了諸如Netty、Mina、Grizzly和xSocket等通信框架。其中,Netty的功能、性能、健壯性、可定制和可擴展行在同類框架中都是首屈一指的,并且已經得到了大量商業項目的成功驗證,如阿里巴巴的分布式服務框架Dubbo,Hadoop的RPC框架Avro等[4]。本文基于Netty框架,定義了一種通用的消息結構Message,繼承Netty的半包解碼器LengthFieldBaseFrameDecoder解碼消息以解決TCP粘包拆包問題,使用protobuf對消息體進行序列化,使通信系統的性能和可靠性均得到了極大的提高。

1 編解碼方法

1.1 粘包拆包問題

由于應用層發送消息時寫入的字節大小不固定以及IP分片等原因,TCP底層會根據緩沖區的實際情況將單個業務消息拆分成多個包,或者將多個小包封裝成一個大包進行發送。接收方有可能一次接收不完整個業務消息或者一次收到幾個消息,此時消息解碼就會出現異常,不能進行接下來的業務處理和消息回應。TCP粘包拆包無法在底層進行規避,只能通過合理的上層應用協議設計進行處理[5]。常用的解決方案有三種:一是消息定長;二是使用特殊字符對消息進行分割;三是將消息分為消息頭和消息體,在消息頭中存儲消息長度。第一種方案在消息封裝上不夠靈活,固定創建的緩沖區長度必須大于最長的消息長度,因此在寫入較短的消息時會造成資源浪費。第二種方案中使用特殊字符分割消息,如果消息本身就包含了該字符,則不能正確進行解碼,存在一定的局限性。本文采用第三種方案,使用消息頭描述消息長度。接收方先讀取固定長度的消息頭,獲取其中包含的消息長度,根據消息長度再次(或多次)讀取相應長度的字節即讀完整個消息,將包中余下的字節緩存起來作為下一個消息的前一部分。

1.2 消息結構定義

消息分為消息頭和消息體兩個部分。消息頭固定長度,用來描述消息的類型、長度和優先級等信息。消息體可變長度,承載消息實體。具體定義如表1和表2。

1.3 繼承半包解碼器

根據上文對消息結構的定義,本文將業務整包消息定義為4個部分。如圖1所示,HDR1中包含標識符和版本號,HDR2中包含會話ID、消息類型和消息優先級,Length和ActualContent分別表示數據幀長度和數據內容。定義MessageDecoder繼承半包解碼器LengthFieldBasedFrameDecoder實現粘包拆包處理。在其構造方法中設置lengthFieldOffset=8(長度字段偏移的字節數)、lengthFieldLength=4(數據幀長度)、lengthAdjustment=10(長度字段調整長度)和initialBytesToStrip=12(數據幀跳過字節數)。實際的長度字段偏移位置等于in.readerIndex()加上lengthFieldOffset,讀取消息長度字段所占的4個字節表示的數值即為消息長度。通常情況下再次讀取Length長度的字節就能獲取完整的消息,通過lengthAdjustment和initialBytesToStrip對消息長度進行調整。endprint

2 消息序列化

在網絡傳輸上,Java序列化的碼流大小和性能一直以來都為人詬病,再加上無法跨語言進行服務調用,幾乎很少有通信系統使用Java序列化[6]。XML和JSON因其平臺無關性和較小的內存占用成為了大多數通信系統的首選協議,但其為了良好的可讀性增大了空間開銷[7-8]。本文采用Google的Protobuf框架進行POJO對象的序列化。Protobuf是一個平臺無關、語言無關的結構化數據的序列化工具,相對于XML和JSON,其序列化與反序列化處理時間更短,系列化后的碼流更小,更有利于網絡傳輸和持久化[9,10]。使用Protobuf序列化,首先要根據持久化對象的系列屬性編寫數據描述文件proto,其中包含了對包名、類名和屬性的描述。然后將編寫的proto文件與protoc.exe文件放在同一目錄下,進入dos執行編譯命令,在指定目錄生成相應的FileDescriptorProto類,FileDescriptorProto類中的FileDescriptor對象通過toByteArray()和parseFrom(byte[] array)方法實現與二進制數組之間的互相轉換。

3 測試驗證

本文在PC上對上述系統進行測試,電腦配置為:CPU主頻2.10GHz,內存4.00G,硬盤容量500G、轉速7200轉。為了簡單方便地配置和加載服務接口對象和服務接口實現對象,本文通過Spring容器進行統一的對象管理。測試場景為:客戶端同時開啟10000個線程,同一時刻向服務器發起并發計算請求,服務器在異步線程中進行兩數加法計算并返回結果值給客戶端,從控制臺打印出請求消息、響應消息和處理耗時。重復進行10次測試的處理耗時結果如圖3.1所示,Netty RPC 對10000起并發計算請求的處理耗時平均為11280毫秒,遠低于傳統RPC系統的處理耗時。使用JConsole監視服務器程序在Java虛擬機中的運行狀態,其堆內存使用量最高為81.5Mb,相對于傳統RPC,其資源占用率也比較低。

4 結束語

使用原生的Java NIO進行消息系統的開發十分困難,主要體現在線程的并發控制和TCP粘包拆包的處理上。本文基于Netty搭建了一個高性能RPC框架,其異步的線程模型能夠勝任高并發,高吞吐量的消息處理。自描述的消息協議配合半包解碼器有效解決了TCP粘包和拆包的問題,持久化對象傳輸采用Protobuf進行序列化使得傳輸碼流更小,解析速度更快。十次萬級并發計算的測試結果表明該系統無論是可靠性還是性能都十分出色。在實際的消息通信應用中,本文還存在一些可以改進和完善的地方,如Reactor主從線程模型的優化,另外還可以引入Zookeeper對RPC服務器集群進行統一協調管理和服務調度。

參考文獻:

[1] Norman Maurer,Marvin Allen Wolfthal. Netty in Action[M]. Manning, 2015.

[2] Netty[EB/OL]. (2016-6-29)[2017-5-17]. http://netty.io/.

[3] Pugh W, Spacco J. MPJava: High-Performance Message Passing in Java Using Java.nio[J]. Lecture Notes in Computer Science, 2003, 2958: 323-339.

[4] 李林峰. Netty權威指南[M]. 北京: 電子工業出版社, 2015.

[5] 曹建, 劉瓊, 王遠. 基于數據流轉發的實時數據交換系統設計[J]. 計算機應用, 2016, 36(3):596-600.

[6] 崔曉旻. 基于Netty 的高可服務消息中間件的研究與實現[D]. 成都: 電子科技大學, 2015.

[7] Breg F. CD Polychronopoulos[J]. Concurrency & Computation Practice & Experience, 2003, 15(35):173-180.

[8] 何成萬, 余秋惠. JosML—一個用于實現Java對象序列化的XML模型[J]. 計算機工程, 2002, 28(1):283-284.

[9] Ayham Mhd Hailiam, Andrey Borisovich Nikolaev. Data Transmission over the Network Using PROTOBUF Protocol[J]. Automation and Control in Technical Systems, 2015, 2: 3-12.

[10] 查駿. 基于NIO的遠程調用框架的設計與實現[D]. 上海: 復旦大學, 2012.endprint

猜你喜歡
編解碼序列化
1553B總線控制器編解碼設計
為多重編解碼世界做好準備
大型民機試飛遙測視頻編解碼方法研究
基于H.265編解碼的高清視頻傳輸系統研究
Java反序列化漏洞探析及其修復方法研究
論初中語文作文的序列化訓練
Java 反序列化漏洞研究
作文訓練微格化、序列化初探
主流視頻編解碼軟件的硬件性能分析與設計
2G/3G網絡IP化語音編解碼協商策略部署研究
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合