W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在 Dubbo 中使用高效的 Java 序列化(Kryo 和 FST)
dubbo RPC是dubbo體系中最核心的一種高性能、高吞吐量的遠程調(diào)用方式,我喜歡稱之為多路復用的TCP長連接調(diào)用,簡單的說:
dubbo RPC主要用于兩個dubbo系統(tǒng)之間作遠程調(diào)用,特別適合高并發(fā)、小數(shù)據(jù)的互聯(lián)網(wǎng)場景。
而序列化對于遠程調(diào)用的響應速度、吞吐量、網(wǎng)絡帶寬消耗等同樣也起著至關(guān)重要的作用,是我們提升分布式系統(tǒng)性能的最關(guān)鍵因素之一。
在dubbo RPC中,同時支持多種序列化方式,例如:
在通常情況下,這四種主要序列化方式的性能從上到下依次遞減。對于dubbo RPC這種追求高性能的遠程調(diào)用方式來說,實際上只有1、2兩種高效序列化方式比較般配,而第1個dubbo序列化由于還不成熟,所以實際只剩下2可用,所以dubbo RPC默認采用hessian2序列化。
但hessian是一個比較老的序列化實現(xiàn)了,而且它是跨語言的,所以不是單獨針對java進行優(yōu)化的。而dubbo RPC實際上完全是一種Java to Java的遠程調(diào)用,其實沒有必要采用跨語言的序列化方式(當然肯定也不排斥跨語言的序列化)。
最近幾年,各種新的高效序列化方式層出不窮,不斷刷新序列化性能的上限,最典型的包括:
這些序列化方式的性能多數(shù)都顯著優(yōu)于hessian2(甚至包括尚未成熟的dubbo序列化)。
有鑒于此,我們?yōu)閐ubbo引入Kryo和FST這兩種高效Java序列化實現(xiàn),來逐步取代hessian2。
其中,Kryo是一種非常成熟的序列化實現(xiàn),已經(jīng)在Twitter、Groupon、Yahoo以及多個著名開源項目(如Hive、Storm)中廣泛的使用。而FST是一種較新的序列化實現(xiàn),目前還缺乏足夠多的成熟使用案例,但我認為它還是非常有前途的。
在面向生產(chǎn)環(huán)境的應用中,我建議目前更優(yōu)先選擇Kryo。
使用Kryo和FST非常簡單,只需要在dubbo RPC的XML配置中添加一個屬性即可:
<dubbo:protocol name="dubbo" serialization="kryo"/>
<dubbo:protocol name="dubbo" serialization="fst"/>
要讓Kryo和FST完全發(fā)揮出高性能,最好將那些需要被序列化的類注冊到dubbo系統(tǒng)中,例如,我們可以實現(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"/>
在注冊這些類后,序列化的性能可能被大大提升,特別針對小數(shù)量的嵌套對象的時候。
當然,在對一個類做序列化的時候,可能還級聯(lián)引用到很多類,比如Java集合類。針對這種情況,我們已經(jīng)自動將JDK中的常用類進行了注冊,所以你不需要重復注冊它們(當然你重復注冊了也沒有任何影響),包括:
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[]
由于注冊被序列化的類僅僅是出于性能優(yōu)化的目的,所以即使你忘記注冊某些類也沒有關(guān)系。事實上,即使不注冊任何類,Kryo和FST的性能依然普遍優(yōu)于hessian和dubbo序列化。
當然,有人可能會問為什么不用配置文件來注冊這些類?這是因為要注冊的類往往數(shù)量較多,導致配置文件冗長;而且在沒有好的IDE支持的情況下,配置文件的編寫和重構(gòu)都比java類麻煩得多;最后,這些注冊的類一般是不需要在項目編譯打包后還需要做動態(tài)修改的。
另外,有人也會覺得手工注冊被序列化的類是一種相對繁瑣的工作,是不是可以用annotation來標注,然后系統(tǒng)來自動發(fā)現(xiàn)并注冊。但這里annotation的局限是,它只能用來標注你可以修改的類,而很多序列化中引用的類很可能是你沒法做修改的(比如第三方庫或者JDK系統(tǒng)類或者其他項目的類)。另外,添加annotation畢竟稍微的“污染”了一下代碼,使應用代碼對框架增加了一點點的依賴性。
除了annotation,我們還可以考慮用其它方式來自動注冊被序列化的類,例如掃描類路徑,自動發(fā)現(xiàn)實現(xiàn)Serializable接口(甚至包括Externalizable)的類并將它們注冊。當然,我們知道類路徑上能找到Serializable類可能是非常多的,所以也可以考慮用package前綴之類來一定程度限定掃描范圍。
當然,在自動注冊機制中,特別需要考慮如何保證服務提供端和消費端都以同樣的順序(或者ID)來注冊類,避免錯位,畢竟兩端可被發(fā)現(xiàn)然后注冊的類的數(shù)量可能都是不一樣的。
如果被序列化的類中不包含無參的構(gòu)造函數(shù),則在Kryo的序列化中,性能將會大打折扣,因為此時我們在底層將用Java的序列化來透明的取代Kryo序列化。所以,盡可能為每一個被序列化的類添加無參構(gòu)造函數(shù)是一種最佳實踐(當然一個java類如果不自定義構(gòu)造函數(shù),默認就有無參構(gòu)造函數(shù))。
另外,Kryo和FST本來都不需要被序列化的類實現(xiàn)Serializable接口,但我們還是建議每個被序列化類都去實現(xiàn)它,因為這樣可以保持和Java序列化以及dubbo序列化的兼容性,另外也使我們未來采用上述某些自動注冊機制帶來可能。
本文我們主要討論的是序列化,但在做性能分析和測試的時候我們并不單獨處理每種序列化方式,而是把它們放到dubbo RPC中加以對比,因為這樣更有現(xiàn)實意義。
粗略如下:
當然這個測試環(huán)境較有局限,故當前測試結(jié)果未必有非常權(quán)威的代表性。
和dubbo自身的基準測試保持接近:
10個并發(fā)客戶端持續(xù)不斷發(fā)出請求:
進行5分鐘性能測試。(引用dubbo自身測試的考慮:“主要考察序列化和網(wǎng)絡IO的性能,因此服務端無任何業(yè)務邏輯。取10并發(fā)是考慮到rpc協(xié)議在高并發(fā)下對CPU的使用率較高可能會先打到瓶頸?!保?/p>
序列化生成字節(jié)碼的大小是一個比較有確定性的指標,它決定了遠程調(diào)用的網(wǎng)絡傳輸時間和帶寬占用。
針對復雜對象的結(jié)果如下(數(shù)值越小越好):
序列化實現(xiàn) | 請求字節(jié)數(shù) | 響應字節(jié)數(shù) |
---|---|---|
Kryo | 272 | 90 |
FST | 288 | 96 |
Dubbo Serialization | 430 | 186 |
Hessian | 546 | 329 |
FastJson | 461 | 218 |
Json | 657 | 409 |
Java Serialization | 963 | 630 |
遠程調(diào)用方式 | 平均響應時間 | 平均TPS(每秒事務數(shù)) |
---|---|---|
REST: Jetty + JSON | 7.806 | 1280 |
REST: Jetty + JSON + GZIP | TODO | TODO |
REST: Jetty + XML | TODO | TODO |
REST: Jetty + XML + GZIP | TODO | TODO |
REST: Tomcat + JSON | 2.082 | 4796 |
REST: Netty + JSON | 2.182 | 4576 |
Dubbo: FST | 1.211 | 8244 |
Dubbo: kyro | 1.182 | 8444 |
Dubbo: dubbo serialization | 1.43 | 6982 |
Dubbo: hessian2 | 1.49 | 6701 |
Dubbo: fastjson | 1.572 | 6352 |
就目前結(jié)果而言,我們可以看到不管從生成字節(jié)的大小,還是平均響應時間和平均TPS,Kryo和FST相比Dubbo RPC中原有的序列化方式都有非常顯著的改進。
未來,當Kryo或者FST在dubbo中當應用足夠成熟之后,我們很可能會將dubbo RPC的默認序列化從hessian2改為它們中間的某一個。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: