官方网站建设银行2010年存款利息,上海公司购房政策,百度商桥绑定网站,手工活接单正规平台Netty 4.0 新的特性及需要注意的地方 这篇文章和你一起过下Netty的主发行版本的一些显著的改变和新特性#xff0c;让你在把你的应用程序转换到新版本的时候有个概念。 项目结构改变 Netty的包名从org.jboss.netty改为io.netty#xff0c;因为我们不在是JBoss.org的一部分了。…Netty 4.0 新的特性及需要注意的地方 这篇文章和你一起过下Netty的主发行版本的一些显著的改变和新特性让你在把你的应用程序转换到新版本的时候有个概念。 项目结构改变 Netty的包名从org.jboss.netty改为io.netty因为我们不在是JBoss.org的一部分了。 二进制JAR包被分为了多个子模块以便用户能够从类路径中去掉非必需的特性。当前的结构如下 模块描述nettyproject parentcommonutility and loggingbufferbuffer APItransportchannel API and its core implementationstransport-rtrxRTRX transport implementationtransport-sctpSCTP transport implementationtransport-udtUDT transport implementationhandlerchannel handlerscodeccodec frameworkcodec-httpHTTP, Web Sockets, SPDY, and RTSP codeccodec-socksSocks codecexampleexamplesallgenerates an all-in-one JARtarballgenerates a tarball distribution所有的Netty的Jar除了netty-all外包现在都是OSGI的bundle能够用在你喜欢的OSGI容器上。 常用API的变化 现在Netty里的大部分操作都支持简洁的方法链。不能配置的getter现在都没有了get/is前缀 如Channel.getRemoteAddress()→Channel.remoteAddress()Buffer API变化 ChannelBuffer → ByteBuf 由于上文所提到的结构上的变化buffer API现在可以作为一个单独的包被使用。为此ChannelBuffer这个类型名也不那么讲得通了而应该变更为ByteBuf。 用来创建新buffer的功能类ChannelBuffers被拆分为两个功能类Unpooled和BufUtil。就像这个名字所暗示的4.0引入了一个新的池化的ByteBufs它可以通过ByteBuf的分配器Allocator的对应实现ByteBufAllocator来获得。 大多数的buffer变成了动态的具备可配置的最大容量 在3.x时期buffer分为固定和动态两种类型。一个固定buffer的容量在创建之后就无法改变而动态buffer的容量在write*译者按writeByte,writeInt,writeLong...方法需要更多空间时自动扩容。 从4.0开始所有buffer都变成了动态的。但是相对于之前的动态进行了优化。你可以更容易也更安全的对一个buffer的容量进行扩大和缩小。之所以说它容易是因为有一个新的ByteBuf.capacity(int newCapacity)的方法。说它安全是因为你可以设置一个容量的最大值以防止容量没有限制的增大。 1// 不要再使用 dynamicBuffer() - 使用 buffer(). 2ByteBuf buf ByteBuf.buffer(); 3 4// 增加buffer的容量 5buf.capacity(1024); 6... 7 8// 缩减buffer的容量 (最后的512个byte被丢弃) 9buf.capacity(512); 唯一的例外是那些使用wrappedBuffer方法创建的包装(warp)了一个buffer或一个byte数组的buffer。你无法扩大它的容量因为这样会使包装一个已有buffer的目的是去意义——减少内存的复制。如果你想要在包装了一个buffer之后改变它的容量你应该重新创建一个拥有足够容量的buffer然后将你想要包装的那个buffer的内容复制过来。 新接口: CompositeByteBuf 一个新的名叫CompositeByteBuf的接口为组合buffer(composite buffer)的实现定义了多种高级的操作。一个用户可以使用组合buffer以只比随机访问大一点的代价达到一个批量内存复制的目的。要创建一个新的组合buffer可以像以前一样使用Unpooled.wrappedBuffer(... 译者注此处省略号应该是指省略方法参数下同)或Unpooled.compositeBuffer(...)。 可预知的NIO buffer转型 在3.x中ChannelBuffer.toByteBuffer()以及它的其他变体所提供的约定并不那么明确。用户无法确定这些方法会返回一个拥有共享数据的视图buffer还是一个拥有独立数据的通过复制得到的buffer。4.0将toByteBuffer()替换为ByteBuf.nioBufferCount()nioBuffer()以及nioBUffers()。如果调用nioBufferCount()返回0用户总是可以通过调用copy().nioBuffer()来获得一个复制的buffer。 对小字节序变更的支持 对小字节序的支持经历了重大变化。在之前的版本中一个用户为了得到一个小字节序的buffer有两种选择特别指定一个LittleEndianHeapChannelBufferFactory用目标字节序将已存在的buffer包装起来。4.0添加了一个新方法ByteBuf.order(ByteOrder)。这个方法返回当前buffer对象的一个具有指定字节序的视图buffer 01import io.netty.buffer.ByteBuf; 02import io.netty.buffer.Unpooled; 03import java.nio.ByteOrder; 04 05ByteBuf buf Unpooled.buffer(4); 06buf.setInt(0, 1); 07// 打印出 00000001 08System.out.format(%08x%n, buf.getInt(0)); 09 10ByteBuf leBuf buf.order(ByteOrder.LITTLE_ENDIAN); 11// 打印出 01000000 12System.out.format(%08x%n, leBuf.getInt(0)); 13 14assert buf ! leBuf; 15assert buf buf.order(ByteOrder.BIG_ENDIAN); Pooled ByteBuf 前面已经提到Netty引入了pooledByteBufinstances。这在很多方面都很实用举列如下 限制了GC压力这是因为使用unpooled ByteBufs会造成沉重的分配与再分配问题Better handling of direct (native)ByteBuf更好的处理直接本地的ByteBuf一个ByteBuf 可以被一个ByteBufAllocator包含. 01public interface ByteBufAllocator { 02 03 ByteBuf buffer(); 04 ByteBuf buffer(int initialCapacity); 05 ByteBuf buffer(int initialCapacity, int maxCapacity); 06 ByteBuf heapBuffer(); 07 ByteBuf heapBuffer(int initialCapacity); 08 ByteBuf heapBuffer(int initialCapacity, int maxCapacity); 09 ByteBuf directBuffer(); 10 ByteBuf directBuffer(int initialCapacity); 11 ByteBuf directBuffer(int initialCapacity, int maxCapacity); 12 ByteBuf ioBuffer(); 13 14 CompositeByteBuf compositeBuffer(); 15 CompositeByteBuf compositeBuffer(int maxNumComponents); 16 CompositeByteBuf compositeHeapBuffer(); 17 CompositeByteBuf compositeHeapBuffer(int maxNumComponents); 18 CompositeByteBuf compositeDirectBuffer(); 19 CompositeByteBuf compositeDirectBuffer(int maxNumComponents); 20} 要想从一个handler那里获取当前的 ByteBufAllocator,可以使用ChannelHandlerContext.alloc()或Channel.alloc()方法: 1Channel channel ...; 2ByteBuf buf channel.alloc().buffer(512); 3.... 4channel.write(buf); 5 6ChannelHandlerContext ctx ... 7ByteBuf buf2 ctx.alloc().buffer(512); 8.... 9channel.write(buf2) 一旦一个ByteBuf被写入远程节点它会再次自动的释放进入释放到池the pool里。 默认的ByteBufAllocator为PooledByteBufAllocator.如果你不希望使用buffer pooling或使用你自己的allocator你可以运用Channel.config().setAllocator(..)以及一个可供选择的 allocator比如UnpooledByteBufAllocator。 Channel API的变化 在4.0中许多io.netty.channel包中的类都经历大量修改因此文本上的简单搜索-替换是无法让你基于3.x的程序迁移到4.0上。这个部分会尝试将这些重大变更背后的思考过程展示出来而不只是简单地作为展示所有变更。 翻新后的ChannelHandler接口 Upstream → Inbound, Downstream → Outbound 对于初学者来说术语upstream译者注直译为向上游有点像TCP/IP协议栈中从下往上从物理层最终到达应用层这么一个流程和downstream有点让人迷惑。在4.0中只要可能都会使用inbound译者注直译为开往内地的相对于upstream确实更贴切即指数据从外部网络经历层层filter到达我们的处理逻辑和outbound来替换他们。 新的ChannelHandler继承层次 在3.x时代ChannelHandler只是一个标记接口而在ChannelUpstreamHandler、ChannelDownstreamHandler、LifeCycleAwareChannelHandler定义了具体的处理器方法。在Netty 4中ChannelHandler将LifeCycleAwareChannelHandler接口和一堆实现辅助方法融合到了一起具体见代码 01public interface ChannelHandler { 02 03 void beforeAdd(ChannelHandlerContext ctx) throws Exception; 04 void afterAdd(ChannelHandlerContext ctx) throws Exception; 05 void beforeRemove(ChannelHandlerContext ctx) throws Exception; 06 void afterRemove(ChannelHandlerContext ctx) throws Exception; 07 08 void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception; 09 void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception; 10 ... 11} 下方的图表描述了这个新的类型集成层次 fixme原文中还没有插入此图 事件对象从ChannelHandler中消失了 在3.x时代所有的I/O操作都会创建一个新的ChannelEvent对象。对每个读或写的操作还会额外创建一个新的ChannelBuffer对象。由于将资源管理和buffer的池化交给了JVM这实际上极大地简化了Netty的内部实现。但是基于Netty开发的应用在高负载下运行时有时会观察到GCGarbage Collection的压力增大或变化不定这些问题的根源也来自于这里。 4.0通过把事件对象替换为直接与类型相对应译者注原文为strongly typed但是我觉得直译为强类型不太容易理解的方法调用几乎完全避免了事件对象的创建。3.x中有类似于handleUpstream()和handleDownstream()这种能够捕获所有相关类型事件的处理器方法4.0中你将不会再看到它们的身影了。所有的事件类型现在都有各自对应的处理器方法 01// 3.x时代: 02void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception; 03void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception; 04 05// 4.0: 06void channelRegistered(ChannelHandlerContext ctx) throws Exception; 07void channelUnregistered(ChannelHandlerContext ctx) throws Exception; 08void channelActive(ChannelHandlerContext ctx) throws Exception; 09void channelInactive(ChannelHandlerContext ctx) throws Exception; 10void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception; 11 12void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelFuture future) throws Exception; 13void connect( 14 ChannelHandlerContext ctx, SocketAddress remoteAddress, 15 SocketAddress localAddress, ChannelFuture future) throws Exception; 16void disconnect(ChannelHandlerContext ctx, ChannelFuture future) throws Exception; 17void close(ChannelHandlerContext ctx, ChannelFuture future) throws Exception; 18void deregister(ChannelHandlerContext ctx, ChannelFuture future) throws Exception; 19void flush(ChannelHandlerContext ctx, ChannelFuture future) throws Exception; 20void read(ChannelHandlerContext ctx); 21void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception; ChannelHandlerContext类也被修改来反映上述提到的变化: 1// Before: 2ctx.sendUpstream(evt); 3 4// After: 5ctx.fireInboundBufferUpdated(); 所有这些变化意味着用户无法去扩展ChannelEvent这个已经不存在的接口了。那用户要怎样才能定义他或她自己的事件类型呢就像IdleStateEvent4.0中的ChannelHandler有一个处理器方法叫做userEventTriggered()它就是被设计用来满足这种特殊的用户需求。 Simplified channel state model 在3.x中当一个新的Channel被创建并连接成功至少三个ChannelStateEvent会被触发channelOpen、channelBound以及channelConnected。当一个Channel关闭则对应channelDisconnected、channelUnbound以及channelClosed三个事件。 fixme 但是触发这么多事件的意义并不那么明显。如果在一个Channel进入可读或可写的状态时通知用户想来会更有帮助。 fixme channelOpen、channelBound和channelConnected被合并为channelActive。channelDisconnected、channelUnbound和channelClosed被合并为channelInactive。类似的Channel.isBound()和Channel.isConnected()也被合并为了Channel.isActive()。 需要注意的是channelRegistered和channelUnregistered这两个事件与channelOpen和channelClosed具有的意义是不一样的。它们channelRegistered和channelUnregistered是在支持Channel的动态注册、注销以及再注册时被引入的就像下图所示 fixme 每个处理器的缓存 不像3.x那样在每次读操作都简历一个新堆里的缓存来触发上游的MessageEvent4.0不会每次都创建新的 缓存。它直接从socket中读取数据到由用户的ChannelInboundByteHandler和ChannelInboundMessageHandler实现创建的入站缓存。 因为由上述处理器创建的入站缓存直到关联的通道关闭前都会重用所以在上面的GC和内存带宽消耗都能保持较小。同样当接收到的数据被销毁时用户已经完成操作codec的实现就变得更简单和有效了。 在创建出站缓存时也是差不多的不会新建。用户的ChannelOutBoundBYteHandler和ChannelOutboundMessageHandler来操作。 不需要每条消息都有一个事件 4.0里不再有了messageReceived或writeRequested处理器方法。它们被inboundBufferUpdated和flush代替了。用户的入队一个或多个消息到一个入站或出站缓存同时会出发一个inboundBUfferUpdated或flush事件。 01public void inboundBufferUpdated(ChannelHandlerContext ctx) { 02 QueueMyMessage in ctx.inboundMessageBuffer(); 03 QueueMyNewMessage out ctx.nextInboundMessageBuffer(); 04 for (;;) { 05 MyMessage m in.poll(); 06 if (m null) { 07 break; 08 } 09 MyNewMessage decoded decode(m); 10 out.add(decoded); 11 } 12 ctx.fireInboundBufferUpdated(); 13} 14 15public void flush(ChannelHandlerContext ctx, ChannelFuture future) { 16 QueueMyNewMessage in ctx.outboundMessageBuffer(); 17 QueueMyMessage out ctx.nextOutboundMessageBuffer(); 18 for (;;) { 19 MyNewMessage m in.poll(); 20 if (m null) { 21 break; 22 } 23 MyMessage encoded encode(m); 24 out.add(encoded); 25 } 26 ctx.flush(future); 27} 作为选择用户能够在每个单独的入站或出站消息中触发这样的事件来模拟老的行为尽管相对新方法来说效率更低。 消息处理器 vs. 字节处理器 在3.x里一个MessageEvent持有一个任意的对象。它能够是一个ChannelBuffer或是一个用户自定义的对象它们都是同样对待的 01Override 02public void messageReceived(ChannelHandlerContext ctx, MessageEvent evt) { 03 Object msg evt.getMessage(); 04 if (msg instanceof ChannelBuffer) { 05 ChannelBuffer buf (ChannelBuffer) msg; 06 ... 07 } else { 08 MyMessage myMsg (MyMessage) msg; 09 ... 10 } 11} 在4.0里它们就分别对待了因为一个处理器不再处理一个独立的消息而是处理多种多样的消息 01public void inboundBufferUpdated(ChannelHandlerContext ctx) { 02 if (ctx.hasInboundByteBuffer()) { 03 ByteBuf buf ctx.inboundByteBuffer(); 04 ... 05 } else { 06 QueueMyMessage buf ctx.inboundMessageBuffer(); 07 for (;;) { 08 MyMessage msg buf.poll(); 09 if (buf null) { 10 break; 11 } 12 ... 13 } 14 } 15} 你可能发现一个ServerChannel的处理器是一个入站缓存是QueueChannel的入站处理器是较为有趣的。 处理器适配器 大多数用户都发现创建和管理它的生命周期是繁琐的因此它支持用户扩展预定义好的适配器类来使得更方便 ChannelHandlerAdapterChannelStateHandlerAdapterChannelOperationHandlerAdapterChannelInboundMessageHandlerAdapterChannelInboundByteHandlerAdapterChannelOutboundMessageHandlerAdapterChannelOutboundByteHandlerAdapter明智的和不易出错的入站流量挂起 3.x有一个由Channel.setReadable(boolean)提供的不是很明显的入站流量挂起机制。它引入了在ChannelHandler之间的复杂交互操作同时处理器由于不正确实现而很容易互相干扰。 4.0里新的名为read()的出站操作增加了。如果你使用Channel.config().setAutoRead(false)来关闭默认的auto-read标志Netty不会读入任何东西直到你显式地调用read()操作。一旦你发布的read()操作完成同时通道再次停止读一个名为channelReadSuspended()的入站事件会触发一遍你能够重新发布另外的read()操作。你同样也可以拦截read()操作来执行更多高级的流量控制。 暂停接收传入的连接 在3.x里没有方法让一个用户告诉Netty来厅子接收传入连接除非是阻塞I/O线程或者关闭服务器socket。在aotu-read标志没有设置的时候4.0涉及到的read()操作就像一个普通的通道。 半关闭socket TCP和SCTP允许用户关闭一个socket的出站流量而不用完全关闭它。这样的socket被称为“半关闭socket”同时用户能够通过调用SocketChannel.shutdownOutput()方法来获取一个半关闭socket。如果一个远端关闭了出站通道SocketChannel.read(..)会返回-1这看上去并没有和一个关闭了的链接有什么区别。 3.x没有shutdownOutput()操作。同样它总是在SocketChannel.read(..)返回-1的时候关闭链接。 要支持半关闭socket4.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服务器socketNIO UDP socket以及虚拟机内部的通道在同一个I/O线程里。在编写一个需要最小延迟的代理服务器时这确实很有用。 能够从一个已存在的jdk套接字上创建一个Channel 3.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。 01java.nio.channels.FileChannel myFile ...; 02java.nio.channels.SocketChannel mySocket java.nio.channels.SocketChannel.open(); 03 04// Perform some blocking operation here. 05... 06 07// Netty takes over. 08SocketChannel ch new NioSocketChannel(mySocket); 09EventLoopGroup group ...; 10group.register(ch); 11... 12 13// Deregister from Netty. 14ch.deregister().sync(); 15 16// Perform some blocking operation here. 17mySocket.configureBlocking(false); 18myFile.transferFrom(mySocket, ...); 19 20// Register back again to another event loop group. 21EventLoopGroup anotherGroup ...; 22anotherGroup.register(ch); 调度任意的任务到一个I/O线程里运行 当一个Channel被注册到EventLoopGroup时Channel实际上是注册到由EventLoopGroup管理EventLoop中的一个。EventLoop实现了java.utilconcurrent.ScheduledExecutorService接口。这意味着用户可以在一个用户通道归属的I/O线程里执行或调度一个任意的Runnable或Callable。随着新的娘好定义的线程模型的到来稍后会介绍它变得极其容易地编写一个线程安全的处理器。 01public class MyHandler extends ChannelOutboundMessageHandlerAdapter { 02 ... 03 public void flush(ChannelHandlerContext ctx, ChannelFuture f) { 04 ... 05 ctx.flush(f); 06 07 // Schedule a write timeout. 08 ctx.executor().schedule(new MyWriteTimeoutTask(), 30, TimeUnit.SECONDS); 09 ... 10 } 11} 12 13public class Main { 14 public static void main(String[] args) throws Exception { 15 // Run an arbitrary task from an I/O thread. 16 Channel ch ...; 17 ch.executor().execute(new Runnable() { ... }); 18 } 19} 简化的关闭 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选项。 01ChannelConfig cfg ...; 02 03// Before: 04cfg.setOption(tcpNoDelay, true); 05cfg.setOption(tcpNoDelay, 0); // Runtime ClassCastException 06cfg.setOption(tcpNoDelays, true); // Typo in the option name - ignored silently 07 08// After: 09cfg.setOption(ChannelOption.TCP_NODELAY, true); 10cfg.setOption(ChannelOption.TCP_NODELAY, 0); // Compile error AttributeMap 在回应用户指令里你可以附加任意的对象到Channel和ChannelHandlerContext。一个名为AttributeMap的新接口被加入了它被Channel和ChannelHandlerContext继承。作为替代ChannelLocal和Channel.attachment被移除。这些属性会在他们关联的Channel被垃圾回收的同时回收。 01public class MyHandler extends ChannelInboundMessageHandlerAdapterMyMessage { 02 03 private static final AttributeKeyMyState STATE 04 new AttributeKeyMyState(MyHandler.state); 05 06 Override 07 public void channelRegistered(ChannelHandlerContext ctx) { 08 ctx.attr(STATE).set(new MyState()); 09 ctx.fireChannelRegistered(); 10 } 11 12 Override 13 public void messageReceived(ChannelHandlerContext ctx, MyMessage msg) { 14 MyState state ctx.attr(STATE).get(); 15 } 16 ... 17} 新的bootstrap API bootstrap API已经重头重写尽管它的目的还是一样它执行需要使服务器或客户端运行的典型步骤通常能在样板代码里找到。新的bootstrap同样采取了流畅的接口。 01public static void main(String[] args) throws Exception { 02 // Configure the server. 03 ServerBootstrap b new ServerBootstrap(); 04 try { 05 b.group(new NioEventLoopGroup(), new NioEventLoopGroup()) 06 .channel(new NioServerSocketChannel()) 07 .option(ChannelOption.SO_BACKLOG, 100) 08 .localAddress(8080) 09 .childOption(ChannelOption.TCP_NODELAY, true) 10 .childHandler(new ChannelInitializerSocketChannel() { 11 Override 12 public void initChannel(SocketChannel ch) throws Exception { 13 ch.pipeline().addLast(handler1, handler2, ...); 14 } 15 }); 16 17 // Start the server. 18 ChannelFuture f b.bind().sync(); 19 20 // Wait until the server socket is closed. 21 f.channel().closeFuture().sync(); 22 } finally { 23 // Shut down all event loops to terminate all threads. 24 b.shutdown(); 25 } 26} ChannelPipelineFactory → ChannelInitializer 和你在上面的例子注意到的一样ChannelPipelineFactory不再存在了。而是由ChannelInitializer来替换它给予了在Channel和ChannelPipeline的配置的更多控制。 请注意你不能自己创建一个新的ChannelPipeline。通过观察目前为止的用例报告Netty项目队伍总结到让用户去创建自己的管道实现或者是继承默认的实现是没有好处的。因此ChannelPipeline不再让用户创建。ChannelPipeline由Channel自动创建。 ChannelFuture拆分为ChannelFuture和ChannelPromise ChannelFuture已经被拆分为ChannelFuture和ChannelPromise了。这不仅仅是让异步操作里的生产者和消费者间的约定更明显同样也是得在使用从链中返回的ChannelFuture更加安全因为ChannelFuture的状态是不能改变的。 由于这个编号一些方法现在都采用ChannelPromiser而不是ChannelFuture来改变它的状态。 良好定义的线程模型 在3.x里并没有良好设计的线程模型尽管曾经要修复线程模型在3.5的不一致性。4.0定义的一个严格的线程模型来帮助用户编写ChannelHandler而不必担心太多关于线程安全的东西。 Netty将不会再同步地调用ChannelHandler的方法了除非ChannelHandler由Shareable注解。这不会理会处理器方法的类似——入站、操作、或者是生命周期时间处理器方法。 用户不再需要同步入站或出站的事件处理器方法。4.0不允许加入加入一个ChannelHandler超过一次除非它由Sharable注解。每个由Netty调用的ChannelHandler的方法之间的关系总是happens-before。 用户不用定义一个volatile字段来保存处理器的状态。用户能够在他加入一个处理器到ChannelPipeline的时候指定EventExecutor。 如果有指定ChannelHandler的处理器方法总是由自动的EventExecutor来调用如果没指定处理器方法总是由它关联的Channel注册到的EventLoop来调用。声明到一个处理器的EventExecutor和EventLoop总是单线程的。 处理器方法总是由相同线程调用。如果指定了多线程的EventExecutor或EventLoop线程中的一个会被选择然后选择到的线程将会被使用直到取消注册。如果在相同管道里的两个处理器声明到不同的EventExecutor它们会同时被调用。如果多个一个处理器去访问共享数据用户需要注意线程安全即使共享数据只能被相同管道里的处理器访问。加入到ChannelFuture的ChannelFutureListener总是由关联到future相关的Channel的EventLoop线程调用。不再有ExecutionHandler ——它包含到核心里 在你加入一个ChannelHandler到一个ChannelPipeline来告诉管道总是通过指定的EventExecutor调用加入的ChannelHander处理器的方法的时候你可以指定一个EventExecutor。 1Channel ch ...; 2ChannelPipeline p ch.pipeline(); 3EventExecutor e1 new DefaultEventExecutor(16); 4EventExecutor e2 new DefaultEventExecutor(8); 5 6p.addLast(new MyProtocolCodec()); 7p.addLast(e1, new MyDatabaseAccessingHandler()); 8p.addLast(e2, new MyHardDiskAccessingHandler()); EventExecutor是EventLoop的超类同时也继承了ScheduledExecutorService。 fixme 编码解码器框架变化 在编码解码器框架里有实质性的内部改变因为4.0需要一个处理器来创建和管理它的缓存看这篇文章的每个处理器缓存部分。然而从用户角度来看这些变化都不是很大的。 核心编码界面器类移到io.netty.handler.codec包里。FrameDecoder重命名为ByteToMessageDecoder。OneToOneEncoder和OneToOneDecoder由MessageToMessageEncoder和MessageToMessageDecoder替换。decode()decodeLast()encode()的方法前面稍微改变了来支持泛型同时移除冗余参数。编码解码器嵌入器→ EmbeddedChannel 编码解码器嵌入器已经被 io.netty.channel.embedded.EmbeddedByteChannel和EmbeddedMessageChannel替换了。EmbeddedChannel允许用户对任何包含编码解码器的管道进行单元测试。 HTTP编码解码器 HTTP解码器现在在每个HTTP消息中总生成多个消息对象 11 * HttpRequest / HttpResponse 20 - n * HttpContent 31 * LastHttpContent 要看更多的细节请到转到已更新了的HttpSnoopServer例子。如果你希望为一个单一的HTTP消息处理多个消息你可以把HttpObjectAggregator放入管道里。HttpObjectAggregator会把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse。 传输实现的变化 下面是传输协议新加入的东西 使用NIO.2AsynchronousSocketChannel的AIO套接字传输实现。OIO SCTP 传输实现UDT 传输实现用例学习移植示例Factorial 这部分粗略地展示把示例Factorial从3.0移植到4.0的步骤。示例Factorial已经移植到4.0了它放在io.netty.example.factorial包里。请浏览示例的源代码来看下每一处的变化。 移植服务端 重写FactorialServer.run()方法来使用新的 bootstrap API。不再有ChannelFactory了。 由你自己去实例化NioEventLoop一个是用来接收接入的链接另外的就用来处理接收到的链接。从命名FactorialServerPipelineFactory为FactorialServerInitializer。让它继承ChannelInitializerChannel。取代创建新的ChannelPipeline通过Channel.pipeline()来获取。让FactorialServerHandler继承sChannelInboundMessageHandlerAdapterBigInteger。用channelInactive()来替换channelDisconnected()。handleUpstream()不能再使用了。让BigIntegerDecoder继承ByteToMessageDecoderBigInteger。让NumberEncoder继承MessageToByteEncoderNumber。encode()不在返回一个缓存了。由ByteToMessageDecoder来提供填充编码好的数据到缓存里。移植客户端 大部分和移植服务端差不多但你要在你编写一个潜在的大数据流时要多注意下。 重写FactorialClient.run()方法来使用新的bootstrap API。重命名FactorialClientPipelineFactory为FactorialClientInitializer。使FactorialClientHandler继承ChannelInboundMessageHandlerAdapterBigInteger在这一点你发现在4.0里没有了Channel.isWritable()或者channelInterestChanged()。作为代替你自己来管理那些未决定的写操作。新的sendNumbers()看起来如下 01private void sendNumbers() { 02 // Do not send more than 4096 numbers. 03 boolean finished false; 04 MessageBufObject out ctx.nextOutboundMessageBuffer(); 05 while (out.size() 4096) { 06 if (i count) { 07 out.add(Integer.valueOf(i)); 08 i ; 09 } else { 10 finished true; 11 break; 12 } 13 } 14 15 ChannelFuture f ctx.flush(); 16 if (!finished) { 17 f.addListener(numberSender); 18 } 19} 20 21private final ChannelFutureListener numberSender new ChannelFutureListener() { 22 Override 23 public void operationComplete(ChannelFuture future) throws Exception { 24 if (future.isSuccess()) { 25 sendNumbers(); 26 } 27 } 28}; 本文地址http://www.oschina.net/translate/netty-4-0-new-and-noteworthy 原文地址http://netty.io/wiki/new-and-noteworthy.html 本文中的所有译文仅用于学习和交流目的转载请务必注明文章译者、出处、和本文链接我们的翻译工作遵照 CC 协议如果我们的工作有侵犯到您的权益请及时联系我们 转自http://www.oschina.net/translate/netty-4-0-new-and-noteworthy?print转载于:https://www.cnblogs.com/blogsme/p/3599138.html