设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Netty 4.0的新特性及需要注意的地方

2013-4-7 10:45| 发布者: joejoe0332| 查看: 24323| 评论: 0|原作者: 开源中国社区|来自: 开源中国社区

摘要:   这篇文章和你一起过下Netty的主发行版本的一些显著的改变和新特性,让你在把你的应用程序转换到新版本的时候有个概念。 项目结构改变   Netty的包名从org.jboss.netty改为io.netty,因为我们不在是JBoss.org的 ...
简化的关闭

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 API

bootstrap 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自动创建。

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。

Channel ch = ...;
ChannelPipeline p = ch.pipeline();
EventExecutor e1 = new DefaultEventExecutor(16);
EventExecutor e2 = new DefaultEventExecutor(8);

p.addLast(new MyProtocolCodec());
p.addLast(e1, new MyDatabaseAccessingHandler());
p.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消息中总生成多个消息对象:

1       * HttpRequest / HttpResponse
0 - n * HttpContent
1 * 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包里。请浏览示例的源代码来看下每一处的变化。

移植服务端

  1. 重写FactorialServer.run()方法来使用新的 bootstrap API。
    1. 不再有ChannelFactory了。 由你自己去实例化NioEventLoop(一个是用来接收接入的链接,另外的就用来处理接收到的链接)。
  2. 从命名FactorialServerPipelineFactory为FactorialServerInitializer。
    1. 让它继承ChannelInitializer<Channel>。
    2. 取代创建新的ChannelPipeline,通过Channel.pipeline()来获取。
  3. 让FactorialServerHandler继承sChannelInboundMessageHandlerAdapter<BigInteger>。
    1. 用channelInactive()来替换channelDisconnected()。
    2. handleUpstream()不能再使用了。
  4. 让BigIntegerDecoder继承ByteToMessageDecoder<BigInteger>。
  5. 让NumberEncoder继承MessageToByteEncoder<Number>。
    1. encode()不在返回一个缓存了。由ByteToMessageDecoder来提供填充编码好的数据到缓存里。

移植客户端

  大部分和移植服务端差不多,但你要在你编写一个潜在的大数据流时要多注意下。

  1. 重写FactorialClient.run()方法来使用新的bootstrap API。
  2. 重命名FactorialClientPipelineFactory为FactorialClientInitializer。
  3. 使FactorialClientHandler继承ChannelInboundMessageHandlerAdapter<BigInteger>
    1. 在这一点,你发现在4.0里没有了Channel.isWritable()或者channelInterestChanged()。作为代替,你自己来管理那些未决定的写操作。新的sendNumbers()看起来如下:
      private void sendNumbers() {
    // Do not send more than 4096 numbers.
    boolean finished = false;
    MessageBuf<Object> out = ctx.nextOutboundMessageBuffer();
    while (out.size() < 4096) {
    if (i <= count) {
    out.add(Integer.valueOf(i));
    i ++;
    } else {
    finished = true;
    break;
    }
    }

    ChannelFuture f = ctx.flush();
    if (!finished) {
    f.addListener(numberSender);
    }
    }

    private final ChannelFutureListener numberSender = new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
    if (future.isSuccess()) {
    sendNumbers();
    }
    }
    };


酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部