设为首页收藏本站

LUPA开源社区

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

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

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

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

每个处理器的缓存

不像3.x那样在每次读操作都简历一个新堆里的缓存来触发上游的MessageEvent,4.0不会每次都创建新的 缓存。它直接从socket中读取数据到由用户的ChannelInboundByteHandler和ChannelInboundMessageHandler实现创建的入站缓存。

因为由上述处理器创建的入站缓存直到关联的通道关闭前都会重用,所以在上面的GC和内存带宽消耗都能保持较小。同样,当接收到的数据被销毁时用户已经 完成操作,codec的实现就变得更简单和有效了

在创建出站缓存时也是差不多的(不会新建)。用户的ChannelOutBoundBYteHandler和ChannelOutboundMessageHandler来操作。

不需要每条消息都有一个事件

4.0里不再有了messageReceived或
writeRequested处理器方法。它们被inboundBufferUpdated和flush代替了。用户的入队一个或多个消息到一个入站(或
出站)缓存同时会出发一个 inboundBUfferUpdated(或flush)事件。

public void inboundBufferUpdated(ChannelHandlerContext ctx) {
Queue<MyMessage> in = ctx.inboundMessageBuffer();
Queue<MyNewMessage> out = ctx.nextInboundMessageBuffer();
for (;;) {
MyMessage m = in.poll();
if (m == null) {
break;
}
MyNewMessage decoded = decode(m);
out.add(decoded);
}
ctx.fireInboundBufferUpdated();
}

public void flush(ChannelHandlerContext ctx, ChannelFuture future) {
Queue<MyNewMessage> in = ctx.outboundMessageBuffer();
Queue<MyMessage> out = ctx.nextOutboundMessageBuffer();
for (;;) {
MyNewMessage m = in.poll();
if (m == null) {
break;
}
MyMessage encoded = encode(m);
out.add(encoded);
}
ctx.flush(future);
}
作为选择,用户能够在每个单独的入站(或出站)消息中触发这样的事件来模拟老的行为,尽管相对新方法来说效率更低。

消息处理器 vs. 字节处理器

在3.x里一个MessageEvent持有一个任意的对象。它能够是一个ChannelBuffer或是一个用户自定义的对象,它们都是同样对待的:

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent evt) {
Object msg = evt.getMessage();
if (msg instanceof ChannelBuffer) {
ChannelBuffer buf = (ChannelBuffer) msg;
...
} else {
MyMessage myMsg = (MyMessage) msg;
...
}
}
在4.0里,它们就分别对待了,因为一个处理器不再处理一个独立的消息,而是处理多种多样的消息:
public void inboundBufferUpdated(ChannelHandlerContext ctx) {
if (ctx.hasInboundByteBuffer()) {
ByteBuf buf = ctx.inboundByteBuffer();
...
} else {
Queue<MyMessage> buf = ctx.inboundMessageBuffer();
for (;;) {
MyMessage msg = buf.poll();
if (buf == null) {
break;
}
...
}
}
}
你可能发现一个ServerChannel的处理器是一个入站缓存是Queue<Channel>的入站处理器是较为有趣的。

处理器适配器

大多数用户都发现创建和管理它的生命周期是繁琐的,因此它支持用户扩展预定义好的适配器类来使得更方便:

  • ChannelHandlerAdapter
  • ChannelStateHandlerAdapter
  • ChannelOperationHandlerAdapter
  • ChannelInboundMessageHandlerAdapter
  • ChannelInboundByteHandlerAdapter
  • ChannelOutboundMessageHandlerAdapter
  • ChannelOutboundByteHandlerAdapter

明智的和不易出错的入站流量挂起

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的时候关闭链接。

要支持半关闭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套接字上创建一个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。

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() { ... });
}
}


酷毙

雷人

鲜花

鸡蛋

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

最新评论

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

返回顶部