【Netty】サービス側とクライアント


公衆番号へようこそ:【
プログラミングが好きです
バックグラウンドの返信が必要な場合
2019贈呈
1 Tの学习资料よ!!
本稿はNetty 4に基づく.1.36分析を行う
サービス側
Nettyサービス側の起動コードは、基本的に次のとおりです.
private void start() throws Exception {

        final EchoServerHandler serverHandler = new EchoServerHandler();
        /**
         * NioEventLoop        I/O  ,     I/O     
         *      NioEventLoopGroup,
         *           Reactor   。
         *           TCP  ,
         *        I/O       ,      Task、    Task 。
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();

        try {
            //ServerBootstrap     netty   ,         socket  
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
//                                  read/write   Channel        ChannelHandler
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });

            ChannelFuture f = b.bind().sync();

            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully().sync();
            childGroup.shutdownGracefully().sync();
        }
    }

上図のコードから以下の手順にまとめることができます.
1、ServerBootStrapインスタンスの作成2、Reactorスレッドプールの設定とバインド:EventLoopGroup、EventLoopは、本スレッドに登録されているSelectorの上に登録されているすべてのChannel 3、サービス側を設定してバインドするchannel 4、5、ネットワークイベントを処理するChannelPipelineとhandlerを処理し、ネットワーク時間はストリームの形で流れ、handlerは、SSlセキュリティ認証6の符号化、傍受ポート7のバインドおよび起動、準備完了のchannelへのローテーション後、Reactorスレッド:NioEventLoopによってpipline内のメソッドを実行し、最終的にchannelHandlerをスケジューリングおよび実行するなど、多くの機能カスタマイズを完了する.
サービス側作成シーケンス図
ServerBootStrapブートサービス側
これは、サービス・エンドの起動を主に誘導するもので、次のような作業が含まれます.
  • 1.サービス側チャネル
  • の作成
  • 2.初期化サービス側チャネル
  • 3.チャネルをselector
  • に登録する
  • 4.ポートバインド
  • 1.サービス側チャンネルの作成
    プロセス:まずユーザコードのbind()からAbstractBootstrapである.bind()は、次いで、反射ファクトリを介してb.channel(NioServerSocketChannel.class)を介してユーザが入力したNioServerSocketChannelを、下位層のjdkのSelectorProviderを呼び出してchannelを作成するとともに、対応するChannelPipelineを作成する.詳細は下図を参考にして、自分でソースコードを見てみましょう.
    2.サービス側チャネルの初期化
    主な仕事は以下の通りです.
    1)設定したoptionをNioServerSocketChannelConfigにキャッシュする2)設定したattrをchannelに設定する3)設定したchildOptionsを保存し、構成したchildAttrsをServerBootstrapAcceptorに4)NioSocketChannelのpipelineにServerBootstrapAcceptorを追加する
    主なコアソースは以下の通りです.
     @Override
        void init(Channel channel) throws Exception {
            final Map, Object> options = options0();
            synchronized (options) {
                setChannelOptions(channel, options, logger);
            }
    
            final Map, Object> attrs = attrs0();
            synchronized (attrs) {
                for (Entry, Object> e: attrs.entrySet()) {
                    @SuppressWarnings("unchecked")
                    AttributeKey key = (AttributeKey) e.getKey();
                    channel.attr(key).set(e.getValue());
                }
            }
    
            ChannelPipeline p = channel.pipeline();
    
            final EventLoopGroup currentChildGroup = childGroup;
            final ChannelHandler currentChildHandler = childHandler;
            final Entry, Object>[] currentChildOptions;
            final Entry, Object>[] currentChildAttrs;
            synchronized (childOptions) {
                currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
            }
            synchronized (childAttrs) {
                currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
            }
    
            p.addLast(new ChannelInitializer() {
                @Override
                public void initChannel(final Channel ch) throws Exception {
                    final ChannelPipeline pipeline = ch.pipeline();
                    ChannelHandler handler = config.handler();
                    if (handler != null) {
                        pipeline.addLast(handler);
                    }
    
                    ch.eventLoop().execute(new Runnable() {
                        @Override
                        public void run() {
                            pipeline.addLast(new ServerBootstrapAcceptor(
                                    ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                        }
                    });
                }
            });
        }

    まとめ:全体的に上記のワークフローで説明したように.特にお勧めします:ServerBootstrapAcceptorソースコードを表示すると、ServerBootstrapAcceptorはchannelReadイベントがトリガーされたとき(クライアント接続があるとき)、childHandlerをchildChannel Pipelineの末尾に追加し、childHandlerのoptionsとattrsを設定し、最後にchildHandlerをchildGroupに登録します
    3.チャンネルをselectorに登録する
    登録プロセスは以下の図
    小結:チャネル登録プロセスでは、チャネルを対応するEventLoopに関連付ける作業を行います.
    1).各チャネルは特定のEventLoopに関連付けられ、このチャネル内のすべてのIO操作はこのEventLoopで実行される.
    2).チャネルとEventLoopを関連付けると、下位層のJava NIO SocketChannelのregisterメソッドが呼び出され続け、下位層のJava NIO SocketChannelが指定されたselectorに登録する.
    この2つのステップでNetty Channelの登録プロセスが完了する.
    4.ポートバインド
    ポートバインドのソースコードの流れは基本的に以下の図で、詳細は自分でソースコードを読んだほうがいいです.
    小結:実はnettyポートバインドはjdkを呼び出すjavaChannel()である.bind(localAddress, config.getBacklog());バインディングを行い、TCPリンクの確立に成功し、Channelはイベントをアクティブにし、channelPipelineを介して伝播する.
    クライアント
    クライアント起動の一般的なコードは次のとおりです.
      private void start() throws Exception {
    
            /**
             * Netty                 。
             * (1)     TCP  ,   Channel  ;
             * (2)            ChannelPipeline
             */
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap b = new Bootstrap();
                b.group(group)
                        .channel(NioSocketChannel.class)
                        .remoteAddress(new InetSocketAddress(host,port))
                        .handler(new ChannelInitializer() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                socketChannel.pipeline().addLast(new EchoClientHandler());
                            }
                        });
                //    
                ChannelFuture f = b.connect().sync();
    
                f.channel().closeFuture().sync();
            } catch (Exception e) {
                group.shutdownGracefully().sync();
            }
    
    
        }

    プロセス:
    1.ユーザスレッドはBootstrapインスタンスを作成し、API設定によってクライアント関連パラメータを作成し、非同期にクライアント接続を開始する.2.クライアント接続、I/O読み書きを処理するReactorスレッドグループNioEventLoopGroupを作成し、デフォルトではCPUコア数の2倍です.3.JDK NIOクラスライブラリが提供するSocketChannel 4と同様の機能を有する、BootstrapのChannelFactoryおよびユーザが指定するChannelタイプによりクライアントNioSocketChannelを作成する.ネットワークイベントをスケジュールおよび実行するためのデフォルトのChannel Handler Pipelineを作成します.5.非同期でTCP接続を開始し、接続が成功したかどうかを判断する.成功した場合、NioSocketChannelを直接マルチプレクサに登録し、データパケットの読み取りとメッセージ送信のために読み取り操作ビットをリスニングし、すぐに接続に成功しなかった場合、登録接続はマルチプレクサにリスニングされ、接続結果を待つ.6.対応するネットワーク傍受状態をマルチプレクサに登録する.7.マルチプレクサによりI/Oフィールドでチャンネルをポーリングし、接続結果を処理する.8.接続が成功した場合、Future結果を設定し、接続成功イベントを送信し、ChannelPipeline実行をトリガーします.9.実行システムおよびユーザのChannelHandlerは、ChannelPipelineによってスケジューリングされ、論理を実行する.
    ソースコールの流れは次の図のようになります.
    小結:クライアントはどのようにTCP接続を開始しますか?
    次の図を示します.
    特に注意:AbstractChannelHandlerContext.connect()#findContextOutboundこのステップの操作は、戻る結果nextが実はヘッダノードである、つまり次のステップnextである.invokeConnect()ここのnextはヘッダノードなので最終的にHeadContextを呼び出す.connect()
    まとめ
    本文は主にnettyサービス側とクライアントの簡単なワークフローについて述べる.具体的なサービス側とクライアントがどのように通信するか、メモリ管理などの知識は次回に書きます.
    最後に
    Java、ビッグデータに興味があればQRコードを長く押して注目してください.私はあなたたちに価値をもたらすように努力します.あなたに少しでも役に立つと思う人は、「いいね」や「転送」を手伝ってください.公式アカウント「愛コード」に注目し、2019に返信するには関連資料がありますよ.