mina 中的IoBufer(一)
生活随笔
收集整理的這篇文章主要介紹了
mina 中的IoBufer(一)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
IoBuffer 是 MINA 中的獨(dú)有接口,主要繼承實(shí)現(xiàn)的是 java NIO 中的 ByteBuffer ,所以從使用方法上來(lái)看二者區(qū)別不大,唯一比較大的區(qū)別就是, IoBuffer 支持可變長(zhǎng)的數(shù)據(jù)填充,對(duì)于這個(gè)類有三個(gè)關(guān)鍵屬性,分別是capacity( 容量 ) : 是它所包含的元素的數(shù)量。緩沖區(qū)的容量不能為負(fù) limit( 限制 ) :????? 是第一個(gè)不應(yīng)該讀取或?qū)懭氲脑氐乃饕?
position ( 位置 ) :??? 是下一個(gè)要讀取或?qū)懭氲脑氐乃饕?
以上三個(gè)屬性的值都不能為負(fù),其關(guān)系遵守以下不變式:
0<= 位置 <= 限制 <= 容量
在這里還要鄭重的介紹三個(gè)方法分別是:
clear() : 使緩沖區(qū)為一系列新的通道讀取或相對(duì)放置 操作做好準(zhǔn)備:它將限制設(shè)置為容量大小,將位置設(shè)置為 0 。
flip() : 使緩沖區(qū)為一系列新的通道寫入或相對(duì)獲取 操作做好準(zhǔn)備:它將限制設(shè)置為當(dāng)前位置,然后將位置設(shè)置為 0 。
rewind() : 使緩沖區(qū)為重新讀取已包含的數(shù)據(jù)做好準(zhǔn)備:它使限制保持不變,將位置設(shè)置為 0 。
從本質(zhì)上講這三個(gè)方法都是對(duì)之前所介紹的三個(gè)屬性進(jìn)行操作,所以這里就會(huì)有一個(gè)誤區(qū),很多人從字面上解讀 clear() 這個(gè)方法,以為這個(gè)方法就是將數(shù)據(jù)擦除。而實(shí)際上 clear 方法并不有擦除數(shù)據(jù),僅僅是做了二件事,一是將 limit=capacity, 二是 position=0 ,這使得數(shù)據(jù)好像被清除,以便接收下一次的讀寫。但請(qǐng)大家千萬(wàn)注意 ,在寫自定義解(編)碼器時(shí)一定不要隨便使用這些方法,請(qǐng)確保在對(duì)這些方法有了足夠的認(rèn)識(shí)后(最好的方法就是解讀源碼),才去使用它。特別是直接使用 clear() 時(shí),一旦使用不當(dāng),在截取數(shù)據(jù)非常容易發(fā)生死循環(huán)的情況,因?yàn)榍懊嬉呀?jīng)說(shuō)過(guò), clear 并不是清除數(shù)據(jù),而只是改變屬性值,也就是說(shuō)原數(shù)據(jù)依舊保留,這會(huì)導(dǎo)致某些時(shí)候會(huì)不停的重置這三個(gè)屬性,去讀同一段索引的數(shù)據(jù),從而導(dǎo)致死循環(huán)。網(wǎng)上好多 demo 都是直接使用的 clear() ,其實(shí)都是存在一定隱患的,希望能看到這里的朋友能引起警惕 , 個(gè)人認(rèn)為最好的清除方式就是人為的控制 Iobuffer 的三個(gè)屬性值。 以下是參考代碼 :
int oldPos = in.position();
int oldLimit = in.limit();
....... 開(kāi)始 ..........
處理數(shù)據(jù)
....... 結(jié)束 ..........
int pos = in.position();
in.limit(oldLimit);
in.position(pos);
注: 以上代碼僅適用于一次讀取不完,需要從iobuffer 中多次讀取的情況,具體情況請(qǐng)具體對(duì)待,核心思想就是操作iobuffer 的三個(gè)屬性。
sendUrgentData() 方法的使用
這個(gè)方法的作用,只要是用過(guò) socket 編程的童鞋都知道它的作用。沒(méi)錯(cuò),它就是通過(guò)發(fā)送一個(gè)緊急字符到服務(wù)端 (對(duì) socket 來(lái)說(shuō)實(shí)際上并不存在嚴(yán)格意義上的客戶端或服務(wù)端,誰(shuí)主動(dòng)誰(shuí)就是客戶端) ,用來(lái)測(cè)試連接是否保持的一個(gè)方法。使用這個(gè)方法有二個(gè)限制。一是必須對(duì)方的 OS 支持該方法,二是要求服務(wù)端的 SO_OOBINLINE 為 false. 否則 服務(wù) 端將會(huì)把這個(gè)緊急字符認(rèn)為是正常報(bào)文一并接收,而不是拋棄。反之當(dāng) SO_OOBINLINE 為 true 時(shí),這個(gè)緊急字符僅僅用來(lái)確認(rèn)通訊是否正常之用,服務(wù)端接收后會(huì)立刻拋棄不予處理的。默認(rèn)情況下 SO_OOBINLINE 的值就是 false, 所以一般情況下,客戶端直接用這個(gè)方法就可以測(cè)試連接是否保持了。
按理來(lái)說(shuō)這個(gè)默認(rèn)設(shè)置是個(gè)好事,但在筆者的項(xiàng)目開(kāi)發(fā)中曾經(jīng)因?yàn)檫@個(gè) sendUrgentData () 被困擾了近一周的時(shí)間。事情的起因要從性能測(cè)試開(kāi)始說(shuō)起。
測(cè)試人員在測(cè)試過(guò)程中發(fā)現(xiàn)當(dāng)前置機(jī)啟動(dòng)后有連接產(chǎn)生時(shí)就會(huì)讓 CPU 占用率高居不下,開(kāi)始筆者不是很在意,認(rèn)為這個(gè)時(shí)間里有 IO 操作, CPU 高居不下很正常,后來(lái)進(jìn)一步測(cè)試發(fā)生在沒(méi)有數(shù)據(jù)發(fā)送的情況下 CPU 也會(huì)占到近 50% 左右,這個(gè)現(xiàn)象就很不正常了,于是折騰開(kāi)始。
先是確認(rèn)前置機(jī)的哪個(gè)部分會(huì)占用 cpu, 很快將目標(biāo)鎖定到了調(diào)度機(jī),接下來(lái)對(duì)調(diào)度機(jī)進(jìn)行代碼排查,沒(méi)有發(fā)生任何問(wèn)題,頭大了,再次進(jìn)入 QQ 群討論該問(wèn)題,有人向我推薦了 JRMC (這也是我要向大家強(qiáng)烈推薦該工具的原因,網(wǎng)絡(luò)的力量是強(qiáng)大的!!!嘿嘿。。。),通過(guò)這個(gè)監(jiān)視工具,本人很快就再次縮小目標(biāo),將目標(biāo)定位到一個(gè)叫 Ioprocesse-1 的線程上面,從名字及本文之前介紹的內(nèi)容來(lái)看,很明顯這個(gè) MINA 框架內(nèi)部線程導(dǎo)致的,隨后就到網(wǎng)上查找是否有同類的現(xiàn)象,很遺憾本人可能是遇到一個(gè)前所未有的問(wèn)題了,網(wǎng)絡(luò)上提到使用 MINA 導(dǎo)致 CPU 占用率過(guò)高的內(nèi)容幾乎沒(méi)有,無(wú)奈之下本人試著換 JDK 版本、 MINA 版本、甚至改寫 MINA 源代碼,一番折騰下來(lái),結(jié)果是統(tǒng)統(tǒng)做了無(wú)用功。因?yàn)檫@個(gè)問(wèn)題暫時(shí)不會(huì)影響測(cè)試和使用再加上時(shí)間過(guò)緊,后來(lái)就暫時(shí)將這個(gè)問(wèn)題擱置了起來(lái)。某天,在開(kāi)發(fā)的過(guò)程中筆者忽然想到:前置機(jī)的三個(gè)部分都是獨(dú)立程序,通信機(jī)的接口也都是用 mina 改寫的,為什么終端與通訊機(jī)沒(méi)有這種現(xiàn)象發(fā)生呢?一番推理之下,本人反而將目標(biāo)鎖定到了占用率正常的通機(jī)機(jī)上面了,通過(guò)反復(fù)的排查,最終將問(wèn)題鎖定到了方法級(jí),那就是 sendUrgentData 這個(gè)罪魁禍?zhǔn)住?
本人試驗(yàn)發(fā)現(xiàn),只要沒(méi)有調(diào)用 sendUrgentData 方法所有一切都很正常, 但通信機(jī) 一旦調(diào)用 sendUrgentData 方法用來(lái)測(cè)試與調(diào)度機(jī)是否保持通信時(shí),就會(huì)讓調(diào)度機(jī)的 CPU 占用率瞬間飆升。但一個(gè)通信程序測(cè)試連接的方法是必不可少,而且暫時(shí)沒(méi)有更好的能代替 sendUrgentData 的方法,所以就想著去改造 sendUrgentData 的源代碼,結(jié)果一番跟蹤下來(lái)才傻了眼,原來(lái) sendUrgentData 方法 的底層實(shí)現(xiàn)是 native 類型( 注三 )根本就無(wú)從改起,最終本人將注意打到了調(diào)度機(jī)的解碼器上面。代碼比較多筆者就不帖了,只寫上具體的解決步驟,有實(shí)際需求的,歡迎討論。
步驟:
1 、在客戶端(本例中客戶端為通訊機(jī))使用 sendUrgentData ()方法時(shí),請(qǐng)?jiān)O(shè)置一個(gè)報(bào)?? 文??? 中不可能出現(xiàn)的數(shù)字,本案例中使用的是 -16 。
2、? 在服務(wù)端的處理類(即實(shí)現(xiàn) IoHandlerAdapter 的類,為必須類)的 sessionCreated 方法內(nèi)設(shè)置 SO_OOBINLINE 為 true. 代碼如下:
@Override
public void sessionCreated(IoSession session) throws Exception {
IoSessionConfig cfg = session.getConfig();
if (cfg instanceof SocketSessionConfig) {
((SocketSessionConfig) cfg).setOobInline( true );
}
}
3、? 在服務(wù)端的解碼器中將接受到的 -16 的字符全部手動(dòng)拋棄(注意 IoBuff 三屬性的重新設(shè)定)。
至此問(wèn)題解決,但從解決方法來(lái)看,這個(gè)方法并不具有代表性,當(dāng)碰到以下情況是并不一定適用:
情況一: 報(bào)文的內(nèi)容本身有可能無(wú)所不包,那么 sendUrgentData 設(shè)置什么好呢??
情況二: 客戶端的程序無(wú)法改動(dòng)時(shí),又該如何應(yīng)對(duì)呢??
當(dāng)然這個(gè)問(wèn)題的終結(jié)解決方案并不在本文的討論范圍之類,這個(gè)很明顯就是 mina 的一個(gè) BUG ,不知道 2.04 的版本有沒(méi)有解決這個(gè)問(wèn)題,但從官網(wǎng)所貼的問(wèn)題列表來(lái)看,這個(gè) BUG 應(yīng)該是依舊存在的,奈何筆者 E 文比較爛,看看資料還可以,要我動(dòng)手寫,并將以上內(nèi)容用 E 文表達(dá)出來(lái),確實(shí)是沒(méi)有那個(gè)勇氣的,希望能看到這一段的童鞋能代勞一、二,督促官方早日改好這個(gè) BUG ,咱也算是為開(kāi)源軟件盡了一份力不是? ^_^
有的童鞋看到這里可能會(huì)說(shuō)筆者是不是太啰嗦了,幾句話可以解決的事情講這么多,這里我想強(qiáng)調(diào)一下的是,本文 的核心內(nèi)容 主要講的還是使用心得, 而 不是使用方法,在這里之所以把 解決 過(guò)程講得這么詳細(xì),還是希望達(dá)成二點(diǎn)目的。
1 、筆者希望通過(guò)對(duì)過(guò)程詳細(xì)的描述,來(lái)提醒大家。做咱們這一行的不可能不遇到問(wèn)題,但遇到問(wèn)題首要的是先縮小范圍,再確定范圍,實(shí)際確定不下來(lái)的,不妨跟同行討論一番,如果還確定不下來(lái),咱就暫時(shí)放一放,等腦子靜下來(lái)以后,把思路跳出來(lái),反向追蹤,說(shuō)不定問(wèn)題的表象并不是問(wèn)題發(fā)生的緣原呢?(這話有點(diǎn)拗口,不做解釋。。。。?? -_-!! )在本文中就是一個(gè)很明顯的例子,明明是調(diào)度機(jī)表現(xiàn)出來(lái)的 CPU 占用過(guò)高,但導(dǎo)致這一現(xiàn)象發(fā)生的卻是在通訊機(jī)上了。
2 、再次推薦 JRMC, 這東西真的很好用。獨(dú)樂(lè)樂(lè)不如眾樂(lè)樂(lè)~~?? 什么?你已經(jīng)知道了?知道了,你怎么不告訴我?你真是壞啊。。。。。。哦 ~ 漏說(shuō)了一句 , 這個(gè)工具是 Oracle JRockit JDK 自帶的,據(jù)說(shuō)這個(gè) JDK 的效率能比普通 JDK 的效率高上 2%-10% 非常適合在生產(chǎn)環(huán)境下使用。 什么??你還是知道了?? 你,你,你。。。。。。(吐血 ing.... )
當(dāng)然了,群眾的力量是無(wú)窮的,大家嫌我太羅嗦的話,下面的內(nèi)容我就簡(jiǎn)單一點(diǎn)吧。
注三: 所謂的 native 類型的方法,是 JAVA 中的一種特殊方法,凡是標(biāo)注這個(gè)關(guān)鍵字的方法,其本身并沒(méi)有任何實(shí)現(xiàn)代碼,最終的運(yùn)行都是通過(guò)虛擬機(jī)調(diào)用 OS 的底層的方法來(lái)運(yùn)行的。
Concurrent 包下的一些類
這里主要推薦二個(gè)集合類,分別是 ConcurrentLinkedQueue , ConcurrentHashMap 這二個(gè)類的使用基本上不需要你考慮是不是多線程的編程,也不需要用鎖,可以大幅提高并發(fā)量過(guò)大時(shí)的對(duì)像存取,至于實(shí)現(xiàn)機(jī)制勞駕大家自己去 GOOGLE 一把吧。
結(jié)尾語(yǔ)
文章到這里基本上算是結(jié)束了,而項(xiàng)目最終的并發(fā)量從 2680 提升至 3.5W, 客戶要求的是 2W 的并發(fā)量,所以后面更高的并發(fā)沒(méi)有繼續(xù)再測(cè),至本文截稿為止,最新的消息是項(xiàng)目的一期工作已經(jīng)順利通過(guò)了國(guó)家級(jí)機(jī)構(gòu)的評(píng)測(cè)。但實(shí)質(zhì)上本文關(guān)于 MINA 框架還有很多東西沒(méi)有涉及到,比如說(shuō)與 Spring 的結(jié)合,對(duì) Jmx 的支持,自定義協(xié)議詳細(xì)舉例等等,但我想這點(diǎn)小困難應(yīng)該不會(huì)妨礙大家對(duì) mina 的學(xué)習(xí)熱情吧!感興趣的童鞋不妨把它當(dāng)成一個(gè)課后作業(yè)來(lái)做做吧 !!? ^_^
轉(zhuǎn)載于:https://my.oschina.net/leoson/blog/104093
總結(jié)
以上是生活随笔為你收集整理的mina 中的IoBufer(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 免费音乐接口,当时写音乐播放器没资源,今
- 下一篇: [转载红鱼儿]kbmmw 开发点滴:kb