数据结构1:
BOSS线程池:
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
BOSS线程池中有一条线程工作,就是发现新的连接。 所以一个线程就够了。
Netty是支持同时监听多个端口的,所以BOSS线程池的大小按照需要监听的服务器端口数量进行设置就行了。 一般是1
工作线程池:
EventLoopGroup workLoogGroup = new NioEventLoopGroup(processorsNumber * 2, threadFactory, SelectorProvider.provider());
work线程池的大小,这个大小一般是物理机器/虚拟机器 可用内核的个数 * 2
work线程池中的线程(如果封装的是JAVA NIO,那么具体的线程实现类就是NioEventLoop)
负责 channel 事件监听,并根据事件状态,调用不同的事件就绪后的处理方法 ChannelHandler。
两个线程池的设计 把nio 中selector的角色分成针对OP_ACCEPT 事件是BOSS线程来执行监听的。
而读写事件是用work线程来轮询监听的。
所以说netty 封装了一个线程池来处理 数据读写事件就绪 以及 真实的读写事件。
/指定Netty的Boss线程和work线程 和配置nio模型
serverBootstrap.group(bossLoopGroup, workLoogGroup);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
serverBootstrap.bind(new InetSocketAddress("0.0.0.0", 83));
具体使用流程是:(基于nio模型)
1. BOSS线程负责发现连接到服务器的新的channel(类比nio中SocketServerChannel的ACCEPT事件),并且将这个channel经过检查后 注册到WORK连接池 的某个线程中。
2. WORK线程 发现操作系统有一个它感兴趣的IO事件就绪时(类比nio中SocketChannel的READ事件)则调用相应的ChannelHandler事件。当某个channel失效后(例如显示调用ctx.close())这个channel将从绑定的EventLoop中被剔除。(nio中需要自己编写方法,进行读写操作。)
3. 一个Work线程池的线程将按照底层nio的Selector的事件状态,决定执行ChannelHandler中的哪一个事件方法 channelRegistered、channelUnregistered、channelActive、channelInactive。 重要的是channelRead channelReadComplete这两个。
TCPServerHandler 继承ChannelInboundHandlerAdapter 作为助手来提供以上方法。这个助手要注册在每个channel中。
4. 执行完成后,work线程将一直轮询 直到该work线程所管理的channel 发生了新的IO事件。
所以nio默认没有提供线程池,真正的读写事件需要自己使用线程池来做。 而netty就默认提供线程池 worker线程。
数据结构2:
Channel:
Channel 其实就是每个socket连接的封装。
每一个channel,都有独立的handler、ChannelHandlerContext、ChannelPipeline、Attribute
每个ChannelPipeline 都可以添加多个handler。用来对该channel 做处理。
ChannelHandler:
handler这种叫法就是给用户提供处理的地方。
在ChannelPipeline中,有若干的过滤器。我们称之为“ChannelHandler”(处理器或者过滤器)。
用于处理/过滤 流入数据的ChannelHandler,称之为“ChannelInboundHandler”
用于处理/过滤 流出数据的ChannelHandler,称之为“ChannelOutboundHandler”
数据在ChannelPipeline中有一个一个的Handler进行处理,并形成一个新的数据状态
实现我们自己业务的“数据流入处理器:
我们可以实现ChannelInboundHandler接口或者ChannelOutboundHandler接口,
来实现我们自己业务的“数据流入处理器”或者“数据流出”处理器。
消息协议:
也采用ChannelHandler的形式通过ChannelPipeline 来添加。
具体有几种:
HttpRequestDecoder/HttpResponseEncoder:
实现了Http协议的数据输入格式的解析。这个类将数据编码为HttpMessage对象,并交由下一个ChannelHandler进行处理。 反之将 服务器端HttpReponse对象的描述转换成ByteBuf对象形式
ByteArrayDecoder/ByteArrayDecoder :
最基础的数据流输入处理器,将所有的byte转换为ByteBuf对象
(一般的实现类是:io.netty.buffer.UnpooledUnsafeDirectByteBuf)。我们进行一般的文本格式信息传输到服务器时,最好使用这个Handler将byte数组转换为ByteBuf对象。
DelimiterBasedFrameDecoder:
这个数据流输入处理器,会按照外部传入的数据中给定的某个关键字符/关键字符串,重新将数据组装为新的段落并发送给下一个Handler处理器。后文中,我们将使用这个处理器进行TCP半包的问题。
支持标准数据格式解析的处理器:
例如支持Google Protocol Buffers 数据格式解析的ProtobufDecoder和ProtobufVarint32FrameDecoder处理器。
还有支持标准的编码成Google Protocol Buffers格式、JBoss Marshalling 格式、ZIP压缩格式的ProtobufEncoder、ProtobufVarint32LengthFieldPrepender、MarshallingEncoder、JZlibEncoder等
Protobuf数据协议的集成:Netty利用自身的Channelpipeline的设计,对Protobuf数据协议进行了无缝结合。
Boss Marshalling数据协议的集成:JBoss Marshalling 是一个Java对象的序列化API包,修正了JDK自带的序列化包的很多问题,又保持跟 java.io.Serializable 接口的兼容。Netty通过封装这个协议,可以帮助我们在客户端-服务端简便的进行对象系列化和反序列化。
HTTP Request / HTTP Response 协议的集成:在Netty中,可以方便的接受和发送Http协议。也就是说,我们可以使用Netty搭建自己的WEB服务器,当然您还可以根据自己的业务要求,方便的设计类似于Struts、Spring MVC这样的WEB框架。
nio 与 netty对比。
“技术层”框架本身只对IO模型技术实现进行了封装,并不关心IO模型中流淌的数据格式
“业务层”框架对数据格式也进行了处理,线程池进行了处理,让我们可以抽出精力关注业务本身。