オペレーティングシステムレベルからのLinuxのネットワークIOモデルの理解

IファイルI / Oを含む/ O(入力出力)、ネットワークI / O. スピード軽蔑のコンピュータの世界:

  • ナノ秒:メモリは、データを読み取ります。

  • ギガビットイーサネットのデータが読み:微妙なレベル。1000のNS = 1マイクロ秒、低速なメモリカードよりも千倍。

  • (ミリ秒):ディスクは、データを読み取ります。1ミリ秒=10万NS、ハードディスクメモリよりも遅い10倍。

  • 上下CPU 1ナノ秒のクロックサイクルは、比較的近いCPUのメモリにある、他の缶はいきません。

CPU処理速度は、データ作成のI / O速度データよりもはるかに大きいです。

任意のプログラミング言語は、このCPUの処理速度およびIで問題が発生します/ O速度が一致していません!

どのようにネットワークへのネットワークI / Oの最適化プログラミング:どのように効率的なネットワーク・データ処理のためのCPUの使用?

関連概念

オペレーティングシステムレベルからのI / Oにそれをネットワークを理解するには?世界には、コンピュータの概念の独自の定義を設定します。あなたはこれらの概念を理解していない場合、私たちは本当に技術とデザインのアイデアの本質を理解することはできません。だから私の意見では、これらの概念は、技術やコンピュータの世界を理解するための基礎となります。

1.1同期および非同期、ブロッキングおよび非ブロッキング

同期および非同期、ブロックと非ブロック:理解のネットワークI / Oは、話題を避けられません。(温水システムのカーネル・コールと同様に、ユーザーの行動サンジのプログラムのように)、例えばサンジ沸騰の水を取り、これら二つのグループの概念の国立成功大学方言の翻訳を理解することができます。

  • 同期/非同期の沸騰に水の後かに対処する私を必要とすることを懸念。
  • ブロッキング/非ブロッキングは、この期間中の水の沸騰は、他のことを行っていないことを懸念しています。

1.1.1同期ブロック

水は何も熱遮断(ブロッキング)、オープンウォーター(同期)をやったことはありません決定したまでは点火後、Shadengは、待っていません。

1.1.2ノンブロッキング同期

水を参照するには、時間までの時間から点火、時計TV(非ブロッキング)した後の水の後熱(同期)をオフにし、オープンされていません。

1.1.3非同期ブロック

スイッチが押され、水後の水Shadeng開口(ブロッキング)、オートパワーオフ(非同期)。

存在しないネットワークプログラミングモデル。

1.1.4非同期ノンブロッキング

スイッチが押され、なぜ理由(非ブロッキング)、オートパワーオフ(非同期)水ました。

1.2カーネル空間とユーザ空間

  • カーネルは、ネットワークとファイルの読み取りおよび書き込みデータを担当しています。
  • ユーザーはネットワークやファイルシステムによって得られたデータを呼び出します。

1.2.1ユーザーモードカーネルモード

  • データは、システムコールが起こる持って読み書きするためのプログラム。
  • カーネルモードへのユーザモードからシステムコールインタフェーススレッドスイッチを介して、カーネルは読み込み、書き込みデータは、その後、スイッチバック。
  • プロセスまたはスレッドの異なる空間状態。

1.2.2スイッチングスレッド

時間のかかるユーザーモードとカーネルモードの切り替え、料金のリソース(メモリ、CPU)最適化の提案:

  • 少ないスイッチ。
  • 共有スペース。

1.3ソケット - ソケット

  • ソケットを使用すると、プログラミングをネットワーク化することができます。
  • 接続を確立するためのシステムコールのソケットを介してアプリケーション()、データを送受信する(I / O)。
  • ソケットは、非同期呼び出しにノンブロッキング、ノン・ブロッキング、アプリケーションへの呼び出しを非同期サポート、アプリケーションをサポートしています

1.4ハンドルファイルディスクリプタ-FD

**ネットワークプログラミングの必要性は、FDを知っていますか?FDは何地獄ですか?** Linuxの場合:すべてがファイルで、FDは参考資料です。すべてのように見えるがJAVA?プログラム動作が参照されているオブジェクト内のオブジェクトです。JAVA限られたメモリ内に作成されたオブジェクトの数は、FDの同じ数も限られています。

Linuxのファイルやネットワーク接続を扱う、あなたはFDを開閉する必要があります。各プロセスは、デフォルトFDを持つことになります。

  • 0標準入力STDIN
  • 標準出力に1
  • 2エラー出力標準エラー出力

リクエストを処理して1.5ネットワーク・サーバ

  • 接続を確立した後。
  • (CPUのアイドル)準備ができてデータを待ちます。
  • プロセス(CPUのアイドル)にカーネルからデータをコピーします。

**どのようにそれを最適化するには?** 1つのI / Oアクセス(例は読むこと)の場合、データは、第一のバッファカーネルオペレーティング・システムにコピーされ、オペレーティング・システムのカーネルは、アプリケーションのアドレス空間にバッファからコピーされます。読み出し動作が発生したときにそう、それは二つの段階を通過します。

  • データの準備(データが準備されるのを待つ)を待ちます。
  • (プロセスにカーネルからデータをコピーする)処理をカーネルからデータをコピーします。

これは、Linuxシステムには、次の3つのネットワークモードの溶液中での繰り返し表示され、アップグレード、なぜならこれら二つのフェーズです。

I / Oモデル

2.1阻塞I / O - I / Oをブロック

説明:ほとんど元のネットワークI / Oモデル。データのコピーが完了するまでのプロセスをブロックします。短所:高い同時実行、サービスおよびクライアント・ピア接続、マルチスレッドによって引き起こされる問題:

  • CPUリソース、コンテキストスイッチ。
  • 幾何メモリコストは、約1MB JVMスレッドのコストを上昇します。
public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket();
        ss.bind(new InetSocketAddress(Constant.HOST, Constant.PORT));
        int idx =0;
        while (true) {
            final Socket socket = ss.accept();//阻塞方法
            new Thread(() -> {
                handle(socket);
            },"线程["+idx+"]" ).start();
        }
    }

    static void handle(Socket socket) {
        byte[] bytes = new byte[1024];
        try {
            String serverMsg = "  server sss[ 线程:"+ Thread.currentThread().getName() +"]";
            socket.getOutputStream().write(serverMsg.getBytes());//阻塞方法
            socket.getOutputStream().flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
复制代码

2.2 I / Oを非ブロック - 非はIOをブロック

はじめに:プロセスは、システムコールを繰り返し、すぐに結果を返しています。欠点は:プロセスが1000fdsを持っている場合、ポーリングは、ユーザー・プロセス1000カーネルのシステムコール、ユーザーモードとカーネルモードを前後に切り替え、コストの幾何学的な増加に代わって行われます。

public static void main(String[] args) throws IOException {
        ServerSocketChannel ss = ServerSocketChannel.open();
        ss.bind(new InetSocketAddress(Constant.HOST, Constant.PORT));
        System.out.println(" NIO server started ... ");
        ss.configureBlocking(false);
        int idx =0;
        while (true) {
            final SocketChannel socket = ss.accept();//阻塞方法
            new Thread(() -> {
                handle(socket);
            },"线程["+idx+"]" ).start();
        }
    }
    static void handle(SocketChannel socket) {
        try {
            socket.configureBlocking(false);
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            socket.read(byteBuffer);
            byteBuffer.flip();
            System.out.println("请求:" + new String(byteBuffer.array()));
            String resp = "服务器响应";
            byteBuffer.get(resp.getBytes());
            socket.write(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
复制代码

2.3 I / Oマルチプレクサ - IO多重化

説明:単一のスレッドが同時に複数のネットワーク接続を処理することができます。データが到着すると、ユーザプロセスに通知する場合、カーネルは、ポーリングを担当するすべてのソケット、ソケットです。ターンサポートにおけるマルチプレクサのLinuxカーネルのコード反復プロセスでは3つのコール、すなわちSELECT、POLL、ファイルディスクリプタ3種類のネットワークI / Oモデルを多重化します。Javaコードは、以下の図面と併せて説明します。

2.3.1 I / Oマルチプレクサ - を選択

説明:接続要求は、再検査工程に到着しました。短所:

  • ハンドルキャップ - デフォルトFDによってオープンは、1024限ります。
  • 初期化を繰り返して - 各select呼び出しを()、ユーザーからトラバースする必要がカーネルモード、カーネルに設定されたコピーモードをfdが。
  • すべてのFDの状態の一社の調査による一つは効率的ではありません。

チャネルを構築し、一つでもジャック接続のクライアント側で、屋根付きのソケットに挿入された行のようなサービス側を選択し、チャネル・レジスタの読み取りと書き込みのイベントをオンにします。準備ができて、あなたは、読み取りや書き込みのイベントハンドラ、または次の缶ハンドルを削除するために覚えておく必要があります。

public static void main(String[] args) throws IOException {
        ServerSocketChannel ssc = ServerSocketChannel.open();//管道型ServerSocket
        ssc.socket().bind(new InetSocketAddress(Constant.HOST, Constant.PORT));
        ssc.configureBlocking(false);//设置非阻塞
        System.out.println(" NIO single server started, listening on :" + ssc.getLocalAddress());
        Selector selector = Selector.open();
        ssc.register(selector, SelectionKey.OP_ACCEPT);//在建立好的管道上,注册关心的事件 就绪
        while(true) {
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> it = keys.iterator();
            while(it.hasNext()) {
                SelectionKey key = it.next();
                it.remove();//处理的事件,必须删除
                handle(key);
            }
        }
    }
    private static void handle(SelectionKey key) throws IOException {
        if(key.isAcceptable()) {
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                SocketChannel sc = ssc.accept();
                sc.configureBlocking(false);//设置非阻塞
                sc.register(key.selector(), SelectionKey.OP_READ );//在建立好的管道上,注册关心的事件 可读
        } else if (key.isReadable()) { //flip
            SocketChannel sc = null;
                sc = (SocketChannel)key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(512);
                buffer.clear();
                int len = sc.read(buffer);
                if(len != -1) {
                    System.out.println("[" +Thread.currentThread().getName()+"] recv :"+ new String(buffer.array(), 0, len));
                }
                ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes());
                sc.write(bufferToWrite);
        }
    }
复制代码

2.3.2 I / Oマルチプレクサ - 世論調査

説明:効率性を提供するために、新しいデータ構造(リンクリスト)を設計。世論調査とほとんど変化と比較して、本質的には、選択したが、世論調査では、ファイル記述子の最大数の制限を選択する方法はありません。デメリット:すべてのFDの状態の一社の調査による1は効率的ではありません。

2.3.3 I / Oマルチプレクサ - epollを

説明:数fdと、トリガーにイベント通知メカニズムを使用して、一回だけコピーするカーネルモードの必要性へのユーザーモードに制限はありません。コールバック機構は、I / O動作に関連する対応するコールバックFDによって活性化されたら、FDによって登録Epoll_ctlは、fdは準備ができています。短所:

  • クロスプラットフォーム、Linuxサポートは最高です。
  • 基礎となる実装の複雑さ。
  • 同期。
 public static void main(String[] args) throws Exception {
        final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open()
                .bind(new InetSocketAddress(Constant.HOST, Constant.PORT));
        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(final AsynchronousSocketChannel client, Object attachment) {
                serverChannel.accept(null, this);
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer attachment) {
                        attachment.flip();
                        client.write(ByteBuffer.wrap("HelloClient".getBytes()));//业务逻辑
                    }
                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        System.out.println(exc.getMessage());//失败处理
                    }
                });
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();//失败处理
            }
        });
        while (true) {
            //不while true main方法一瞬间结束
        }
    }
复制代码

もちろん、上記の欠点は、それは無視することができます利点と比較します。JDKは、非同期的に提供していますが、実際の基礎となるLinux環境やファイルディスクリプタではなく、複数のサイクルではなく、真の非同期ノンブロッキング。そして、コードの呼び出し上の画像のように、ハンドルのネットワーク接続はデカップリングするコードとビジネス・コードは良い十分ではなかったです。ネッティーは簡潔で、デカップリング、明確に構造化APIを提供します。

 public static void main(String[] args) {
        new NettyServer().serverStart();
        System.out.println("Netty server started !");
    }

    public void serverStart() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new Handler());
                    }
                });
        try {
            ChannelFuture f = b.localAddress(Constant.HOST, Constant.PORT).bind().sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

class Handler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        ctx.writeAndFlush(msg);
        ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
复制代码

bossGroup処理ネットワーク要求の家政婦()ネットワーク接続の準備ができると、仕事への労働者のワークグループ与える(誰が)。

概要

レビュー

  • 非同期/同期接続が確立され、ユーザプログラムが読み書きに、エンドユーザ手順が依然としてデータを読み出す)(システムリードを呼び出す必要がある場合、その逆、同期非同期副です。真の非同期を達成するためのWindowsは、カーネルのコードは非常に複雑ですが、ユーザプログラムは透明です。

  • 読み取りおよび書き込みを待っている間に、ノンブロッキング、接続が確立される/ブロッキング、ユーザプログラム、他の何かをやっていません。それは、非ブロックブロックし、その逆もされている場合。ほとんどのオペレーティングシステムがサポートされています。

なぜRedisの、Nginxは、ネッティー、Node.jsのように甘いですか?

これらの技術は、反復がネットワーク要求の効率的な処理を提供するLinuxカーネルのシステムコールを伴うと表示されています。理由を知り、私はこれらを知って、I / Oのより深い理解に基本的なコンピュータの知識を理解します。そして、相互の励ましの王様!

おすすめ

転載: juejin.im/post/5e020b53e51d455824271f53