當前位置:編程學習大全網 - 編程語言 - java mina 可以多線程寫麽

java mina 可以多線程寫麽

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格式
  • copyright 2024編程學習大全網