Netty の謎を解く - Netty のクイックスタート

現在最も普及しているネットワーク通信フレームワークの1つとして、RPCやメッセージなどのミドルウェアを含めたnettyが利用されており、十分に検討する必要があります。nettyのクイックスタートから分析を始めましょう。

netty の起動にはサーバーの起動とクライアントの起動があり、主な入り口は ServerBootstrap と Bootstrap です。ServerBootstrap はサーバーを起動し、Bootstrap はクライアントを起動します。サーバーの起動はより複雑ですが、クライアントの起動は比較的簡単です。

TCPハンドシェイクプロセス

起動プロセスは実際にはネットワーク接続を確立するプロセスであるため、まず、TCP の 3 ウェイ ハンドシェイクと 4 ウェイ ハンドシェイクを確認する必要があります。

接続を確立するプロセス

 

切断のプロセス

1) 接続の確立後にクライアントに障害が発生した場合はどうすればよいですか?

サーバーに障害が発生すると、すべての接続が受動的に切断され、クライアントは定期的に接続を再試行できます。

クライアントに障害が発生した場合、接続数が増えるほどサーバーへの負荷が大きくなるため、サーバーは無駄な接続がリソースを占有することを許可しません。これには検出器 (ハートビート設計) が必要です。サーバーは定期的にハートビート パケットをクライアントに送信しますが、数回連続して応答がない場合、接続は無意味とみなされ、サーバーは積極的に接続を切断します。

サーバーの起動

サーバーの起動はServerBootstrapから始まりますが、このクラスは各種情報の設定に便利なフローモードで設計されており、以下のような簡単なコードを見てみましょう。

public static void main(String[] args) throws InterruptedException {
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        NioEventLoopGroup boss = new NioEventLoopGroup(2);
        NioEventLoopGroup worker = new NioEventLoopGroup(5);

        serverBootstrap.channel(NioServerSocketChannel.class)
                .group(boss, worker)
                .option(ChannelOption.SO_BACKLOG, 5)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new StringEncoder());

                    }
                });

        ChannelFuture future = serverBootstrap.bind(18080).sync();
        future.channel().closeFuture().addListener(new GenericFutureListener() {

            @Override
            public void operationComplete(Future future) throws Exception {
                boss.shutdownGracefully();
                worker.shutdownGracefully();
            }
        }).sync();


    }

サーバーを起動するプロセスで、コアは主に次のことを行います。

1) サーバーが作成するチャネルのタイプを決定するチャネル タイプを指定します。

2) サーバー上で動作するスレッドグループ (ボススレッドグループとワーカースレッドグループを含む) を指定します スレッドグループを指定した場合のみ、後続サーバーは正常に動作します

3)parentChannel と childChannel のネットワーク プロトコル構成を指定します。

4) チャネルに関連する channelHandler を指定します。これは、主に childChannel の Handler を設定するために、ネットワーク IO の読み取りおよび書き込みプロセスで使用される論理処理クラスです。

5) ポートをバインドします。これはサーバー起動の最後のステップであり、サーバーはポートをバインドした後、クライアントが接続を確立するのを待ちます。

ポートをバインドする主な目的は何ですか? ソース コードから、ポートをバインドすると主に次のことが行われることがわかります。

1)parentChannel を作成します --> ネットワーク プロトコルの設定、パイプラインの設定などを含むparentChannel の設定を初期化します。 -->parentChannel を登録し、parentChannel をボス スレッド グループ内のスレッドに登録し、セレクタに関連付けて、クライアントが接続するのを待ちます。それ以降、スレッドとセレクターは生涯にわたってparentChannelに従います。この利点は、スレッド コンテキストを切り替える必要がないため、サーバーのパフォーマンスが向上することです。

2) クライアントリクエストが来ると、parentChannelに関連付けられたパイプラインに対応するchannelHandlerプロセスが存在し、クライアントの接続リクエストを読み込んでchildChannelを生成すると同時に、childChannelに対して再度1の処理(チャネルの作成、チャネルの初期化、チャネルの登録)を行います。

要約すると、起動プロセス全体は実際にはチャネルを中心に行われ、主に次のとおりです。チャネルの作成 - チャネルの初期化 (channelPipeline、channelHandler の設定を含む。channelHandler を channelPipeline に追加すると、自動的に handleAdded イベントがトリガーされます) - チャネルの登録 - doBind() - channelActive。

 

クライアントの起動

クライアントの起動は比較的簡単なので、クライアントを起動するための簡単なコードも見てみましょう。

public static void main(String[] args) throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();

        NioEventLoopGroup worker = new NioEventLoopGroup(5);

        bootstrap.channel(NioSocketChannel.class)
                .group(worker)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new StringEncoder());

                    }
                })
                .remoteAddress("127.0.0.1", 18080);

        ChannelFuture future = bootstrap.connect().sync();
        future.channel().closeFuture().addListener(new GenericFutureListener() {

            @Override
            public void operationComplete(Future future) throws Exception {
                worker.shutdownGracefully();
            }
        }).sync();


    }

クライアントの起動プロセスには主に次のものが含まれていることがわかります。

1) チャネル タイプを指定します。これにより、クライアントが接続を確立するときに作成されるチャネルのタイプが決まります。

2) クライアント通信用のスレッドグループモデルを指定します。

3) 通信中のIOを処理するchannelHandlerを指定する

4) ChannelOption ネットワーク プロトコル構成を指定します

5) すべての準備が完了したら、サーバーへの接続を開始できます。接続が成功すると、通信を開始できます。

ここで注意すべき点は、クライアントであるかサーバーであるかに関係なく、チャネルの処理は同様であるということです。チャンネルの作成、チャンネルの初期化、チャンネルの登録を行います。

分解は次回

netty のスレッド グループの概念は何を意味しますか?

なぜボス リニア グループとワーカー スレッド グループがあるのですか? それらはどのように分けられているのでしょうか?

親チャンネルと子チャンネルの関係は何ですか? netty では何を表しているのでしょうか?

ChannelHandler の動作メカニズムは何ですか? netty はそれをどのように実装しますか?

ネットワーク プログラミングにおけるチャンネルとは正確には何ですか? チャネル、クライアント、サーバーの関係は何ですか?

最良の中の最良であると主張するネットワーク スレッド モデルはどのようなものでしょうか? nettyではどのように実装されていますか?

netty の随所で使用される有名なパイプライン モードはどのように実装および適用されますか?

 

おすすめ

転載: blog.csdn.net/qq_42672856/article/details/115682937