4 传输
OIO和NIO写法大相径庭,但netty的阻塞和非阻塞的代码基本一致,
public class NettyOioServer { public void server(int port) throws Exception { final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8"))); EventLoopGroup group = new OioEventLoopGroup(); try { //创建 ServerBootstrap ServerBootstrap b = new ServerBootstrap(); b.group(group) //使用 OioEventLoopGroup以允许阻塞模式(旧的I/O) .channel(OioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) //指定 ChannelInitializer,对于每个已接受的连接都调用它 .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( //添加一个 ChannelInboundHandlerAdapter以拦截和处理事件 new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(buf.duplicate()) //将消息写到客户端,并添加 ChannelFutureListener, //以便消息一被写完就关闭连接 .addListener(ChannelFutureListener.CLOSE); } }); } }); //绑定服务器以接受连接 ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); } finally { //释放所有的资源 group.shutdownGracefully().sync(); } }
public class NettyNioServer { public void server(int port) throws Exception { final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8"))); //为非阻塞模式使用NioEventLoopGroup NioEventLoopGroup group = new NioEventLoopGroup(); try { //创建ServerBootstrap ServerBootstrap b = new ServerBootstrap(); b.group(group).channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) //指定 ChannelInitializer,对于每个已接受的连接都调用它 .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( //添加 ChannelInboundHandlerAdapter以接收和处理事件 new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //将消息写到客户端,并添加ChannelFutureListener, //以便消息一被写完就关闭连接 ctx.writeAndFlush(buf.duplicate()) .addListener(ChannelFutureListener.CLOSE); } }); } } ); //绑定服务器以接受连接 ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); } finally { //释放所有的资源 group.shutdownGracefully().sync(); } } }
每个Channel 都将会被分配一个ChannelPipeline 和ChannelConfig。
ChannelConfig 包含了该Channel 的所有配置设置,并且支持热更新。由于特定的传输可能
具有独特的设置,所以它可能会实现一个ChannelConfig 的子类型。
ChannelHandler 的典型用途包括:
将数据从一种格式转换为另一种格式;
提供异常的通知;
提供Channel 变为活动的或者非活动的通知;
提供当Channel 注册到EventLoop 或者从EventLoop 注销时的通知;
提供有关用户自定义事件的通知。
零拷贝 零拷贝(zero-copy)是一种目前只有在使用NIO 和Epoll 传输时才可使用的特性。它使你可以快速 高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间,其在像FTP 或者 HTTP 这样的协议中可以显著地提升性能。但是,并不是所有的操作系统都支持这一特性。特别地,它对 于实现了数据加密或者压缩的文件系统是不可用的——只能传输文件的原始内容。反过来说,传输已被 加密的文件则不是问题。
4.3.2 Epoll—用于Linux 的本地非阻塞传输 正如我们之前所说的,Netty 的NIO 传输基于Java 提供的异步/非阻塞网络编程的通用抽象。 虽然这保证了Netty 的非阻塞API 可以在任何平台上使用,但它也包含了相应的限制,因为JDK 为了在所有系统上提供相同的功能,必须做出妥协。 Linux作为高性能网络编程的平台,其重要性与日俱增,这催生了大量先进特性的开发,其 中包括epoll——一个高度可扩展的I/O事件通知特性。这个API自Linux内核版本2.5.44(2002)被 引入,提供了比旧的POSIX select和poll系统调用① ① 参见Linux 手册页中的epoll(4):http://linux.die.net/man/4/epoll。 更好的性能,同时现在也是Linux上非阻 塞网络编程的事实标准。Linux JDK NIO API使用了这些epoll调用。 Netty为Linux提供了一组NIO API,其以一种和它本身的设计更加一致的方式使用epoll,并 且以一种更加轻量的方式使用中断。① 如果你的应用程序旨在运行于Linux系统,那么请考虑利用 这个版本的传输;你将发现在高负载下它的性能要优于JDK的NIO实现。 这个传输的语义与在图4-2 所示的完全相同,而且它的用法也是简单直接的。相关示例参照 代码清单4-4。如果要在那个代码清单中使用epoll 替代NIO,只需要将NioEventLoopGroup 替换为EpollEventLoopGroup , 并且将NioServerSocketChannel.class 替换为 EpollServerSocketChannel.class 即可。
Netty的OIO实现