現象: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提交,最好在日誌中記...