99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

Dubbo3 Kryo 和 FST 序列化

2022-04-12 14:04 更新

在 Dubbo 中使用高效的 Java 序列化(Kryo 和 FST)

目錄

  • 序列化漫談
  • 啟用Kryo和FST
  • 注冊(cè)被序列化類
  • 無(wú)參構(gòu)造函數(shù)和Serializable接口
  • 序列化性能分析與測(cè)試
    測(cè)試環(huán)境
    測(cè)試腳本
    Dubbo RPC中不同序列化生成字節(jié)大小比較
    Dubbo RPC中不同序列化響應(yīng)時(shí)間和吞吐量對(duì)比
  • 未來(lái)

序列化漫談

dubbo RPC是dubbo體系中最核心的一種高性能、高吞吐量的遠(yuǎn)程調(diào)用方式,我喜歡稱之為多路復(fù)用的TCP長(zhǎng)連接調(diào)用,簡(jiǎn)單的說(shuō):

  • 長(zhǎng)連接:避免了每次調(diào)用新建TCP連接,提高了調(diào)用的響應(yīng)速度
  • 多路復(fù)用:?jiǎn)蝹€(gè)TCP連接可交替?zhèn)鬏敹鄠€(gè)請(qǐng)求和響應(yīng)的消息,降低了連接的等待閑置時(shí)間,從而減少了同樣并發(fā)數(shù)下的網(wǎng)絡(luò)連接數(shù),提高了系統(tǒng)吞吐量。

dubbo RPC主要用于兩個(gè)dubbo系統(tǒng)之間作遠(yuǎn)程調(diào)用,特別適合高并發(fā)、小數(shù)據(jù)的互聯(lián)網(wǎng)場(chǎng)景。

而序列化對(duì)于遠(yuǎn)程調(diào)用的響應(yīng)速度、吞吐量、網(wǎng)絡(luò)帶寬消耗等同樣也起著至關(guān)重要的作用,是我們提升分布式系統(tǒng)性能的最關(guān)鍵因素之一。

在dubbo RPC中,同時(shí)支持多種序列化方式,例如:

  1. dubbo序列化:阿里尚未開(kāi)發(fā)成熟的高效java序列化實(shí)現(xiàn),阿里不建議在生產(chǎn)環(huán)境使用它
  2. hessian2序列化:hessian是一種跨語(yǔ)言的高效二進(jìn)制序列化方式。但這里實(shí)際不是原生的hessian2序列化,而是阿里修改過(guò)的hessian lite,它是dubbo RPC默認(rèn)啟用的序列化方式
  3. json序列化:目前有兩種實(shí)現(xiàn),一種是采用的阿里的fastjson庫(kù),另一種是采用dubbo中自己實(shí)現(xiàn)的簡(jiǎn)單json庫(kù),但其實(shí)現(xiàn)都不是特別成熟,而且json這種文本序列化性能一般不如上面兩種二進(jìn)制序列化。
  4. java序列化:主要是采用JDK自帶的Java序列化實(shí)現(xiàn),性能很不理想。

在通常情況下,這四種主要序列化方式的性能從上到下依次遞減。對(duì)于dubbo RPC這種追求高性能的遠(yuǎn)程調(diào)用方式來(lái)說(shuō),實(shí)際上只有1、2兩種高效序列化方式比較般配,而第1個(gè)dubbo序列化由于還不成熟,所以實(shí)際只剩下2可用,所以dubbo RPC默認(rèn)采用hessian2序列化。

但hessian是一個(gè)比較老的序列化實(shí)現(xiàn)了,而且它是跨語(yǔ)言的,所以不是單獨(dú)針對(duì)java進(jìn)行優(yōu)化的。而dubbo RPC實(shí)際上完全是一種Java to Java的遠(yuǎn)程調(diào)用,其實(shí)沒(méi)有必要采用跨語(yǔ)言的序列化方式(當(dāng)然肯定也不排斥跨語(yǔ)言的序列化)。

最近幾年,各種新的高效序列化方式層出不窮,不斷刷新序列化性能的上限,最典型的包括:

  • 專門(mén)針對(duì)Java語(yǔ)言的:Kryo,F(xiàn)ST等等
  • 跨語(yǔ)言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等

這些序列化方式的性能多數(shù)都顯著優(yōu)于hessian2(甚至包括尚未成熟的dubbo序列化)。

有鑒于此,我們?yōu)閐ubbo引入Kryo和FST這兩種高效Java序列化實(shí)現(xiàn),來(lái)逐步取代hessian2。

其中,Kryo是一種非常成熟的序列化實(shí)現(xiàn),已經(jīng)在Twitter、Groupon、Yahoo以及多個(gè)著名開(kāi)源項(xiàng)目(如Hive、Storm)中廣泛的使用。而FST是一種較新的序列化實(shí)現(xiàn),目前還缺乏足夠多的成熟使用案例,但我認(rèn)為它還是非常有前途的。

在面向生產(chǎn)環(huán)境的應(yīng)用中,我建議目前更優(yōu)先選擇Kryo。

啟用Kryo和FST

使用Kryo和FST非常簡(jiǎn)單,只需要在dubbo RPC的XML配置中添加一個(gè)屬性即可:

<dubbo:protocol name="dubbo" serialization="kryo"/>
<dubbo:protocol name="dubbo" serialization="fst"/>

注冊(cè)被序列化類

要讓Kryo和FST完全發(fā)揮出高性能,最好將那些需要被序列化的類注冊(cè)到dubbo系統(tǒng)中,例如,我們可以實(shí)現(xiàn)如下回調(diào)接口:

public class SerializationOptimizerImpl implements SerializationOptimizer {

    public Collection<Class> getSerializableClasses() {
        List<Class> classes = new LinkedList<Class>();
        classes.add(BidRequest.class);
        classes.add(BidResponse.class);
        classes.add(Device.class);
        classes.add(Geo.class);
        classes.add(Impression.class);
        classes.add(SeatBid.class);
        return classes;
    }
}

然后在XML配置中添加:

<dubbo:protocol name="dubbo" serialization="kryo" optimizer="org.apache.dubbo.demo.SerializationOptimizerImpl"/>

在注冊(cè)這些類后,序列化的性能可能被大大提升,特別針對(duì)小數(shù)量的嵌套對(duì)象的時(shí)候。

當(dāng)然,在對(duì)一個(gè)類做序列化的時(shí)候,可能還級(jí)聯(lián)引用到很多類,比如Java集合類。針對(duì)這種情況,我們已經(jīng)自動(dòng)將JDK中的常用類進(jìn)行了注冊(cè),所以你不需要重復(fù)注冊(cè)它們(當(dāng)然你重復(fù)注冊(cè)了也沒(méi)有任何影響),包括:

GregorianCalendar
InvocationHandler
BigDecimal
BigInteger
Pattern
BitSet
URI
UUID
HashMap
ArrayList
LinkedList
HashSet
TreeSet
Hashtable
Date
Calendar
ConcurrentHashMap
SimpleDateFormat
Vector
BitSet
StringBuffer
StringBuilder
Object
Object[]
String[]
byte[]
char[]
int[]
float[]
double[]

由于注冊(cè)被序列化的類僅僅是出于性能優(yōu)化的目的,所以即使你忘記注冊(cè)某些類也沒(méi)有關(guān)系。事實(shí)上,即使不注冊(cè)任何類,Kryo和FST的性能依然普遍優(yōu)于hessian和dubbo序列化。

當(dāng)然,有人可能會(huì)問(wèn)為什么不用配置文件來(lái)注冊(cè)這些類?這是因?yàn)橐?cè)的類往往數(shù)量較多,導(dǎo)致配置文件冗長(zhǎng);而且在沒(méi)有好的IDE支持的情況下,配置文件的編寫(xiě)和重構(gòu)都比java類麻煩得多;最后,這些注冊(cè)的類一般是不需要在項(xiàng)目編譯打包后還需要做動(dòng)態(tài)修改的。
另外,有人也會(huì)覺(jué)得手工注冊(cè)被序列化的類是一種相對(duì)繁瑣的工作,是不是可以用annotation來(lái)標(biāo)注,然后系統(tǒng)來(lái)自動(dòng)發(fā)現(xiàn)并注冊(cè)。但這里annotation的局限是,它只能用來(lái)標(biāo)注你可以修改的類,而很多序列化中引用的類很可能是你沒(méi)法做修改的(比如第三方庫(kù)或者JDK系統(tǒng)類或者其他項(xiàng)目的類)。另外,添加annotation畢竟稍微的“污染”了一下代碼,使應(yīng)用代碼對(duì)框架增加了一點(diǎn)點(diǎn)的依賴性。
除了annotation,我們還可以考慮用其它方式來(lái)自動(dòng)注冊(cè)被序列化的類,例如掃描類路徑,自動(dòng)發(fā)現(xiàn)實(shí)現(xiàn)Serializable接口(甚至包括Externalizable)的類并將它們注冊(cè)。當(dāng)然,我們知道類路徑上能找到Serializable類可能是非常多的,所以也可以考慮用package前綴之類來(lái)一定程度限定掃描范圍。
當(dāng)然,在自動(dòng)注冊(cè)機(jī)制中,特別需要考慮如何保證服務(wù)提供端和消費(fèi)端都以同樣的順序(或者ID)來(lái)注冊(cè)類,避免錯(cuò)位,畢竟兩端可被發(fā)現(xiàn)然后注冊(cè)的類的數(shù)量可能都是不一樣的。

無(wú)參構(gòu)造函數(shù)和Serializable接口

如果被序列化的類中不包含無(wú)參的構(gòu)造函數(shù),則在Kryo的序列化中,性能將會(huì)大打折扣,因?yàn)榇藭r(shí)我們?cè)诘讓訉⒂肑ava的序列化來(lái)透明的取代Kryo序列化。所以,盡可能為每一個(gè)被序列化的類添加無(wú)參構(gòu)造函數(shù)是一種最佳實(shí)踐(當(dāng)然一個(gè)java類如果不自定義構(gòu)造函數(shù),默認(rèn)就有無(wú)參構(gòu)造函數(shù))。

另外,Kryo和FST本來(lái)都不需要被序列化的類實(shí)現(xiàn)Serializable接口,但我們還是建議每個(gè)被序列化類都去實(shí)現(xiàn)它,因?yàn)檫@樣可以保持和Java序列化以及dubbo序列化的兼容性,另外也使我們未來(lái)采用上述某些自動(dòng)注冊(cè)機(jī)制帶來(lái)可能。

序列化性能分析與測(cè)試

本文我們主要討論的是序列化,但在做性能分析和測(cè)試的時(shí)候我們并不單獨(dú)處理每種序列化方式,而是把它們放到dubbo RPC中加以對(duì)比,因?yàn)檫@樣更有現(xiàn)實(shí)意義。

測(cè)試環(huán)境

粗略如下:

  • 兩臺(tái)獨(dú)立服務(wù)器
  • 4核Intel(R) Xeon(R) CPU E5-2603 0 @ 1.80GHz
  • 8G內(nèi)存
  • 虛擬機(jī)之間網(wǎng)絡(luò)通過(guò)百兆交換機(jī)
  • CentOS 5
  • JDK 7
  • Tomcat 7
  • JVM參數(shù)-server -Xms1g -Xmx1g -XX:PermSize=64M -XX:+UseConcMarkSweepGC

當(dāng)然這個(gè)測(cè)試環(huán)境較有局限,故當(dāng)前測(cè)試結(jié)果未必有非常權(quán)威的代表性。

測(cè)試腳本

和dubbo自身的基準(zhǔn)測(cè)試保持接近:

10個(gè)并發(fā)客戶端持續(xù)不斷發(fā)出請(qǐng)求:

  • 傳入嵌套復(fù)雜對(duì)象(但單個(gè)數(shù)據(jù)量很小),不做任何處理,原樣返回
  • 傳入50K字符串,不做任何處理,原樣返回(TODO:結(jié)果尚未列出)

進(jìn)行5分鐘性能測(cè)試。(引用dubbo自身測(cè)試的考慮:“主要考察序列化和網(wǎng)絡(luò)IO的性能,因此服務(wù)端無(wú)任何業(yè)務(wù)邏輯。取10并發(fā)是考慮到rpc協(xié)議在高并發(fā)下對(duì)CPU的使用率較高可能會(huì)先打到瓶頸?!保?/p>

Dubbo RPC中不同序列化生成字節(jié)大小比較

序列化生成字節(jié)碼的大小是一個(gè)比較有確定性的指標(biāo),它決定了遠(yuǎn)程調(diào)用的網(wǎng)絡(luò)傳輸時(shí)間和帶寬占用。

針對(duì)復(fù)雜對(duì)象的結(jié)果如下(數(shù)值越小越好):

序列化實(shí)現(xiàn)請(qǐng)求字節(jié)數(shù)響應(yīng)字節(jié)數(shù)
Kryo27290
FST28896
Dubbo Serialization430186
Hessian546329
FastJson461218
Json657409
Java Serialization963630

Dubbo RPC中不同序列化響應(yīng)時(shí)間和吞吐量對(duì)比

遠(yuǎn)程調(diào)用方式平均響應(yīng)時(shí)間平均TPS(每秒事務(wù)數(shù))
REST: Jetty + JSON7.8061280
REST: Jetty + JSON + GZIPTODOTODO
REST: Jetty + XMLTODOTODO
REST: Jetty + XML + GZIPTODOTODO
REST: Tomcat + JSON2.0824796
REST: Netty + JSON2.1824576
Dubbo: FST1.2118244
Dubbo: kyro1.1828444
Dubbo: dubbo serialization1.436982
Dubbo: hessian21.496701
Dubbo: fastjson1.5726352

rt

tps

測(cè)試總結(jié)

就目前結(jié)果而言,我們可以看到不管從生成字節(jié)的大小,還是平均響應(yīng)時(shí)間和平均TPS,Kryo和FST相比Dubbo RPC中原有的序列化方式都有非常顯著的改進(jìn)。

未來(lái)

未來(lái),當(dāng)Kryo或者FST在dubbo中當(dāng)應(yīng)用足夠成熟之后,我們很可能會(huì)將dubbo RPC的默認(rèn)序列化從hessian2改為它們中間的某一個(gè)。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)