程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> NFS-RPC框架優化過程(從37k到168k)

NFS-RPC框架優化過程(從37k到168k)

編輯:關於JAVA
 

NFS-RPC框架從編寫之初,到現在為止(應該還會有些提升,不過估計不大),每秒支撐的請求數上升了好幾倍,測試結果的演變為:
37k –> 56k –> 65k –> 88k –> 93k –> 143k –> 148k –> 153k –> 160k –> 163k –> 168k
以上測試結果為在100並發、100 request byte、100 response byte以及單連接下的背景下得出的,在這篇blog中來分享下這個框架所做的一些優化動作,希望能給編寫rpc框架或使用mina/netty/grizzly的同學們一點點幫助,也希望得到高手們更多的指點。

1、37k –> 56k
由於目前大部分的NIO框架采用的均為1個socket io線程處理多個連接的io事件,如果io線程時間占用太長的話,就會導致收到的響應處理的比較慢的現象,這步優化就是針對反序列化過程占用io線程而做的,采用的方法即為在讀取流時僅根據長度信息把所有的bytes都讀好,然後直接作為收到的信息返回給業務線程,業務線程在進行處理前先做反序列化動作,感興趣的同學可以看看NFS-RPC中:code.google.nfs.rpc.netty.serialize.NettyProtocolDecoder或code.google.nfs.rpc.mina.serialize.MinaProtocolDecoder,以及code.google.nfs.rpc.mina.server.MinaServerHandler或code.google.nfs.rpc.netty.server.NettyServerHandler。

2、56k –> 65k
在測試的過程中,發現YGC的耗時比較長,在咨詢了sun的人後告訴我主要是由於有舊生代的數據結構引用了大量新生代對象造成的,經過對程序的分析,猜測是benchmark代碼本身用於記錄請求響應時間信息的ConcurrentLinkedQueue造成的,在某超級大牛的指示下,換成了在每個線程中用數組的方式,按區間來記錄響應時間信息等,感興趣的同學可以看看NFS-RPC中:code.google.nfs.rpc.benchmark.SimpleProcessorBenchmarkClientRunnable

3、65k –> 88k
在某超級大牛的分析下,發現目前的情況下io線程的上下文切換還是比較頻繁,導致io線程處理效率不夠高,默認情況下,NIO框架多數采用的均為接到一個包後,將這個包交由反序列化的處理器進行處理,對於包中有多個請求信息或響應信息的情況,則采用一個一個通知的方式,而rpc框架在接到一個請求或響應對象時的做法通常是喚醒等待的業務線程,因此對於一個包中有多個請求或響應的狀況就會導致io線程需要多次喚醒業務線程,這個地方改造的方法是nio框架一次性的將包中所有的請求或響應對象通知給業務線程,然後由業務線程pipeline的去喚醒其他的業務線程,感興趣的同學可以看看NFS-RPC中:code.google.nfs.rpc.netty.serialize.NettyProtocolDecoder或code.google.nfs.rpc.mina.serialize.MinaProtocolDecoder,以及code.google.nfs.rpc.netty.client.NettyClientHandler或code.google.nfs.rpc.mina.client.MinaClientProcessor。

4、88k –> 93k
這步沒什麼可說的,只是多支持了hessian序列化,然後這個結果是用hessian序列化測試得出的,注意的是hessian不要使用3.1.x或3.2.x版本,這兩個系列的版本性能極差,建議使用hessian 4.0.x版本。

5、93k –> 143k
在到達93k時,看到測試的結果中有不少請求的響應時間會超過10ms,於是用btrace一步一步跟蹤查找是什麼地方會出現這種現象,後來發現是由於之前在確認寫入os send buffer時采用的是await的方式,而這會導致需要等待io線程來喚醒,增加了線程上下文切換以及io線程的負擔,但這個地方又不能不做處理,後來就改造成僅基於listener的方式進行處理,如寫入失敗會直接創建一個響應對象,這次改造後效果非常明顯,感興趣的同學可以看看nfs-rpc中:code.google.nfs.rpc.mina.client.MinaClient或code.google.nfs.rpc.netty.client.NettyClient。

6、143k –> 148k
這步是在@killme2008 的指點下,將tcpNoDelay設置為了false,但這個設置不適用於低壓力的情況,在10個線程的情況下tps由3w降到了2k,因此tcpNoDelay這個建議還是設置成true。

7、148k –> 153k
這步沒什麼可說的,只是多支持了protobuf序列化,這個結果是用protobuf序列化測試得出的,之前還測試過下@bnu_chenshuo 寫的一個protorpc,是基於protobuf的rpc加上netty實現的,結果也很強悍,測試出來是149k,看來protobuf的rpc也是有不少值得學習的地方的。

8、153k –> 160k
server接到消息的處理過程也修改成類似client的pipeline機制,同時將之前獲取協議處理器和序列化/反序列化的處理器的地方由map改成了數組,具體可以參考NettyServerHandler、ProtocolFactory和Codecs。

9、160k –> 163k
Grizzly的leader對grizzly部分的代碼做了很多的修改,結果創造了目前rpc benchmark的最高紀錄:163k,更多優化的細節敬請大家期待後續的一篇grizzly rpc優化細節的blog…

10、163k –> 168k
@minzhou 對nfs-rpc的代碼進行了優化,將之前在Decoder中做的構造String對象的部分挪到了業務線程中,於是TPS也有了一定的上升,感謝。

上面就是到目前為止的所有優化動作,其中的很多我估計高手的話是不會犯錯的,我走的彎路多了些,總結來說rpc框架的優化動作為:
1、盡可能減少io線程的占用時間;
2、盡可能減少線程上下文切換;
3、盡可能使用高效的序列化/反序列化;

NFS-RPC框架在後面將繼續做更多的測試和優化,例如采用aio、coroutine、測試CNK問題等,敬請關注:http://code.google.com/p/nfs-rpc
 

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved