diff --git a/netty/netty-j.md b/netty/netty-j.md new file mode 100644 index 0000000..b7fdcc8 --- /dev/null +++ b/netty/netty-j.md @@ -0,0 +1,166 @@ +# netty +## netty解决的问题 +当前,http协议被广泛使用于client和server之间的通信。但是,部分场景下,http协议并不适用,例如传输大文件、e-mail消息、事实的经济或游戏数据。对于该类场景,http协议并无法很好的满足,此时需要一个高度优化的自定义协议实现,而通过netty可以及进行快速的协议实现。 + +netty提供了一个`异步的事件驱动的`网络应用框架,用于快速开发一个拥有高性能、高可拓展性的协议的server和client。 + +## netty example +### Discard Server Example +如下是一个Discard Protocol的样例,其会对所有接收到的数据进行丢弃,并且不返回任何结果。handler方法会处理由netty产生的io事件: +```java +package io.netty.example.discard; + +import io.netty.buffer.ByteBuf; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * Handles a server-side channel. + */ +public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1) + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2) + // Discard the received data silently. + ((ByteBuf) msg).release(); // (3) + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4) + // Close the connection when an exception is raised. + cause.printStackTrace(); + ctx.close(); + } +} +``` + +DiscardServerHandler继承了`ChannelInboundHandlerAdapter`,而`ChannelInboundHandlerAdapter`则是实现了`ChannelInboundHandler`。`ChannelInboundHandler`提供了各种不同的io事件处理方法,可以对这些方法进行覆盖。 + +在上述示例中,对chnnelRead方法进行了重写,channelRead方法将会在从客户端接收到数据时被调用,被调用时会把从客户端接收到的消息作为参数。在上述示例中,接收到的消息是ByteBuf类型。 + +> ByteBuf是一个引用计数对象,必须要通过调用`release`方法来显式释放。并且,`handler method有责任对传入的引用计数对象进行释放操作`。 + +通常,channelRead方法的重写按照如下形式: +```java +@Override +public void channelRead(ChannelHandlerContext ctx, Object msg) { + try { + // Do something with msg + } finally { + // handler makes sure that msg passed has been released + ReferenceCountUtil.release(msg); + } +} +``` + +上述示例中除了重写channelRead方法外,还重写了`exceptionCaught`方法。exceptionCaught方法会在 ***netty由于io error抛出异常*** 或是 ***handler method实现在处理事件时抛出异常*** 的场景下被调用。 + +在通常情况下,被exceptionCaught方法捕获的异常,其异常信息应该被打印到日志中,且异常关联的channel应该被关闭。通过重写caughtException也可以自定义异常捕获后的实现,例如在关闭channel之前向client发送error code. + +### netty应用程序实现示例 +如下是netty实现的Discard Server示例: +```java +package io.netty.example.discard; + +import io.netty.bootstrap.ServerBootstrap; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +/** + * Discards any incoming data. + */ +public class DiscardServer { + + private int port; + + public DiscardServer(int port) { + this.port = port; + } + + public void run() throws Exception { + EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); // (2) + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) // (3) + .childHandler(new ChannelInitializer() { // (4) + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new DiscardServerHandler()); + } + }) + .option(ChannelOption.SO_BACKLOG, 128) // (5) + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) + + // Bind and start to accept incoming connections. + ChannelFuture f = b.bind(port).sync(); // (7) + + // 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. + f.channel().closeFuture().sync(); + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + int port = 8080; + if (args.length > 0) { + port = Integer.parseInt(args[0]); + } + + new DiscardServer(port).run(); + } +} +``` +### 核心组件解析 +#### NioEventLoopGroup +`NioEventLoopGroup`是一个多线程的event loop,用于处理io操作。netty提供了不同的EventLoopGroup实现用于不同种类的传输。 + +在上述示例中,使用了两个NioEventLoopGroup: +- boss:boss event loop用于接收incoming connections +- worker:worker event loop用于处理accepted connection的通信 + +每当boss event loop接收到connection之后,其会将新的连接注册到worker event loop。 + +event group loop中拥有的线程数,以及线程如何与channel相关联(可能是每个线程对应一个channel、或是一个线程管理多个channel),由EventLoopGroup的实现来决定,并且可以通过构造器来进行配置。 + +#### ServerBootstrap +ServerBoostrap是一个helper类,用于方便的构建一个netty server。 + +#### NioServerSocketChannel +在上述使用中,使用了NioServerSocketChannel来实例化一个channel,改channel用于接收incoming connection。 + +#### handler注册 +在上述示例中,为accepted connection注册handler的示例如下所示: +```java +.childHandler(new ChannelInitializer() { // (4) + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new DiscardServerHandler()); + } + }) +``` +在上述注册示例中,每个newly accepted channel都会调用DiscardServerHandler。 + +`ChannelInitializer`是一个特殊的handler,用于帮助用户为新channel配置ahndler。其initChannel为新channel的ChannelPipeline配置添加自定义的handler,从而实现自定义的消息处理逻辑。 + +#### 为Channel实现设置参数 +通过option和childOption方法,可以为channel设置参数。由于netty server基于TCP/IP,故而可以指定socket option例如`tcpNoDelay`和`keepAlive`参数。 + +- option:option设置的是NioServerSocketCahnel,该channel用于接收incoming connection +- childOption:childOption用于设置被server channel接收的channel,被接收的channel为SocketChannel + +#### 为Channel绑定port +在经历完上述配置之后,就可以为server绑定监听端口并启动了。 +