MINA,Grizzly[grizzly-nio-framework],xSocket都是基於javanio的serverframework. 這裏的性能缺陷的焦點是指當壹條channel上的SelectionKey.OP_READready時,1.是由selectthread讀完數據之後再分發給應用程序的handler,2.還是直接就分發,由handlerthread來負責讀數據和handle. mina,xsocket是1.grizzly-nio-framework是2. 盡管讀channelbuffer中bytes是很快的,但是如果我們放大,當連接channel達到上萬數量級,甚至,這種延遲響應的效果將會愈加明顯. MINA: forallselectedKeys { readdatathenfireMessageReceived. } xSocket: forallselectedKeys { readdata,appendittoreadQueuethenperformOnData. } 其中mina在fireMessageReceived時沒有使用threadpool來分發,所以需要應用程序在handler.messageReceived中再分發.而xsocket的performOnData默認是分發給threadpool[WorkerPool],WorkerPool雖然解決了線程池中的線程不能充到最大的問題[跟tomcat6的做法壹樣],但是它的調度機制依然缺乏靈活性. Grizzly: forallselectedKeys { [NIOContext---filterChain.execute--->ourfilter.execute]bindInternal--->startupAcceptor:啟動AbstractPollingIoAcceptor.Acceptor.run使用executor[Executor]的線程,註冊[interestOps:SelectionKey.OP_ACCEPT],然後wakeupselector. 壹旦有連接進來就構建NioSocketSession--對應--channal,然後session.getProcessor().add(session)將當前的channal加入到NioProcessor的selector中去[interestOps:SelectionKey.OP_READ],這樣每個連接中有請求過來就由相應的NioProcessor來處理. 這裏有幾點要說明的是: 1.壹個NioSocketAcceptor對應了多個NioProcessor,比如NioSocketAcceptor就使用了SimpleIoProcessorPoolDEFAULT_SIZE=Runtime.getRuntime().availableProcessors()+1.當然這個size在newNioSocketAcceptor的時候可以設定. 2.壹個NioSocketAcceptor對應壹個javanioselector[OP_ACCEPT],壹個NioProcessor也對應壹個javanioselector[OP_READ]. 3.壹個NioSocketAcceptor對應壹個內部的AbstractPollingIoAcceptor.Acceptor---thread. 4.壹個NioProcessor也對應壹個內部的AbstractPollingIoProcessor.Processor---thread. 5.在newNioSocketAcceptor的時候如果妳不提供Executor(線程池)的話,那麽默認使用Executors.newCachedThreadPool(). 這個Executor將被NioSocketAcceptor和NioProcessor公用,也就是說上面的Acceptor---thread(壹條)和Processor---thread(多條)都是源於這個Executor. 當壹個連接javaniochannal--NioSession被加到ProcessorPool[i]--NioProcessor中去後就轉入了AbstractPollingIoProcessor.Processor.run, AbstractPollingIoProcessor.Processor.run方法是運行在上面的Executor中的壹條線程中的,當前的NioProcessor將處理註冊在它的selector上的所有連接的請求[interestOps:SelectionKey.OP_READ]. AbstractPollingIoProcessor.Processor.run的主要執行流程: for(;;){ intselected=selector(finalSELECT_TIMEOUT=1000L); . if(selected>0){ process(); } } process()-->forallsession-channal:OP_READ-->read(session):這個read方法是AbstractPollingIoProcessor.privatevoidread(Tsession)方法. read(session)的主要執行流程是readchannal-datatobuf,ifreadBytes>0thenIoFilterChain.fireMessageReceived(buf)/*我們的IoHandler.messageReceived將在其中被調用*/; 到此minaNio處理請求的流程已經明了. mina處理請求的線程模型也出來了,性能問題也來了,那就是在AbstractPollingIoProcessor.Processor.run-->process-->read(persession)中,在process的時候mina是forallselected-channals逐次readdata再fireMessageReceived到我們的IoHandler.messageReceived中,而不是並發處理,這樣壹來很明顯後來的請求將被延遲處理. 我們假設:如果NioProcessorPool'ssize=2現在有200個客戶端同時連接過來,假設每個NioProcessor都註冊了100個連接,對於每個NioProcessor將依次順序處理這100個請求,那麽這其中的第100個請求要得到處理,那它只有等到前面的99個被處理完了. 有人提出了改進方案,那就是在我們自己的IoHandler.messageReceived中利用線程池再進行分發dispatching,這個當然是個好主意. 但是請求還是被延遲處理了,因為還有readdata所消耗的時間,這樣第100個請求它的數據要被讀,就要等前面的99個都被讀完才行,即便是增加ProcessorPool的尺寸也不能解決這個問題. 此外mina的陷阱(這個詞較時髦)也出來了,就是在read(session)中,在說這個陷阱之前先說明壹下,我們的client端向server端發送壹個消息體的時候不壹定是完整的只發送壹次,可能分多次發送,特別是在client端忙或要發送的消息體的長度較長的時候.而mina在這種情況下就會call我們的IoHandler.messageReceived多次,結果就是消息體被分割了若幹份,等於我們在IoHandler.messageReceived中每次處理的數據都是不完整的,這會導致數據丟失,無效. 下面是read(session)的源碼: privatevoidread(Tsession){ IoSessionConfigconfig=session.getConfig(); IoBufferbuf=IoBuffer.allocate(config.getReadBufferSize()); finalbooleanhasFragmentation= session.getTransportMetadata().hasFragmentation(); try{ intreadBytes=0; intret; try{ if(hasFragmentation/*hasFragmentation壹定為ture,也許mina的開發人員也意識到了傳輸數據的碎片問題,但是靠下面的處理是遠遠不夠的,因為client壹旦間隔發送,ret就可能為0,退出while,不完整的readBytes將被fire*/){ while((ret=read(session,buf))>0){ readBytes+=ret; if(!buf.hasRemaining()){ break; } } }else{ ret=read(session,buf); if(ret>0){ readBytes=ret; } } }finally{ buf.flip(); } if(readBytes>0){ IoFilterChainfilterChain=session.getFilterChain(); filterChain.fireMessageReceived(buf); buf=null; if(hasFragmentation){ if(readBytesIoAcceptor.accept()在port上阻塞,壹旦有channel就從IoSocketDispatcherPool中獲取壹個IoSocketDispatcher,同時構建壹個IoSocketHandler和NonBlockingConnection,調用 Server.LifeCycleHandler.onConnectionAccepted(ioHandler)initializetheIoSocketHandler.註意:IoSocketDispatcherPool.size默認為2,也就是說只有2條doselect的線程和相應的2個IoSocketDispatcher.這個和MINA的NioProcessor數是壹樣的. 說明2.IoSocketDispatcher[javanioSelector]:IoSocketHandler:NonBlockingConnection------1:1:1 在IoSocketDispatcher[對應壹個Selector].run中--->IoSocketDispatcher.handleReadWriteKeys: forallselectedKeys { IoSocketHandler.onReadableEvent/onWriteableEvent. } IoSocketHandler.onReadableEvent的處理過程如下: 1.readSocket(); 2.NonBlockingConnection.IoHandlerCallback.onData NonBlockingConnection.onData--->appendDataToReadBuffer:readQueueappenddata 3.NonBlockingConnection.IoHandlerCallback.onPostData NonBlockingConnection.onPostData--->HandlerAdapter.onData[ourdataHandler]performOnDatainWorkerPool[threadpool]. 因為是把channel中的數據讀到readQueue中,應用程序的dataHandler.onData會被多次調用直到readQueue中的數據讀完為止.所以依然存在類似mina的陷阱.解決的方法依然類似,因為這裏有NonBlockingConnection. ---------------------------------------------------------------------------------------------- 再下面以grizzly-nio-frameworkv1.9.18源碼為例: tcpusagee.g: Controllersel=newController(); sel.setProtocolChainInstanceHandler(newDefaultProtocolChainInstanceHandler(){ publicProtocolChainpoll(){ ProtocolChainprotocolChain=protocolChains.poll(); if(protocolChain==null){ protocolChain=newDefaultProtocolChain(); //protocolChain.addFilter(ourapp'sfilter/*應用程序的處理從filter開始,類似mina.ioHandler,xSocket.dataHandler*/); //protocolChain.addFilter(newReadFilter()); } returnprotocolChain; } }); //如果妳不增加自己的SelectorHandler,Controller就默認使用TCPSelectorHandlerport:18888 sel.addSelectorHandler(ourapp'sselectorHandleronspecialport); sel.start(); ------------------------------------------------------------------------------------------------------------ 說明1.Controller:ProtocolChain:Filter------1:1:n, Controller:SelectorHandler------1:n,SelectorHandler[對應壹個Selector]:SelectorHandlerRunner------1:1, Controller.start()--->forperSelectorHandlerstartSelectorHandlerRunnertorun. SelectorHandlerRunner.run()--->selectorHandler.select()thenhandleSelectedKeys: forallselectedKeys { NIOContext.execute:dispatchingtothreadpoolforProtocolChain.execute--->ourfilter.execute. } 妳會發現這裏沒有readdatafromchannel的動作,因為這將由妳的filter來完成.所以自然沒有mina,xsocket它們的陷阱問題,分發提前了.但是妳要註意SelectorHandler:Selector:SelectorHandlerRunner:Thread[SelectorHandlerRunner.run]都是1:1:1:1,也就是說只有壹條線程在doSelectthenhandleSelectedKeys. 相比之下雖然grizzly在並發性能上更優,但是在易用性方面卻不如mina,xsocket,比如類似mina,xsocket中表示當前連接或會話的IoSession,INonBlockingConnection對象在grizzly中由NIOContext來負責,但是NIOContext並沒有提供session/connectionlifecycleevent,以及常規的read/write操作,這些都需要妳自己去擴展SelectorHandler和ProtocolFilter,從另壹個方面也可以說明grizzly的可擴展性,靈活性更勝壹籌. 轉載
上一篇:微信輕app用什麽語言開發?下一篇:如何把壹張海報改成jpg格式?如何把圖片改為jpg格式