Nettyシリーズ第二章NIO 1.1擬似非同期I/O

34638 ワード

NIO入門
擬似非同期I/O
1.擬似非同期I/Oの誕生と定義まず擬似非同期I/Oは、BIOのクライアントがサービス側のスレッド処理を行う問題を解決するために誕生した.擬似非同期I/Oはスレッドプールとタスクキューによって実現され、どれだけのクライアントがあっても、スレッドプールは柔軟にスレッドリソースを配置することができ、スレッドの最大値を設定し、大量の同時アクセスによるスレッドの消耗を防止し、新しいクライアントアクセスがある場合、クライアントのsocketをTask(Runnableインタフェースを実装するタスク)にカプセル化してバックエンドのスレッドプールに送信します.
  • 擬似非同期I/O作成TimeServerソース
  • package com.carfi.netty.fakeio;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     1. @author: ll
     2. @time: 2020/6/14 23:15
     */
    public class TimeServer {
        public static void main(String[] args) throws IOException {
            int port = 8080;
            if (args != null && args.length > 0) {
                try {
                    port = Integer.valueOf(args[0]);
                } catch (NumberFormatException e) {
                }
            }
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(port);
                System.out.println("Thr rime server is start in port : " + port);
                Socket socket = null;
                //   
                TimeServerHandlerExecutePool timeServerHandlerExecutePool = new TimeServerHandlerExecutePool(50, 10000);
                while (true) {
                    socket = serverSocket.accept();
                    //              
                    timeServerHandlerExecutePool.execute(new TimtServerHandler(socket));
                }
            } finally {
                if (serverSocket != null) {
                    System.out.println("The time server close");
                    serverSocket.close();
                    serverSocket = null;
                }
            }
        }
    }
    

    ここで、クライアント処理コードはスレッドプールによって完了する、要求socketをtaskにカプセル化し、スレッドプールexecuteメソッドを呼び出して実行し、各要求に新しいスレッド3を作成することを回避する.擬似非同期I/Oによって作成されたTimeServerHandlerExecutePoolソース
    package com.carfi.netty.fakeio;
    
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     *                
     * @author: ll
     * @time: 2020/6/14 23:20
     */
    public class TimeServerHandlerExecutePool {
        private ExecutorService executorService;
    
        /**
         *
         * @param maxPollSize       
         * @param queueSize         
         */
        public TimeServerHandlerExecutePool(int maxPollSize, int queueSize) {
            /**
             * Runtime.getRuntime().availableProcessors()               
             * maxPollSize               
             * 120L       corePoolSize             
             * TimeUnit.SECONDS     
             * new ArrayBlockingQueue(queueSize)       ,      corePoolSize ,                ,            
             *          ,        >       (  workQueue  ),               
             */
            executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
                    maxPollSize, 120L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
        }
    
        public void execute(Runnable task) {
            executorService.execute(task);
        }
    
        public static void main(String[] args) {
            System.out.println(Runtime.getRuntime().availableProcessors());
        }
    
    }
    
  • 擬似非同期I/O作成TimtServerHandlerソース
  • package com.carfi.netty.fakeio;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.util.Date;
    
    /**
     * @author: ll
     * @time: 2020/6/11 22:56
     */
    public class TimtServerHandler implements Runnable {
    
        private Socket socket;
    
        public TimtServerHandler() {
        }
    
        public TimtServerHandler(Socket socket) {
            this.socket = socket;
        }
    
        @Override
        public void run() {
            BufferedReader in = null;
            PrintWriter out = null;
            try {
                in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
                out = new PrintWriter(this.socket.getOutputStream(), true);
                String currentTime = null;
                String body = null;
                while (true) {
                    body = in.readLine();
                    if (body == null) {
                        break;
                    }
                    System.out.println("The time server receive order : " + body);
                    currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
                    out.println(currentTime);
                }
            } catch (Exception e) {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    in = null;
                }
                if (out != null) {
                    out.close();
                    out = null;
                }
                if (this.socket != null) {
                    try {
                        this.socket.close();
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    this.socket = null;
                }
            }
        }
    }
    
    
  • 擬似非同期I/O作成TimeClientソース
  • package com.carfi.netty.fakeio;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    
    /**
     * @author: ll
     * @time: 2020/6/11 23:26
     */
    public class TimeClient {
        public static void main(String[] args) {
            int port = 8080;
            if (args != null && args.length > 0) {
                try {
                    port = Integer.valueOf(args[0]);
                } catch (NumberFormatException e) {
                    //     
                }
            }
            Socket socket = null;
            BufferedReader in = null;
            PrintWriter out = null;
            try {
                socket = new Socket("127.0.0.1", port);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);
                out.println("QUERY TIME ORDER");
                System.out.println("Send order 2 server succedd.");
                String resp = in.readLine();
                System.out.println("Now is : " + resp);
            } catch (IOException e) {
               //    
            } finally {
                if (out != null) {
                    out.close();
                    out = null;
                }
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    in = null;
                }
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    socket = null;
                }
            }
        }
    }
    
    

    擬似非同期I/Oの利点:
  • スレッドプールとメッセージキューは境界があるため、クライアントの同時接続数がどれだけ大きいかにかかわらず、スレッド個数の膨張やメモリオーバーフロー
  • を招くことはない.
    擬似非同期I/Oの欠点:
  • 擬似非同期下位層は依然として同期ブロックモデル
  • である.
  • Socket入力ストリームの読み取り時に、データが読めるまでブロックされ、利用可能なデータの読み取りが完了し、空のポインタ異常またはI/O異常が発生する
  • .
  • 相手方の送信要求又は応答メッセージが比較的遅い又はネットワーク遅延した場合、読み出し入力ストリームの乙方の通信スレッドも長時間にわたって閉塞され、データの送信が60 sで完了すると、読み出し側のスレッドも同期して60 s閉塞され、他のアクセスメッセージは閉塞キューに
  • しか並ぶことができない.
  • OutputStreamのwriteメソッドを呼び出して出力ストリームを書き込むと、送信するバイトがすべて書き込まれるまで、または異常が発生するまで、メッセージ受信者の処理が遅い場合、同期ブロックI/Oはwriteメソッドが無期限にブロックされる
  • を引き起こす.
    擬似非同期I/Oは、相手の応答時間が長すぎるため、カスケード障害を引き起こす可能性があります.
  • サービス側も応答時間
  • を延長する.
  • 擬似非同期I/Oを用いるスレッドは、読み出し入力ストリームがブロックするため、同期ブロック
  • によって障害サービスノードの応答を読み出す.
  • すべての利用可能なスレッドが障害サーバによってブロックされた場合、その後のすべてのI/Oメッセージは、
  • のキューに並ぶ.
  • スレッドプールがブロッキングキューを採用することにより、キューがいっぱいになると、後続のキューに入る動作が
  • ブロックされる.
  • 新しいクライアント要求は拒否され、タイムアウト接続
  • が発生する.
  • すべての接続がすべてタイムアウトした場合、システムがクラッシュしたとみなすことができ、新しい要求メッセージ
  • を受け入れることができない.