2016-11-04 16:15:29.0|分类: netty|浏览量: 3295
channel的生命周期 All these changes mean a user cannot extend the non-existing Simplified channel state modelWhen a new connected However, it's of dubious value to trigger that many events. It is more useful for a user to get notified when a
Note that
4.0 introduced a new operation called Because of this change, you must be very careful not to forget to call 暂停接收传入的连接在3.x里,没有方法让一个用户告诉Netty来厅子接收传入连接,除非是阻塞I/O线程或者关闭服务器socket。在aotu-read标志没有设置的时候,4.0涉及到的read()操作就像一个普通的通道。 半关闭socketTCP和SCTP允许用户关闭一个socket的出站流量而不用完全关闭它。这样的socket被称为“半关闭socket”,同时用户能够通过调用SocketChannel.shutdownOutput()方法来获取一个半关闭socket。如果一个远端关闭了出站通道,SocketChannel.read(..)会返回-1,这看上去并没有和一个关闭了的链接有什么区别。 3.x没有shutdownOutput()操作。同样,它总是在SocketChannel.read(..)返回-1的时候关闭链接。 要支持半关闭socket,4.0增加了SocketChannel.shutdownOutput()方法,同时用户能设置“ALLOW_HALF_CLOSURE”的ChanneOption来阻止Netty在SocketChannel.read(..)返回-1的时候自动关闭链接。 灵活的I/O线程分配在3.x里,一个Channel是由ChannelFactory创建的,同时新创建的Channel会自动注册到一个隐藏的I/O线程。4.0使用新的名为EventLoopGroup的接口来替换ChannelFactory,它由一个或多个EventLoop来构成。同样,一个新的Channel不会自动注册到EventLoopGroup,但用户可以显式调用EventLoopGroup.register()来注册。 感谢这个变化(举例来说,分离了ChannelFactory和I/O线程),用户可以注册不同的Channel实现到同一个EventLoopGroup,或者同一个Channel实现到不同的EventLoopGroup。例如,你可以运行一个NIO服务器socket,NIO UDP socket,以及虚拟机内部的通道在同一个I/O线程里。在编写一个需要最小延迟的代理服务器时这确实很有用。 能够从一个已存在的jdk套接字上创建一个Channel3.x没提供方法从已存在的jdk套接字(如java.nio.channels.SocketChannel)创建一个新的通道。现在你可以用4.0这样做了。 取消注册和重新注册一个Channel从/到一个I/O线程一旦一个新的Channel在3.x里创建,它完全绑定到一个单一的I/O线程上,直到它底层的socket关闭。在4.0里,用户能够从I/O线程里取消注册一个Channel来完全控制它底层jdk套接字。例如,你能够利用Netty提供的高层次无阻塞I/O的优势来解决复杂的协议,然后取消注册Channel并且切换到阻塞模式来在可能的最大吞吐量下传输一个文件。当然,它能够再次注册已经取消了注册的Channel。 java.nio.channels.FileChannel myFile = ...; java.nio.channels.SocketChannel mySocket = java.nio.channels.SocketChannel.open(); // Perform some blocking operation here. ... // Netty takes over. SocketChannel ch = new NioSocketChannel(mySocket); EventLoopGroup group = ...; group.register(ch); ... // Deregister from Netty. ch.deregister().sync(); // Perform some blocking operation here. mySocket.configureBlocking(false); myFile.transferFrom(mySocket, ...); // Register back again to another event loop group. EventLoopGroup anotherGroup = ...; anotherGroup.register(ch); 调度任意的任务到一个I/O线程里运行当一个Channel被注册到EventLoopGroup时,Channel实际上是注册到由EventLoopGroup管理EventLoop中的一个。EventLoop实现了java.utilconcurrent.ScheduledExecutorService接口。这意味着用户可以在一个用户通道归属的I/O线程里执行或调度一个任意的Runnable或Callable。随着新的娘好定义的线程模型的到来(稍后会介绍),它变得极其容易地编写一个线程安全的处理器。 public class MyHandler extends ChannelOutboundMessageHandlerAdapter { ... public void flush(ChannelHandlerContext ctx, ChannelFuture f) { ... ctx.flush(f); // Schedule a write timeout. ctx.executor().schedule(new MyWriteTimeoutTask(), 30, TimeUnit.SECONDS); ... } } public class Main { public static void main(String[] args) throws Exception { // Run an arbitrary task from an I/O thread. Channel ch = ...; ch.executor().execute(new Runnable() { ... }); } } 简化的关闭releaseExternalResources()不必再用了。你可以通过调用EventLoopGroup.shutdown()直接地关闭所有打开的连接同时使所有I/O线程停止,就像你使用java.util.concurrent.ExecutorService.shutdown()关闭你的线程池一样。 类型安全的ChannelOptions有两个方法来配置Netty的Channel的socket参数。第一个是明确地调用ChannelConfig的setter,例如SocketChannelConfig.setTcpNoDelay(true)。这是最为类型安全的方法。另外一个是调用ChannelConfig.setOption()方法。有时候你不得不决定在运行时的时候socket要配置什么选项,同时这个方法在这种情况下有点不切实际。然而,在3.x里它是容易出错的,因为一个用户必需用一对字符串和对象来指定选项。如果用户调用了错误的选项名或者值,他或她将会赵宇到一个ClassCastException或指定的选项甚至可能会默默地忽略了。 4.0引入了名为ChannelOption的新的类型,它提供了类型安全地访问socket选项。 ChannelConfig cfg = ...; // Before: cfg.setOption("tcpNoDelay", true); cfg.setOption("tcpNoDelay", 0); // Runtime ClassCastException cfg.setOption("tcpNoDelays", true); // Typo in the option name - ignored silently // After: cfg.setOption(ChannelOption.TCP_NODELAY, true); cfg.setOption(ChannelOption.TCP_NODELAY, 0); // Compile error AttributeMap在回应用户指令里,你可以附加任意的对象到Channel和ChannelHandlerContext。一个名为AttributeMap的新接口被加入了,它被Channel和ChannelHandlerContext继承。作为替代,ChannelLocal和Channel.attachment被移除。这些属性会在他们关联的Channel被垃圾回收的同时回收。 public class MyHandler extends ChannelInboundMessageHandlerAdapter<MyMessage> { private static final AttributeKey<MyState> STATE = new AttributeKey<MyState>("MyHandler.state"); @Override public void channelRegistered(ChannelHandlerContext ctx) { ctx.attr(STATE).set(new MyState()); ctx.fireChannelRegistered(); } @Override public void messageReceived(ChannelHandlerContext ctx, MyMessage msg) { MyState state = ctx.attr(STATE).get(); } ... } 新的bootstrap APIbootstrap API已经重头重写,尽管它的目的还是一样;它执行需要使服务器或客户端运行的典型步骤,通常能在样板代码里找到。新的bootstrap同样采取了流畅的接口。 public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap b = new ServerBootstrap(); try { b.group(new NioEventLoopGroup(), new NioEventLoopGroup()) .channel(new NioServerSocketChannel()) .option(ChannelOption.SO_BACKLOG, 100) .localAddress(8080) .childOption(ChannelOption.TCP_NODELAY, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(handler1, handler2, ...); } }); // Start the server. ChannelFuture f = b.bind().sync(); // Wait until the server socket is closed. f.channel().closeFuture().sync(); } finally { // Shut down all event loops to terminate all threads. b.shutdown(); } } ChannelPipelineFactory → ChannelInitializer和你在上面的例子注意到的一样,ChannelPipelineFactory不再存在了。而是由ChannelInitializer来替换,它给予了在Channel和ChannelPipeline的配置的更多控制。 请注意,你不能自己创建一个新的ChannelPipeline。通过观察目前为止的用例报告,Netty项目队伍总结到让用户去创建自己的管道实现或者是继承默认的实现是没有好处的。因此,ChannelPipeline不再让用户创建。ChannelPipeline由Channel自动创建。 参考:http://www.oschina.net/translate/netty-4-0-new-and-noteworthy?cmp&p=1# |