Nettyフレームワーク学習の道(一)-JavaネットワークIOモデル

6134 ワード

前言
『Unixネットワークプログラミング:ボリューム1』では5中IOモデルが紹介されており、JAVAはホストホストで動作するプログラムとして、下位層もこの5中I/Oモデルルールに従っている.この5のI/Oモデルはそれぞれ:
  • ブロックIO
  • 非ブロックIO
  • I/O多重
  • 信号駆動式IO
  • 非同期IO
  • POSIX規格では、IOは同期と非同期に分けられ、上の4時間前までは同期IOに属していた.
    Unixシステムでは、オペレーティングシステムのIO操作は、システム呼び出しrecvfrom()であり、システム呼び出しrecvfromは、データの準備とコピーを待つ2つのステップを含む.
    同期およびブロックに関する概念
    同期と非同期はデータにアクセスするメカニズムであり、同期は一般的にアクティブに要求し、IO操作が完了するのを待つ方法を指し、データが準備された後に読み書きするときにブロックしなければならない.
    非同期とは、アクティブにデータを要求した後、他のタスクを処理し続け、IO操作が完了したという通知を待つことで、プロセスがデータの読み書き時にブロックされないようにすることを意味します.
    ブロックと非ブロックとは、プロセスがアクセスするデータがまだ準備されていない場合、プロセスが待つ必要があるかどうかを指します.簡単に言えば、これは関数内部の実装の違い、すなわち、準備ができていない場合に直接戻るか、準備が待っているかに相当します.
    この4つの概念の2つの組み合わせには4つのIOメカニズムがあり、私たちは普段よく見られるダウンロードファイルのシーンでこの4つのメカニズムを整理します.
  • 同期ブロック:明ちゃんはダウンロードの任務を提出した後、ダウンロードの進捗バーをじっと見つめて、完成するまで.
  • 同期非ブロック:明ちゃんはダウンロードの任務を提出した後、別のことをして、ダウンロードが完了するまで、一定の時間ごとにダウンロードの進捗バーを見ています.
  • 非同期ブロック:明ちゃんはダウンロードの任務を提出した後、何もしないでダウンロードの完成を待っているヒント音です.
  • 非同期非ブロック:小明さんはダウンロードの任務を提出した後、別のことをして、「チリン」の通知を受け取るだけでいいです.

  • IOモデル
    ブロックIO
    アプリケーションがIO要求を開始すると、オペレーティングシステムはシステム呼び出しrecvfrom()を処理し、この過程でオペレーティングシステムはデータ準備(データが他のアプリケーションの入力またはネットワークから来る可能性がある)を待つ必要があり、アプリケーションは他のことを処理するのではなく、データ準備(すなわちブロック状態)をずっと待ってから、オペレーティングシステムはIO操作を完了し、その後recvfrom()メソッドが返され、アプリケーションが実行を続行します.これがブロックIOモデルです.
    //     ,         
    ServerSocket serverSocket = new ServerSocket(20000);
    //       ,      
    client = serverSocket.accept();
    //        ,    
    System.out.println("       ");
    //                
    new Thread(new ServerThread(client)).start();
    
    //             
    class ServerThread implements Runnable {
        .....
        @Override
        public void run() {
            boolean flag = true;
            while (flag) {
                //            
                String str = buf.readLine();
                //        get       
                out.println("get"); 
            }
        }
    }
    Socket client = new Socket("127.0.0.1", 20000);
    boolean flag = true;
    while (flag) {
        //           
        String str = input.readLine();
        //             
        out.println(str);
        //           get    
        String echo = buf.readLine();
        System.out.println(echo);
        }
    }

    上記のコードから分かるように、あるclientが接続を確立するたびに、serverはclientごとにthreadを開き、このone-thread-per-clientのモードはserverにとって圧力が大きい.スレッドプールの技術を用いてスレッド個数を制限しても,このblocking−IOのモデルは大量の接続をサポートできない.
    上記one-thread-per-clientのモードでは、大量の接続をサポートできない主な原因は、readLineがIOをブロックすることにある.すなわち、readLineがデータを読み取ることができなかったとき、スレッドがブロックされ続け、スレッドが実行できないため、serverは複数のclientを同時に処理できるように、複数のスレッドを同時に開くしかない.
    ノンブロッキングIO
    上記ブロックIOの短所について、データの準備が完了するまでスレッドがブロックされない場合は、クライアントごとにスレッドを作成する必要はありません.
    Java 1.4はその後、NIOインタフェースを導入した.NIOの最も主要な機能は非ブロックIO動作を行うことである.データが読み取れなかった場合、非ブロックIOはスレッドをブロックせず、直接0を返す.この場合,スレッドの戻り値に基づいてデータの準備ができているか否かを判断することができ,ブロックされることなく他のことを処理することができる.
    では、問題が来ました.私たちはどうしてデータの準備ができているかどうかを知っていますか.最も直接的な方法はポーリングで、一定の時間ごとにデータの準備ができているかどうかをチェックします.
    非ブロックIOはブロックされませんが、関数を呼び出してデータが読めるかどうかを確認します.この現象はコードでこのような形で表示されます.
    while((str = read()) == 0) {
    
    }
    //             。

    非ブロックIOはスレッドをブロックしないが,データ読み取り可能ではないため,スレッドは次の論理を実行し続けることができず,絶えず判断を呼び出し,データの到来を待つしかないことがわかる.この場合を同期IOと呼ぶ.従って、NIOは本質的に非ブロッキング同期IOである.
    IO多重化
    NIOは、データが到着していないためにブロックされることはないので、クライアントごとにthreadを割り当てて、データが読めるかどうかを継続的にポーリングする必要はありません.1つのthreadを使用してすべてのclient接続を監視し、このthreadループによってclientのデータが読めるかどうかを判断し、ある場合は他のthreadのclient接続がデータで読めることを通知することができます.このような行為をIO多重と呼ぶ.すべてのクライアント接続にデータ読み取り可能かどうかを監視するために、NIOにSelectorクラスが用意されています.
    Selectorを使用してIO多重化を実現するには、1つのthreadだけがデータが来るかどうかに関心を持ち、他のスレッドが通知を待つ必要がある.これにより,リスニングスレッドだけがループして判断し,CPUリソースを多く占めることはない.NIOのSelectorといえば、LinuxプログラミングにおけるIO多重化を言わざるを得ない.NIOのSelectorの下層はシステムレベルのIO多重化スキームを使用するからだ.
    LinuxシステムのIO多重実装スキームは2種類ある:selectとepoll.Linux 2.6+のバージョンではNIOの下位層にepollが使用され、2.4である.xのバージョンではselect関数が使用されています.epoll関数はselectよりも性能の面でずっとよく、ここではLinuxプログラミングの具体的な詳細に関心を持たないことができます.特筆すべきは、Javaのnettyネットワークフレームワークの底層がNIO技術を使用していることだ.
    信号駆動式IO
    信号駆動式IOとは、プロセスが予めカーネルに通知し、ある記述子でイベントを送信すると、カーネルが信号を使用して関連プロセスに通知することを意味する.信号駆動式IOは、プロセスに通知された後も、プロセスによってIO動作が完了するため、真の非同期を実現していない.
    非同期IO
    上記IO多重化モデルでは、データの準備が完了すると他のスレッドにデータの読み取り開始を通知し、データの読み取り中にスレッドは他のタスクを処理できない、すなわちIO多重化は実は同期IOである.
    JDK 7には新しいインタフェースAIO(Asynchronous IO)が追加された.AIOの独特な点は,IO操作が開始されると,スレッドがIO読み取りが完了するのを待つのではなく,直接戻って他の操作を継続できることである.データの読み取りが完了すると、スレッドデータの読み取りが完了したことが通知されます.このようにIO動作を開始するが、データの読み出しを待つ必要がないIO動作を非同期IOと呼ぶ.AIOを使用する場合、1つのスレッドが複数のIOオペレーションを同時に開始することができ、これは、1つのスレッドが複数のリクエストを同時に処理できることを意味する.有名なwebサーバNginxは非同期IOを使っています.
    まとめ
    これまで,同期とブロックの概念と5種類のネットワークIOモデルを紹介し,これらのIOモデルの概念がコードの作成に大きく役立つことを理解した.