blog

Nettyを知る

メンテナンス可能な高性能プロトコルサーバとクライアントの迅速な開発のために。 Nettyは最も人気のあるNIOフレームワークで、何百もの商用およびビジネスプロジェクトで検証されており、Dubboなど多...

Dec 14, 2020 · 6 min. read
シェア

Netty

Netty は、NIO 実装に基づいた非同期イベント駆動型のウェブアプリケーションフレームワークで、保守性の高い高性能なプロトコルサーバとクライアントを迅速に開発することができます。

Nettyは最も人気のあるNIOフレームワークで、何百もの商用およびビジネスプロジェクトで検証されており、DubboやElasticsearchなど、多くのフレームワークやオープンソースコンポーネントがNettyを基礎RPCとして使用しています。公式サイトよりNettyの特徴をご紹介します:

デザイン
  • すべてのトランスポートタイプ(ブロッキングおよびノンブロッキングソケット)の統一API
  • 柔軟で拡張可能なイベントモデルに基づく、明確な懸念事項の分離
  • 高度にカスタマイズ可能なスレッドモデル - シングルスレッド、1つまたは複数のスレッドプール、SEDAなど
  • 真のコネクションレス型データグラムソケットのサポート
使いやすい
  • 十分に文書化されたJavadoc、ユーザーガイド、サンプル
  • JDK 5 または 6 で十分です。
パフォーマンス
  • より高いスループット、より低いレイテンシ
  • 資源消費の削減
  • 不要なメモリの重複を削減
セキュア
  • SSL / TLSおよびStartTLSのフルサポート

これらの特徴を簡単に説明するだけでも十分です。

シンプルなHTTPサーバーの構築

私の方で使用しているネッティのバージョンは

<dependency>
 <groupId>io.netty</groupId>
 <artifactId>netty-all</artifactId>
 <version>4.1.48.Final</version>
</dependency>

ブラウザでアクセス先のアドレスを入力してアクセスされたサービスにレスポンスを返す、シンプルなHTTPサーバーを書いてください。

サーバー側のコードは以下の通りです。

@Slf4j
public class Main {
 public static void main(String[] args) {
 log.info("=========<<<<<<<Nettyを起動する>>>>>>=========");
 //つのスレッドグループを構築する。bossGroupは接続を処理し、workGroupはリクエストを処理する。
 NioEventLoopGroup bossGroup = new NioEventLoopGroup();
 NioEventLoopGroup workGroup = new NioEventLoopGroup();
 try {
 //サーバはヘルパークラスを起動する
 ServerBootstrap b = new ServerBootstrap();
 b.group(bossGroup, workGroup)
 .channel(NioServerSocketChannel.class)
 	//Channelリクエストを処理するために、ChannelHandlerをセットアップする。.
 .childHandler(new ChannelInitializer<SocketChannel>() {
 @Override
 protected void initChannel(SocketChannel socketChannel) throws Exception {
 //Httpメッセージのエンコーディングを扱う
 socketChannel.pipeline().addLast("httpServerCodec", new HttpServerCodec());
 //カスタムChannelHandlerはFullRequestを使うので
 socketChannel.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
 //カスタムChannelHandlerを追加する
 socketChannel.pipeline().addLast("httpServerHandler", new NettyServerHandler());
 }
 });
 ChannelFuture sync = b.bind(8888).sync();
 log.info("NettyServer開始された:.1:8888");
 //サービスのポートが閉じるのを待つ
 sync.channel().closeFuture().addListener(new ChannelFutureListener() {
 @Override
 public void operationComplete(ChannelFuture channelFuture) throws Exception {
 //潔く終了し、スレッドプールを解放する
 bossGroup.shutdownGracefully();
 workGroup.shutdownGracefully();
 log.info("nettyを終了する");
 }
 });
 } catch (Exception e) {
 log.error("NettyServer起動に失敗した", e);
 }
 }
}

Netty アプリケーションを書く場合、一般的に NioEventLoopGroup bossGroup, workGroup のインスタンスを 2 つ作成します。 まず、この 2 つのインスタンスについて簡単に説明すると、これは実際には 2 つのスレッドプールで、スレッド数はデフォルトで CPU コア数 * 2 になります。bossGroup は接続の処理に使われ、workGroup はリクエストの処理に使われます。

ServerBootstrapは、上で作成した2つのスレッドグループなど、Nettyアプリケーションのスタートアップアセンブリに必要なコンポーネントの一部を設定するために使用されます。 channelメソッドは、サーバー側のリスニングソケットチャンネルNioServerSocketChannelを指定するために使用されます。

.childHandler() メソッドは主に Channel リクエストを処理するための ChannelHandler をセットアップするために使用され、簡単に言えば、Netty サービスへのリクエストが channelhandler の内部で実行されると理解できます。

次に bind メソッドでサービスを 8888 番ポートにバインドしています。 bind メソッドは内部的にポートバインディングなどの一連の動作を行うので、それまでの設定はすべて整っており、ポートバインディングの操作が完了するまで sync メソッドで現在の Thread をブロックしています。次の文では、アプリケーションはサーバーのチャネルがクローズされるまでブロックして待機します。

メインスレッドがブロックせずに解放されるように、cf.channel().closeFuture().addListenerにshutdownGracefully関数を記述します。そして、パイプラインが閉じられるとき、それは自動的に優雅に閉じられます。

new ChannelInitializer<SocketChannel>() {
 @Override
 protected void initChannel(SocketChannel socketChannel) throws Exception {
 //Httpメッセージのエンコーディングを扱う
 socketChannel.pipeline().addLast("httpServerCodec", new HttpServerCodec());
 //カスタムChannelHandlerはFullRequestを使うので
 socketChannel.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
 //カスタムChannelHandlerを追加する
 socketChannel.pipeline().addLast("httpServerHandler", new NettyServerHandler());
 } 
}

このコードは主に、その後の処理に必要なコーデックと、独自のビジネスを実装するためのカスタムチャンネルハンドラーを追加するためのものです。

Nettyは高性能なネットワーク通信フレームワークですが、低レベルのフレームワークでもあります。NettyにHttpをサポートさせるには、適切なコーデックを提供する必要があります。

Netty独自のHttpコーデックコンポーネントであるHttpServerCodecは、Httpリクエストを扱うときによく使われるHttpRequestDecoderとHttpResponseEncoderを組み合わせたものなので、Nettyはこれらを直接組み合わせて使いやすくしています。Nettyはこれらを直接組み合わせて使いやすくしています。

HttpObjectAggregator はリクエストを FullHttpRequests にマージする機能を提供します。

NettyServerHandler は、自分で実装する必要があるクラスです。

コードは以下の通り:

/**
 * 
 * 
 * @Version 1.0
 * @Description Netty Httpリクエストの設計は、HttpRequestとHttpContentの2つの部分に分かれている。
 * HttpRequest 主にリクエストヘッダ、リクエストメソッド、その他の情報を含む
 * HttpContent リクエストの本文は以下の情報を含む
 * <p>
 * Netty 別のクラスFullHttpRequestが提供されている。 FullHttpRequestはリクエストされたすべての情報を含んでいる。
 * これはHttpRequestとHttpContentを直接または間接的に継承したインターフェースで、その実装クラスがDefalutFullHttpRequestである。
 * つまり、ここではFullHttpRequestを使うことができる。
 * FullHttpRequestを使っている場合は、ChannelPipelineにHttpObjectAggregatorのインスタンスを追加する必要があることに注意。
 * HttpObjectAggregator はリクエストを一つのFullHttpRequestに変換する。
 */
@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
 @Override
 protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
 log.info("リクエストの種類:" + request.method().name());
 log.info("uri:" + request.uri());
 ByteBuf buf = request.content();
 String requestContent = buf.toString(CharsetUtil.UTF_8);
 log.info("リクエストボディは:" + requestContent);
 //レスポンスを設定する
 ByteBuf byteBuf = Unpooled.copiedBuffer("hello world" + requestContent, CharsetUtil.UTF_8);
 FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf);
 //レスポンスヘッダを追加する
 response.headers().add(HttpHeaderNames.CONTENT_TYPE, "text/plain");
 response.headers().add(HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes());
 //クライアントに応答する
 ctx.writeAndFlush(response);
 }
}

ここまでで、簡単なHTTPサービスが書けました。では、mainメソッドを実行して、postmanでテストしてみましょう。

Read next

プロミスのJS実装

JS - PromisesA+ 仕様書.md\nコード\n// 中間プロミスの解析\nconst = =&gt; { // 中間プロミスのパース

Dec 13, 2020 · 5 min read

npm / yarn共通コマンド

Dec 13, 2020 · 2 min read

J14 2つの値を比較する

Dec 12, 2020 · 1 min read

JavaScriptにSymbol型がある理由

Dec 10, 2020 · 2 min read

DeFiとは?

Dec 10, 2020 · 1 min read