nio之netty5应用
1、netty5和netty4的区别不是很大,但是与netty3差别还是有的。这里不介绍netty4,因为和netty5的方式都差不多。netty5的复杂性相对于netty3要多很多了。基本上架构都被重构了。所以这里主要是介绍一些属性和用法。
2、核心的变化主要有:
支持Android
提供了:
- 移动设备变成更加强大
- 通过Ice Cream Sandwich解决了在ADK中最著名的与NIO和SSLEngine相关的问题,且
- 用户显然想要重用他们应用中的的编解码和处理器代码。
我们决定官方支持Android(4.0及以上版本)
简化处理器层次
ChannelInboundHandler和ChannelOutboundHandler整合为ChannelHandler。ChannelHandler现在包含输入和输出的处理方法。
ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter和ChannelDuplexHandlerAdapter已被废弃,由 ChannelHandlerAdapter代替。
由于现在无法区分处理器(handler) 是输入还是输出的处理器,CombinedChannelDuplexHandler现在由 ChannelHandlerAppender代替。
更多相关变化,可参考https://github.com/netty/netty/pull/1999
channelRead0() → messageReceived()
我知道。这是一个愚蠢的错误。如果你使用了SimpleChannelInboundHandler,你需要把channelRead0()重命名为messageReceived()。
废弃中移除的
Channel.deregister()已被移除。不再生效和被使用。取而代之的,我们将允许Channel被充注册到不同的事件循环。
ChannelHandlerContext.attr(..) == Channel.attr(..)
Channel和ChannelHandlerContext类都实现了AttributeMap接口,使用户可以在其上关联一个或多个属性。有时会让用户感到困惑的是Channel和ChannelHandlerContext都有其自己的存储用户定义属性的容器。例如,即使你通过Channel.attr(KEY_X).set(valueX)给属性'KEY_X’赋值,你却无法通过ChannelHandlerContext.attr(KEY_X).get()方法获取到值。反之亦是如此。这种行为不仅仅令人不解而且还浪费内存。
为了解决这个问题,我们决定每个Channel内部仅保留一个map。AttributeMap总是用AttributeKey作为它的key。AttributeKey确保键的唯一性,因此每个Channel中如果存在一个以上的属性容易是多余的。只要用户把他自己的AttributeKey定义成ChannelHandler的private static final变量,就不会有出现重复key的风险。
更简单更精确的缓冲区泄漏追踪
之前,查找缓冲区泄漏是很困难的,并且泄漏的警告信息也不是很有帮助。现在我们有了增强的泄漏报告机制,该机制会在增长超过上限时触发。
更多的信息可查看:http://netty.io/wiki/reference-counted-objects.html 。由于该特性十分重要,所以也移植入了4..0.14.Final版中。
PooledByteBufAllocator成为默认的allocator
在4.x版本中,UnpooledByteBufAllocator是默认的allocator,尽管其存在某些限制。现在PooledByteBufAllocator已经广泛使用一段时间,并且我们有了增强的缓冲区泄漏追踪机制,所以是时候让PooledByteBufAllocator成为默认了。
全局唯一的Channel ID
每个Channel现在有了全局唯一的ID,其生成的依据是:
* MAC地址(EUI-48或是EUI-64),最好是全局唯一的,
* 当前进程的ID
* System#currentTimeMillis()
* System#nanoTime()
* 随机的32位整数,以及
* 系列递增的32位整数
可通过Channel.id()方法获取Channel的ID。
更灵活的线程模型
增加了新的ChannelHandlerInvoker接口,用于使用户可以选择使用哪个线程调用事件处理方法。替代之前的在向ChannelPipeline添加 ChannelHandler时指定一个EventExecutor的方式,使用该特性需要指定一个用户自定义的 ChannelHandlerInvoker实现。
EmbeddedChannel的易用性
EmbeddedChannel中的readInbound()和readOutbound()方法返回专门类型的参数,因此你不必在转换他们的返回值。这可以简化你的测试用例代码。
EmbeddedChannel ch = ...; // BEFORE: FullHttpRequest req = (FullHttpRequest) ch.readInbound(); // AFTER: FullHttpRequest req = ch.readInbound();
使用Executor代替ThreadFactory
有些应用要求用户使用Executor运行他们的任务。4.x版本要求用户在创建事件循环(event loop)时指定ThreadFacotry,现在不再是这样了。
关于该变化的更多信息,可参考:https://github.com/netty/netty/pull/1762
Class loader友好化
一些类型,如AttributeKey对于在容器环境下运行的应用是不友好的,现在不是了。
编解码和处理器(handlers)
* XmlFrameDecoder支持流式的XML文档
* 二进制的memcache协议编解码
* 支持SPDY/3.1 (也移植到了4.x版本)
* 重构了HTTP多部分的编解码
3、这里写了一个简单的实现过程,来区分netty5和netty3的写法
1)服务类和处理类
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; public class Server { public static void main(String[] args) { //服务类 ServerBootstrap serverBootstrap = new ServerBootstrap(); //声明两个线程池 EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup work = new NioEventLoopGroup(); try { //设置线程组 serverBootstrap.group(boss,work); //设置服务socket工厂 serverBootstrap.channel(NioServerSocketChannel.class); //设置管道 serverBootstrap.childHandler(new ChannelInitializer<Channel>() { protected void initChannel(Channel channel) throws Exception { channel.pipeline().addLast(new StringDecoder()); channel.pipeline().addLast(new StringEncoder()); channel.pipeline().addLast(new ServerHandler()); } }); //设置服务器连接数 serverBootstrap.option(ChannelOption.SO_BACKLOG,2048); //设置tcp延迟状态 serverBootstrap.option(ChannelOption.TCP_NODELAY,true); //设置激活状态,2小时清除 serverBootstrap.option(ChannelOption.SO_KEEPALIVE,true); //监听端口 ChannelFuture channelFuture = serverBootstrap.bind(9000); //等待服务器关闭 channelFuture.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { //关闭线程池 boss.shutdownGracefully(); work.shutdownGracefully(); } } }
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class ServerHandler extends SimpleChannelInboundHandler<String> { //接收消息并处理 protected void messageReceived(ChannelHandlerContext channelHandlerContext, String s) throws Exception { System.out.println(s); channelHandlerContext.writeAndFlush("hello client"); } }
2)客户端和处理类
import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.net.InetSocketAddress; import java.util.Scanner; //单个客户端 public class Client { public static void main(String[] args) { //服务类 Bootstrap bootstrap = new Bootstrap(); //线程池 EventLoopGroup client = new NioEventLoopGroup(); try { //设置线程组 bootstrap.group(client); //设置socket工厂 bootstrap.channel(NioSocketChannel.class); //设置管道处理 bootstrap.handler(new ChannelInitializer<Channel>() { protected void initChannel(Channel channel) throws Exception { channel.pipeline().addLast(new StringEncoder()); channel.pipeline().addLast(new StringDecoder()); channel.pipeline().addLast(new ClientHandler()); } }); //连接服务器 ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress("localhost", 9000)); //输入信息测试 Scanner scanner = new Scanner(System.in); while (true) { String msg = scanner.nextLine(); channelFuture.channel().writeAndFlush(msg); } } catch (Exception e) { e.printStackTrace(); } finally { //关闭线程池 client.shutdownGracefully(); } } }
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class ClientHandler extends SimpleChannelInboundHandler<String>{ //接收消息 protected void messageReceived(ChannelHandlerContext channelHandlerContext, String s) throws Exception { System.out.println(s); } }
3)多个客户端
import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.concurrent.atomic.AtomicInteger; //多个客户端连接 public class MClient { //服务类 private Bootstrap bootstrap = new Bootstrap(); //因为线程组里面是线程安全的,所以这里采用数组的形式 private List<Channel> channels = new ArrayList<Channel>(); //原子类,用于计数 private AtomicInteger atomicInteger = new AtomicInteger(); //初始化操作 public void init(int count) { //声明线程池 EventLoopGroup client = new NioEventLoopGroup(); //设置线程组 bootstrap.group(client); //设置socket工厂 bootstrap.channel(NioSocketChannel.class); //设置管道 bootstrap.handler(new ChannelInitializer<Channel>() { protected void initChannel(Channel channel) throws Exception { channel.pipeline().addLast(new StringEncoder()); channel.pipeline().addLast(new StringDecoder()); channel.pipeline().addLast(new ClientHandler()); } }); //初始化连接 for (int i = 0; i < count; i++) { ChannelFuture channelFuture = bootstrap.connect("10.0.20.154", 9000); channels.add(channelFuture.channel()); } } //获取下一个channel public Channel nextChannel() { return this.getChannel(0); } private Channel getChannel(int n) { //平均获取其中的channel Channel channel = channels.get(Math.abs(atomicInteger.getAndIncrement() % channels.size())); //判断是否连接 if (!channel.isActive()) { //如果没有连接,选择激活替换 channels.set(channels.indexOf(channel), bootstrap.connect("10.0.20.154", 9000).channel()); //超出总长度,直接抛异常 if (n>=channels.size()) { throw new RuntimeException("no use channel"); } //递归,目的是获取下一个,直到数组没有channel return getChannel(n+1); } return channel; } public static void main(String[] args) { MClient mClient = new MClient(); mClient.init(5); Scanner scanner = new Scanner(System.in); while (true) { try { String msg = scanner.nextLine(); mClient.nextChannel().writeAndFlush(msg); } catch (Exception e) { e.printStackTrace(); } } } }