Netty実践(一):簡単な入門
前言
Nettyは現在世界で最も流行しているNIOフレームワークの一つとして、機能、性能、丈夫性の面で屈指であり、メッセージミドルウェアRocketMQ、分散型通信フレームワークDubboxなどの多くのプロジェクトで検証されている.Nettyの内部実装は複雑ですが、外部に提供されるAPIは非常に簡単で、私たちのネットワーク処理コードとビジネスロジック処理コードを簡単に分離し、ネットワークアプリケーションを迅速に開発します.
JAVA NIO、JAVA SOCKETをまだ知らない場合は、まずブロガーの以前のブログ「Java NIOの世界に入る」、「Java NIOサーバーとクライアントがファイルダウンロードを実現する」、「Java通信実戦:カスタム通信プロトコルを作成してFTPサービスを実現する」を参照してください.
コードインスタンスからNettyを分析する
サーバ側起動コード
Nettyサービス側が作成を開始するコード手順を大まかに示しました.次に、具体的に分析します.
元のJAVA SOCKET通信では、IO/NiOベースにかかわらず、実質的にサービス側は2つの作業を処理する必要がある:第一に、クライアントの接続要求を受け入れる;第二に、クライアントの要求を処理して通信する.上記のコードでは、Nettyはすでに2つのEventLoopGroupスレッドグループ(bossGroup/workerGroup)を抽象化してこの2つのタスクを完了しています.
サービス側が起動する前に、明らかにいくつかの構成(Channelのタイプ、サービス側が業務処理を行うHandler、いくつかのTCP/IPプロトコルの構成など)が必要であり、NettyはサーバBootstrap/Bootstrapを利用してそれぞれサーバ/Clientを構成することができる.
ポートをバインドし、サービスを開始し、返されるオブジェクトChannelFutureに注意します.Futureと呼ばれている以上、推測は非同期の動作です.チャンネルFutureによってチャンネルを取得することができ、チャンネル上でチャンネルの読み取り、書き込み、閉じるなどの操作を行うことに注意してください.
bind法により,複数のポートをバインドし,N個のClientsがサーバ側のM個のポートでデータ通信を行うことができる.
次に、サービス側が業務処理を行うHandlerについて見てみましょう.
まず、サービス側のHandlerがChannelHandler Adapterを継承していることを見ると、実際にはアダプタ設計モードが体現されています.ChannelHandlerAdapter implements ChannelHandlerは,我々のサービス側Handlerが直接implements ChannelHandlerであればoverrideが非常に多くのAPIを必要とし,まずChannelHandler Adapterを介して汎用的なChannelHandlerを実現し,その後サービス側Handlerに特定のAPIを複写させる.
チャネル上の特定のイベントが発生すると,特定のメソッドを呼び出して処理し,明瞭に見える.
Nettyがネットワーク通信を行うデータ型は、ByteBufなどのバッファデータ型である.以前NIOでByteBufferを利用して通信していたときは、positionの位置変化に注意する必要がありましたが、Nettyでは、これらに関心を持つ必要はありません.
Client/Server側にはバッファが存在するため、バッファのメッセージの解放とリフレッシュに注意する必要があります.読むならreleaseが必要で、書くならflush(flushの時にreleaseした)だけで相手に送信します.
NettyはNIOフレームワークであり、操作が非同期であるため、上のwriteAndFlush操作はFutureオブジェクトに戻り、操作が完了してチャネルを閉じるなど、このFuture上で傍受することができます.
クライアント起動コード
ここでは、クライアントとサービス・エンドの起動の違いについて説明します.
第一に、クライアントは1つのスレッドグループしか必要とせず、サービス側は2つ必要とする.
第二に、サービス起動補助クラス、クライアントはBootstrap、サービス側はServer Bootstrap
第三に、サービス側チャネル構成はNioServerSocketChannelであり、クライアントはNioSocketChannelである.
クライアント業務処理Handler
実行
ここまで、Nettyのシンプルさ、強さを実感しましたか?
次のブログで会いましょう.
Nettyは現在世界で最も流行しているNIOフレームワークの一つとして、機能、性能、丈夫性の面で屈指であり、メッセージミドルウェアRocketMQ、分散型通信フレームワークDubboxなどの多くのプロジェクトで検証されている.Nettyの内部実装は複雑ですが、外部に提供されるAPIは非常に簡単で、私たちのネットワーク処理コードとビジネスロジック処理コードを簡単に分離し、ネットワークアプリケーションを迅速に開発します.
JAVA NIO、JAVA SOCKETをまだ知らない場合は、まずブロガーの以前のブログ「Java NIOの世界に入る」、「Java NIOサーバーとクライアントがファイルダウンロードを実現する」、「Java通信実戦:カスタム通信プロトコルを作成してFTPサービスを実現する」を参照してください.
コードインスタンスからNettyを分析する
サーバ側起動コード
public class Main {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup(); // (2)
int port = 8867;
try {
ServerBootstrap b = new ServerBootstrap(); // (3)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (4)
.childHandler(new ChannelInitializer() { // (5)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (6)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (7)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (8)
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
System.out.println("start server....");
f.channel().closeFuture().sync();
System.out.println("stop server....");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("exit server....");
}
}
}
Nettyサービス側が作成を開始するコード手順を大まかに示しました.次に、具体的に分析します.
元のJAVA SOCKET通信では、IO/NiOベースにかかわらず、実質的にサービス側は2つの作業を処理する必要がある:第一に、クライアントの接続要求を受け入れる;第二に、クライアントの要求を処理して通信する.上記のコードでは、Nettyはすでに2つのEventLoopGroupスレッドグループ(bossGroup/workerGroup)を抽象化してこの2つのタスクを完了しています.
サービス側が起動する前に、明らかにいくつかの構成(Channelのタイプ、サービス側が業務処理を行うHandler、いくつかのTCP/IPプロトコルの構成など)が必要であり、NettyはサーバBootstrap/Bootstrapを利用してそれぞれサーバ/Clientを構成することができる.
ポートをバインドし、サービスを開始し、返されるオブジェクトChannelFutureに注意します.Futureと呼ばれている以上、推測は非同期の動作です.チャンネルFutureによってチャンネルを取得することができ、チャンネル上でチャンネルの読み取り、書き込み、閉じるなどの操作を行うことに注意してください.
bind法により,複数のポートをバインドし,N個のClientsがサーバ側のM個のポートでデータ通信を行うことができる.
次に、サービス側が業務処理を行うHandlerについて見てみましょう.
public class ServerHandler extends ChannelHandlerAdapter {
// ,
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
try {
// Do something with msg
System.out.println("server get :" + in.toString(CharsetUtil.UTF_8));
ChannelFuture channelFuture = ctx.writeAndFlush(Unpooled.copiedBuffer(("server send time: " + new Date()).getBytes()));
// ,
channelFuture.addListener(ChannelFutureListener.CLOSE);
} finally {
//ByteBuf , release()
//or ((ByteBuf)msg).release();
ReferenceCountUtil.release(msg);
}
}
//exceptionCaught() Throwable
// Netty IO
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
まず、サービス側のHandlerがChannelHandler Adapterを継承していることを見ると、実際にはアダプタ設計モードが体現されています.ChannelHandlerAdapter implements ChannelHandlerは,我々のサービス側Handlerが直接implements ChannelHandlerであればoverrideが非常に多くのAPIを必要とし,まずChannelHandler Adapterを介して汎用的なChannelHandlerを実現し,その後サービス側Handlerに特定のAPIを複写させる.
チャネル上の特定のイベントが発生すると,特定のメソッドを呼び出して処理し,明瞭に見える.
Nettyがネットワーク通信を行うデータ型は、ByteBufなどのバッファデータ型である.以前NIOでByteBufferを利用して通信していたときは、positionの位置変化に注意する必要がありましたが、Nettyでは、これらに関心を持つ必要はありません.
Client/Server側にはバッファが存在するため、バッファのメッセージの解放とリフレッシュに注意する必要があります.読むならreleaseが必要で、書くならflush(flushの時にreleaseした)だけで相手に送信します.
NettyはNIOフレームワークであり、操作が非同期であるため、上のwriteAndFlush操作はFutureオブジェクトに戻り、操作が完了してチャネルを閉じるなど、このFuture上で傍受することができます.
クライアント起動コード
public class Client {
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new ClientHandler());
}
});
// Start the client.
ChannelFuture f = b.connect("127.0.0.1", 8867).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
}
}
ここでは、クライアントとサービス・エンドの起動の違いについて説明します.
第一に、クライアントは1つのスレッドグループしか必要とせず、サービス側は2つ必要とする.
第二に、サービス起動補助クラス、クライアントはBootstrap、サービス側はServer Bootstrap
第三に、サービス側チャネル構成はNioServerSocketChannelであり、クライアントはNioSocketChannelである.
クライアント業務処理Handler
public class ClientHandler extends ChannelHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer(("client send hello ").getBytes()));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
try {
// Do something with msg
System.out.println("client get :" + in.toString(CharsetUtil.UTF_8));
ctx.close();
} finally {
//ByteBuf , release()
//or ((ByteBuf)msg).release();
ReferenceCountUtil.release(msg);
}
}
}
実行
ここまで、Nettyのシンプルさ、強さを実感しましたか?
次のブログで会いましょう.