【経験まとめ】NIOでよく見られる落とし穴解析

3858 ワード

NIOプログラミングは簡単ですか?大変ですか.簡単ですか?容易ではない
ですか?......
いくつかの罠を知っておく必要があります
 
トラップ1:イベントの処理忘れキーの削除
select戻り値が0より大きい場合、ループ処理
Selector.selectedKeys   ,         
Iterator<SelectionKey> it=set.iterator();
While(it.hasNext()){
    SelectionKey key=it.next();
    it.remove(); //    
    ……    
}

除去しない結果は今回の準備のできたkey集合が次回再び戻ってくることです
回,无限循环,CPU消耗100%
 
トラップ2:Selectorが返すkey集合非スレッドセキュリティ
1、Selector.selectedKeys/keysが返す集合はすべて非スレッドで安全である
2、Selector.selectedKeysが返す取り外し可能
3、Selector.keys不変
4、selected keysの処理は単一スレッド処理または適切な同期が必要
 
トラップ3:チャネルを正しく登録し、interestを更新
1、直接登録してはいけませんか.
      channel.register(selector, ops, attachment);
2、いけないわけではない、効率の問題
3、少なくとも2回ロックをかけて、ロックの競争が激しい
4、チャンネル自体のregLockは、競争がほとんどない
5、Selector内部のkey集合、競争が激しい
6、もっと良い方法:バッファキューに参加して、登録を待って、reactor単一スレッド処理
If(isReactorThread()){
     channel.register(selector, ops, attachment);
}
else{
     register.offer(new Event(channel,ops,attachment));
     selector.wakeup();
}

 
7、プラットフォームの違いを遮断し、ロックの激しい競争を避け、登録channelのような方式を採用する.
if (this.isReactorThread()) {
    key.interestOps(key.interestOps() | SelectionKey.OP_READ);
}
else {
    this.register.offer(new Event(key,SelectionKey.OP_READ));
    selector.wakeup();
}

 
 
 トラップ4:OP_を正しく処理WRITE
1、 OP_WRITEの不適切な処理はCPU 100%を招きやすい
2、 OP_WRITEトリガ条件
       前提:interest了OP_WRITE
       トリガ条件:
            ソケット送信バッファ書き込み可能
            リモートオフ
            エラー発生
3、正しい処理方式
4、接続されたチャンネルにのみ登録
5、データが書ける時だけ登録する
6.トリガーされた直後に登録をキャンセルします.そうしないと、トリガーされ続けてループが発生します.
7、処理完了後、状況に応じて登録を継続するかどうかを決定する
       完全な書き込みなしで登録を続行
       登録不要ですべて書き込み
 
 
トラップ5:channelの登録を正しくキャンセル
1、SelectableChannel登録が明確に取り消されるまで有効である
2、どうやって登録をキャンセルしますか?
      channel.close()内部でkey.cancel()が呼び出されます.
       key.cancel();
3、チャンネルの読み書きを中断したスレッドによるチャンネルクローズ
      でもまだ足りない!
      key.cancel()は、keyをcancelledKeysに追加するだけです. 次のselectまで本当に処理され、channelのsocketfdは本当に登録をキャンセルした後にしかclose(fd)されません.
4、結果は何ですか.
      サービス側、問題は大きくなく、select呼び出しが頻繁です
      クライアントは、通常1つの接続しかなく、channelを閉じた後、selectを呼び出さずにselectorを閉じます.
      sockfdは閉じずCLOSE_にとどまるWAIT状態
      正しい処理方法は、登録を取り消すこともイベントとしてreactorに渡し、直ちにwakeupしてselectを行うべきである.
5、適宜selector.selectNow()を呼び出す
6、Nettyは256以上の接続がクローズされた時、自発的にselectNowを呼び出す
 
トラップ6:OP_同時登録ACCPETとOP_READ、同時にOP_を登録するCONNECTとOP_WRITE
1、最下位ではreadとwriteの2つのイベントしかありません
     Java NIOにはOP_も導入されていますACCEPTとOP_CONNECT
     OP_ACCEPT、OP_READ == Read
     OP_CONNECT、OP_WRITE == Write
    同時登録OP_ACCEPTとOP_READ、またはOP_同時登録CONNECTとOP_WRITEは異なるプラットフォームで誤った行動を起こし、それを避ける!
 
罠8:NIOのバグ
 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933
 http://bugs.sun.com/view_bug.do?bug_id=6693490
1、現象:すでに閉じた接続がずっと準備状態にあることを招き、select(timeout)がブロックされず、CPUが100%消費する
     解決:
     jdk 6 u 4以降にアップグレード
     コード上の回避
2、コードはどのようにこのバグを回避しますか?
      単純なシナリオ:channel.close()のたびにselectを呼び出す
     Jetty 6のシナリオ:
     selectブロック時間が設定値よりはるかに小さい場合
     すべてのinterestが0のkeyをキャンセル
     Selectorを再作成し、有効なkeyを登録します.
     まだ丈夫ではありません.selectは中断やwakeupの起動によって誤審を招く可能性があります.
     より完備:wakeup判断と中断状態判断を加える
     Minaにはこのバグを回避するコードがあります
     Netty 3、定期的にselectNowを呼び出す
 
最後の忠告
1、経験豊富なエンジニアがいない限り、できるだけ自分のnioフレームワークを実現しないでください.
2、できるだけ広く実践されたオープンソースNIOフレームワークMina、Netty 3、xSocketを使用する
3、できるだけ最新の安定版JDKを使う
4、問題があったら、javaのバグを見てもいいかもしれません database