日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tomcat架构分析 (connector NIO 实现)【转】

發(fā)布時間:2025/4/5 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tomcat架构分析 (connector NIO 实现)【转】 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文地址:https://www.iteye.com/blog/gearever-1844203

ller線程中維護的這個Selector標(biāo)為主Selector。?
Poller是NIO實現(xiàn)的主要線程。首先作為events queue的消費者,從queue中取出PollerEvent對象,然后將此對象中的channel以O(shè)P_READ事件注冊到主Selector中,然后主Selector執(zhí)行select操作,遍歷出可以讀數(shù)據(jù)的socket,并從Worker線程池中拿到可用的Worker線程,然后將socket傳遞給Worker。整個過程是典型的NIO實現(xiàn)。?

Worker?
Worker線程拿到Poller傳過來的socket后,將socket封裝在SocketProcessor對象中。然后從Http11ConnectionHandler中取出Http11NioProcessor對象,從Http11NioProcessor中調(diào)用CoyoteAdapter的邏輯,跟BIO實現(xiàn)一樣。在Worker線程中,會完成從socket中讀取http request,解析成HttpServletRequest對象,分派到相應(yīng)的servlet并完成邏輯,然后將response通過socket發(fā)回client。在從socket中讀數(shù)據(jù)和往socket中寫數(shù)據(jù)的過程,并沒有像典型的非阻塞的NIO的那樣,注冊O(shè)P_READ或OP_WRITE事件到主Selector,而是直接通過socket完成讀寫,這時是阻塞完成的,但是在timeout控制上,使用了NIO的Selector機制,但是這個Selector并不是Poller線程維護的主Selector,而是BlockPoller線程中維護的Selector,稱之為輔Selector。?

NioSelectorPool?
NioEndpoint對象中維護了一個NioSelecPool對象,這個NioSelectorPool中又維護了一個BlockPoller線程,這個線程就是基于輔Selector進行NIO的邏輯。以執(zhí)行servlet后,得到response,往socket中寫數(shù)據(jù)為例,最終寫的過程調(diào)用NioBlockingSelector的write方法。?

Java代碼??
  • public?int?write(ByteBuffer?buf,?NioChannel?socket,?long?writeTimeout,MutableInteger?lastWrite)?throws?IOException?{??
  • ????????SelectionKey?key?=?socket.getIOChannel().keyFor(socket.getPoller().getSelector());??
  • ????????if?(?key?==?null?)?throw?new?IOException("Key?no?longer?registered");??
  • ????????KeyAttachment?att?=?(KeyAttachment)?key.attachment();??
  • ????????int?written?=?0;??
  • ????????boolean?timedout?=?false;??
  • ????????int?keycount?=?1;?//assume?we?can?write??
  • ????????long?time?=?System.currentTimeMillis();?//start?the?timeout?timer??
  • ????????try?{??
  • ????????????while?(?(!timedout)?&&?buf.hasRemaining())?{??
  • ????????????????if?(keycount?>?0)?{?//only?write?if?we?were?registered?for?a?write??
  • ????????????????????//直接往socket中寫數(shù)據(jù)??
  • ????????????????????int?cnt?=?socket.write(buf);?//write?the?data??
  • ????????????????????lastWrite.set(cnt);??
  • ????????????????????if?(cnt?==?-1)??
  • ????????????????????????throw?new?EOFException();??
  • ????????????????????written?+=?cnt;??
  • ????????????????????//寫數(shù)據(jù)成功,直接進入下一次循環(huán),繼續(xù)寫??
  • ????????????????????if?(cnt?>?0)?{??
  • ????????????????????????time?=?System.currentTimeMillis();?//reset?our?timeout?timer??
  • ????????????????????????continue;?//we?successfully?wrote,?try?again?without?a?selector??
  • ????????????????????}??
  • ????????????????}??
  • ????????????????//如果寫數(shù)據(jù)返回值cnt等于0,通常是網(wǎng)絡(luò)不穩(wěn)定造成的寫數(shù)據(jù)失敗??
  • ????????????????try?{??
  • ????????????????????//開始一個倒數(shù)計數(shù)器???
  • ????????????????????if?(?att.getWriteLatch()==null?||?att.getWriteLatch().getCount()==0)?att.startWriteLatch(1);??
  • ????????????????????//將socket注冊到輔Selector,這里poller就是BlockSelector線程??
  • ????????????????????poller.add(att,SelectionKey.OP_WRITE);??
  • ????????????????????//阻塞,直至超時時間喚醒,或者在還沒有達(dá)到超時時間,在BlockSelector中喚醒??
  • ????????????????????att.awaitWriteLatch(writeTimeout,TimeUnit.MILLISECONDS);??
  • ????????????????}catch?(InterruptedException?ignore)?{??
  • ????????????????????Thread.interrupted();??
  • ????????????????}??
  • ????????????????if?(?att.getWriteLatch()!=null?&&?att.getWriteLatch().getCount()>?0)?{??
  • ????????????????????keycount?=?0;??
  • ????????????????}else?{??
  • ????????????????????//還沒超時就喚醒,說明網(wǎng)絡(luò)狀態(tài)恢復(fù),繼續(xù)下一次循環(huán),完成寫socket??
  • ????????????????????keycount?=?1;??
  • ????????????????????att.resetWriteLatch();??
  • ????????????????}??
  • ??
  • ????????????????if?(writeTimeout?>?0?&&?(keycount?==?0))??
  • ????????????????????timedout?=?(System.currentTimeMillis()?-?time)?>=?writeTimeout;??
  • ????????????}?//while??
  • ????????????if?(timedout)???
  • ????????????????throw?new?SocketTimeoutException();??
  • ????????}?finally?{??
  • ????????????poller.remove(att,SelectionKey.OP_WRITE);??
  • ????????????if?(timedout?&&?key?!=?null)?{??
  • ????????????????poller.cancelKey(socket,?key);??
  • ????????????}??
  • ????????}??
  • ????????return?written;??
  • ????}??

  • 也就是說當(dāng)socket.write()返回0時,說明網(wǎng)絡(luò)狀態(tài)不穩(wěn)定,這時將socket注冊O(shè)P_WRITE事件到輔Selector,由BlockPoller線程不斷輪詢這個輔Selector,直到發(fā)現(xiàn)這個socket的寫狀態(tài)恢復(fù)了,通過那個倒數(shù)計數(shù)器,通知Worker線程繼續(xù)寫socket動作。看一下BlockSelector線程的邏輯;?

    Java代碼??
  • public?void?run()?{??
  • ????????????while?(run)?{??
  • ????????????????try?{??
  • ????????????????????......??
  • ??
  • ????????????????????Iterator?iterator?=?keyCount?>?0???selector.selectedKeys().iterator()?:?null;??
  • ????????????????????while?(run?&&?iterator?!=?null?&&?iterator.hasNext())?{??
  • ????????????????????????SelectionKey?sk?=?(SelectionKey)?iterator.next();??
  • ????????????????????????KeyAttachment?attachment?=?(KeyAttachment)sk.attachment();??
  • ????????????????????????try?{??
  • ????????????????????????????attachment.access();??
  • ????????????????????????????iterator.remove();?;??
  • ????????????????????????????sk.interestOps(sk.interestOps()?&?(~sk.readyOps()));??
  • ????????????????????????????if?(?sk.isReadable()?)?{??
  • ????????????????????????????????countDown(attachment.getReadLatch());??
  • ????????????????????????????}??
  • ????????????????????????????//發(fā)現(xiàn)socket可寫狀態(tài)恢復(fù),將倒數(shù)計數(shù)器置位,通知Worker線程繼續(xù)??
  • ????????????????????????????if?(sk.isWritable())?{??
  • ????????????????????????????????countDown(attachment.getWriteLatch());??
  • ????????????????????????????}??
  • ????????????????????????}catch?(CancelledKeyException?ckx)?{??
  • ????????????????????????????if?(sk!=null)?sk.cancel();??
  • ????????????????????????????countDown(attachment.getReadLatch());??
  • ????????????????????????????countDown(attachment.getWriteLatch());??
  • ????????????????????????}??
  • ????????????????????}//while??
  • ????????????????}catch?(?Throwable?t?)?{??
  • ????????????????????log.error("",t);??
  • ????????????????}??
  • ????????????}??
  • ????????????events.clear();??
  • ????????????try?{??
  • ????????????????selector.selectNow();//cancel?all?remaining?keys??
  • ????????????}catch(?Exception?ignore?)?{??
  • ????????????????if?(log.isDebugEnabled())log.debug("",ignore);??
  • ????????????}??
  • ????????}??

  • 使用這個輔Selector主要是減少線程間的切換,同時還可減輕主Selector的負(fù)擔(dān)。以上描述了NIO connector工作的主要邏輯,可以看到在設(shè)計上還是比較精巧的。NIO connector還有一塊就是Comet,有時間再說吧。需要注意的是,上面從Acceptor開始,有很多對象的封裝,NioChannel及其KeyAttachment,PollerEvent和SocketProcessor對象,這些不是每次都重新生成一個新的,都是NioEndpoint分別維護了它們的對象池;?

    Java代碼??
  • ConcurrentLinkedQueue<SocketProcessor>?processorCache?=?new?ConcurrentLinkedQueue<SocketProcessor>()??
  • ConcurrentLinkedQueue<KeyAttachment>?keyCache?=?new?ConcurrentLinkedQueue<KeyAttachment>()??
  • ConcurrentLinkedQueue<PollerEvent>?eventCache?=?new?ConcurrentLinkedQueue<PollerEvent>()??
  • ConcurrentLinkedQueue<NioChannel>?nioChannels?=?new?ConcurrentLinkedQueue<NioChannel>()??

  • 當(dāng)需要這些對象時,分別從它們的對象池獲取,當(dāng)用完后返回給相應(yīng)的對象池,這樣可以減少因為創(chuàng)建及GC對象時的性能消耗。

    轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/articles/11452705.html

    總結(jié)

    以上是生活随笔為你收集整理的tomcat架构分析 (connector NIO 实现)【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。