經(jīng)驗不在于年限,在于積累---專注互聯(lián)網(wǎng)軟件開發(fā)

          把工作當(dāng)事業(yè)做,把項目當(dāng)作品做!

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            55 Posts :: 0 Stories :: 66 Comments :: 0 Trackbacks

          最近的一個線上項目(認(rèn)證服務(wù)器)老是出現(xiàn)服務(wù)延遲的情況。具體的問題描述:

          (1)客戶端發(fā)送一個請求A(長連接),在服務(wù)器端的業(yè)務(wù)層需要20秒以上才能接收到。

          (2)客戶端發(fā)送一個請求B(端連接),在服務(wù)器端的業(yè)務(wù)層可以迅速接收到。

          從現(xiàn)象大致知道問題出在服務(wù)器端的網(wǎng)絡(luò)接收層,大量通過長連接發(fā)送過來的請求都堵塞在網(wǎng)絡(luò)層得不到處理(在網(wǎng)絡(luò)層排隊,還沒到應(yīng)用層)。

          (友情提示:本博文章歡迎轉(zhuǎn)載,但請注明出處:hankchen,http://www.aygfsteel.com/hankchen

           

          后來經(jīng)過排查,發(fā)現(xiàn)是Netty中的OrderedMemoryAwareThreadPoolExecutor原因。相關(guān)代碼如下:

          MemoryAwareThreadPoolExecutor executor = new OrderedMemoryAwareThreadPoolExecutor(threadNums, maxChannelMemorySize,
                                                                                                                  maxTotalMemorySize, keepAliveTime,
                                                                                                                  TimeUnit.SECONDS);
          ExecutionHandler executionHandler = new ExecutionHandler(executor);

          public ChannelPipeline getPipeline() throws Exception
          {
                  ChannelPipeline pipeline = pipeline();
                  pipeline.addLast("decoder", new AuthDecoder());
                  pipeline.addLast("encoder", new AuthEncoder());
                  pipeline.addLast("executor", executionHandler);
                  pipeline.addLast("handler", new AuthServerHandler(commandFactory));
                  return pipeline;
          }

           

          先介紹下背景知識,再來分析問題。

          大家都知道,Netty是一個基于事件的NIO框架。在Netty中,一切網(wǎng)絡(luò)動作都是通過事件來傳播并處理的,例如:Channel讀、Channel寫等等。回憶下Netty的流處理模型:

          Boss線程(一個服務(wù)器端口對于一個)---接收到客戶端連接---生成Channel---交給Work線程池(多個Work線程)來處理。

          具體的Work線程---讀完已接收的數(shù)據(jù)到ChannelBuffer---觸發(fā)ChannelPipeline中的ChannelHandler鏈來處理業(yè)務(wù)邏輯。

          注意:執(zhí)行ChannelHandler鏈的整個過程是同步的,如果業(yè)務(wù)邏輯的耗時較長,會將導(dǎo)致Work線程長時間被占用得不到釋放,從而影響了整個服務(wù)器的并發(fā)處理能力。

          所以,為了提高并發(fā)數(shù),一般通過ExecutionHandler線程池來異步處理ChannelHandler鏈(worker線程在經(jīng)過ExecutionHandler后就結(jié)束了,它會被ChannelFactory的worker線程池所回收)。在Netty中,只需要增加一行代碼:

          public ChannelPipeline getPipeline() {
                   return Channels.pipeline(
                           new DatabaseGatewayProtocolEncoder(),
                           new DatabaseGatewayProtocolDecoder(),
                           executionHandler, // Must be shared
                           new DatabaseQueryingHandler());
          }
          例如:
          ExecutionHandler executionHandler = new ExecutionHandler(
                       new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576))

           

          對于ExecutionHandler需要的線程池模型,Netty提供了兩種可選:

          1) MemoryAwareThreadPoolExecutor 通過對線程池內(nèi)存的使用控制,可控制Executor中待處理任務(wù)的上限(超過上限時,后續(xù)進(jìn)來的任務(wù)將被阻塞),并可控制單個Channel待處理任務(wù)的上限,防止內(nèi)存溢出錯誤;

          2) OrderedMemoryAwareThreadPoolExecutor 是 MemoryAwareThreadPoolExecutor 的子類。除了MemoryAwareThreadPoolExecutor 的功能之外,它還可以保證同一Channel中處理的事件流的順序性,這主要是控制事件在異步處理模式下可能出現(xiàn)的錯誤的事件順序,但它并不保證同一Channel中的事件都在一個線程中執(zhí)行(通常也沒必要)。

          例如:

          Thread X: --- Channel A (Event A1) --.   .-- Channel B (Event B2) --- Channel B (Event B3) --->
                                                \ /
                                                 X
                                                / \
          Thread Y: --- Channel B (Event B1) --'   '-- Channel A (Event A2) --- Channel A (Event A3) --->

          上圖表達(dá)的意思有幾個:

          (1)對整個線程池而言,處理同一個Channel的事件,必須是按照順序來處理的。例如,必須先處理完Channel A (Event A1) ,再處理Channel A (Event A2)、Channel A (Event A3)

          (2)同一個Channel的多個事件,會分布到線程池的多個線程中去處理。

          (3)不同Channel的事件可以同時處理(分擔(dān)到多個線程),互不影響。  

          OrderedMemoryAwareThreadPoolExecutor 的這種事件處理有序性是有意義的,因為通常情況下,請求發(fā)送端希望服務(wù)器能夠按照順序處理自己的請求,特別是需要多次握手的應(yīng)用層協(xié)議。例如:XMPP協(xié)議。

           

          現(xiàn)在回到具體業(yè)務(wù)上來,我們這里的認(rèn)證服務(wù)也使用了OrderedMemoryAwareThreadPoolExecutor。認(rèn)證服務(wù)的其中一個環(huán)節(jié)是使用長連接,不斷處理來自另外一個服務(wù)器的認(rèn)證請求。通信的數(shù)據(jù)包都很小,一般都是200個字節(jié)以內(nèi)。一般情況下,處理這個過程很快,所以沒有什么問題。但是,由于認(rèn)證服務(wù)需要調(diào)用第三方的接口,如果第三方接口出現(xiàn)延遲,將導(dǎo)致這個過程變慢。一旦一個事件處理不完,由于要保持事件處理的有序性,其他事件就全部堵塞了!而短連接之所以沒有問題,是因為短連接一個Channel就一個請求數(shù)據(jù)包,處理完Channel就關(guān)閉了,根本不存在順序的問題,所以在業(yè)務(wù)層可以迅速收到請求,只是由于同樣的原因(第三方接口),處理時間會比較長。

          其實,認(rèn)證過程都是獨立的請求數(shù)據(jù)包(單個帳號),每個請求數(shù)據(jù)包之間是沒有任何關(guān)系的,保持這樣的順序沒有意義!

           

          最后的改進(jìn)措施:

          1、去掉OrderedMemoryAwareThreadPoolExecutor,改用MemoryAwareThreadPoolExecutor。

          2、減少調(diào)用第三方接口的超時時間,讓處理線程盡早回歸線程池。

          (友情提示:本博文章歡迎轉(zhuǎn)載,但請注明出處:hankchen,http://www.aygfsteel.com/hankchen

          posted on 2012-04-08 12:32 hankchen 閱讀(14375) 評論(0)  編輯  收藏 所屬分類: 網(wǎng)絡(luò)開發(fā)+Mina+Netty
          主站蜘蛛池模板: 香格里拉县| 新闻| 青神县| 潞城市| 资溪县| 运城市| 邵武市| 连山| 柳河县| 政和县| 乐陵市| 西充县| 彭阳县| 大关县| 德令哈市| 墨竹工卡县| 顺平县| 曲沃县| 怀柔区| 内江市| 宁阳县| 赤峰市| 门头沟区| 绿春县| 南汇区| 平利县| 荥经县| 永春县| 山东| 清新县| 江源县| 贵州省| 隆德县| 正镶白旗| 广安市| 铁岭县| 佛冈县| 吉隆县| 和政县| 灯塔市| 法库县|