NIO最佳實踐

2021-09-11 12:48:54 字數 4301 閱讀 2362

現象:cpu占用超高

原因:訂閱了selectionkey.op_write事件

iteratoriterator = selector.selectedkeys().iterator();

while (iterator.hasnext())

socketchannel.register(selector, selectionkey.op_read | selectionkey.op_write);

}複製**

分析:當socket緩衝區可寫入時就會觸發op_write事件. 而socket緩衝區大多時間都可寫入(網路不擁堵),由於nio水平觸發的特性op_write會一直觸發導致while()一直空轉

水平觸發: 簡單解釋為只要滿足條件就一直觸發,而不是發生狀態改變時才觸發(有點主動和被動觸發的感覺)

最佳實踐:

方案一: 當有寫資料需求時訂閱op_write事件,資料傳送完成取消訂閱.

while (channel.isopen()) 

//當採用臨時訂閱op_write方式 必須使用select(ms)進行超時返回

// 因為很有可能當select()前極短時間內writebuffer有資料,而此時沒有訂閱op_write事件,會使select()一直阻塞

int ready = selector.select(300);

if (ready > 0)

writebuffer.clear();

socketchannel.register(selector, selectionkey.op_read);}}

}複製**

當使用臨時訂閱op_write事件方式時,必須使用selector.select(long),進行超時返回. 因為很有可能當select()前極短時間內writebuffer有資料,而此時沒有訂閱op_write事件,會使select()一直阻塞

方案二: 不訂閱op_write事件,直接通過socketchannel.write()寫資料.

selector selector = selector.open();

channel.register(selector, selectionkey.op_connect);

channel.connect(new inetsocketaddress("localhost", 5555));

while (channel.isopen())

writebuffer.clear();

}int ready = selector.select(500);

...各種事件處理

}複製**

方案三: 一直訂閱op_write,socketchannel主動寫

while (channel.isopen()) 

//訂閱讀/寫事件

socketchannel.register(selector, selectionkey.op_read | selectionkey.op_write);

}if (selectionkey.isreadable())

if (selectionkey.iswritable()) }}

}}

}/**

* 等待獲取寫快取

* @param bytebuf 緩衝區

* @param ms 緩衝時間 防止空轉

* @param cap 閾值:超過則直接返回,沒超過等待ms後判斷是否超過閾值

* @return

*/public bytebuffer awaitgetwrite

(bytebuf bytebuf, long ms, int cap)

else catch (interruptedexception e)

if (bytebuf.readablebytes() > 0) else }}

複製**

優點缺點

方案1當網路擁堵時,不嘗試寫資料

需要自己控制訂閱/取消訂閱的時機

方案2不關心網路擁堵,只要有資料就嘗試寫,當網路擁堵時做大量無用功

編寫方便,無需關心op_write事件訂閱時機

方案3相比方案1 編碼複雜度下降

綜合上述個人覺得還是方案3比較好

現象:網路擁堵時,cpu占用超高

原因:網路擁堵時, channel.write()一直寫不進去,導致while()空轉

採取上一問題方案3可以避免該問題

writebuffer.flip();

while (writebuffer.hasremaining())

writebuffer.clear();

複製**

分析:當網路擁堵時,channel.write()可能寫入0資料,而這裡採用死迴圈寫入資料,假如一直寫不進去就會導致空轉

最佳實踐:

while (writebuffer.isreadable())  

}複製**

結合op_write訂閱時機問題,可以得知方案一的臨時訂閱op_write事件方式,能更好的防止channel.write(bytebuffer)空轉

現象:當tcp一方斷開時,另一方cpu占用超高

原因:當tcp一方斷開時,一直會觸發op_read,導致空轉.

分析:當tcp一方斷開時,觸發op_read,socketchannel.read(readbuffer)返回-1,表示對方連線已斷開,自己也需要斷開連線socketchannel.close(),否則會一直觸發op_read,導致空轉

while (true) 

socketchannel.register(selector, selectionkey.op_read | selectionkey.op_write);

} else

if (selectionkey.isreadable())

} ...}}

}複製**

最佳實踐:

if (selectionkey.isreadable())  else

if (read == -1)

}複製**

bytebuf,bytebuffer對比

特性bytebuffer

1.有position,limit屬性,通過flip()切換讀寫模式 ,不支援同時讀/寫 2.定長 3.直接記憶體

bytebuf

1.有rix,wix,cap,maxcap屬性,支援同時讀/寫 2.自動擴容 3.直接記憶體,堆記憶體,組合

建議使用bytebuf

現象:使用clear()導致丟資料

原因:clear()實現通過 rix=wix=0,假如此時同時有資料寫入,該部分資料則丟失

if (selectionkey.iswritable())  else 

}...

}複製**

最佳實踐:

使用discardreadbytes(),其通過arraycopy方式並且執行緒安全,能夠防止資料丟失.但頻繁的arraycopy會有效能問題. 可以使用clear()和discardreadbytes()的組合

if (selectionkey.iswritable()) 

bytebuffer bytebuffer = writebuffer.niobuffer();

channel.write(bytebuffer);

writebuffer.readerindex(writebuffer.readerindex() + bytebuffer.position());

int left = bytebuffer.limit() - bytebuffer.position();

if (left != 0) else }}

...}複製**

epoll空輪訓bug

最佳實踐 Flutter 最佳實踐

最佳實踐是乙個領域可以接受的專業標準,對於任何程式語言來說,提高 質量 可讀性 可維護性和健壯性都非常重要。讓我們探索一些設計和開發flutter應用程式的最佳實踐。class enum typedef和extension應採用駝峰命名uppercamelcase規則。class mainscree...

JUnit最佳實踐

junit最佳實踐 cherami 轉貼 參與分 20053,專家分 4960 發表 2003 9 16 下午7 57 版本 1.0 閱讀 3899次 martin fowler說過 當你試圖列印輸出一些資訊或除錯乙個表示式時,寫一些測試 來替代那些傳統的方法。一開始,你會發現你總是要建立一些新的f...

SVN最佳實踐

楊爭 subversion是新一代的版本控制工具,由於其優於cvs的一些特點,得到了越來越多人的關注和使用,本人根據自己使用svn的經驗,寫了這篇文章,希望對大家有所幫助,其中有些實踐並不是僅僅適用於svn,對其他版本控制工具也是適用的。1 養成良好的記錄日誌的習慣.svn ci提交,最好在日誌中記...