Netty学习笔记
何为Netty
Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
何为事件驱动
事件处理的两道工序:
方法 | 说明 |
---|---|
事件分离器(Event Demultiplexer) | 将事件源(socket/file)的I/O时间分离出来(IO就绪事件,IO完成事件),并分传递到对应的I/O事件处理器。 |
事件处理器(Event Handler) | 应用预先注册需要处理的事件及其事件处理器(回调函数)。 |
事件分离器的两种模式:
模式 | 说明 |
---|---|
Reactor | 事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。I/O操作由应用完成,调用事件处理器时,表示I/O就绪。 |
Proactor | demultiplexor事件处理器负责发起异步I/O操作,将用户定义的数据缓冲区地址和数据大小传递给操作系统,操作系统进行I/O操作,事件分离器捕获IO操作完成事件,然后将事件传递给对应处理器。I/O操作由系统完成,调用事件处理器时,表示I/O完成。 |
何为异步I/O
常见的I/O模式有一下几种,Netty是多路复用模式。假设场景:用户想从水管管道取5000L水,但是这个水管的水流是断断续续的,那么他有以下几种方法:
模式 | 大白话 | 说明 |
---|---|---|
阻塞 I/O(blocking IO) | 他打开水龙头,在必须原地等待水量达到5000L,然后这个任务才完成。 | 阻塞,同步I/O |
非阻塞 I/O(nonblocking IO) | 他打开水龙头,然后去做别的事情,两分钟过来查看一次(轮询),直到水位达到500L,然后这个任务才完成。 | 非阻塞,同步I/O |
I/O 多路复用( IO multiplexing)(事件驱动I/O) | 他加装多个水龙头,然后在水龙头的前端安装水滴传感器(阻塞),他在后台轮询各个传感器的数据,一旦得到A水龙头的水滴传感器已经被触发时,他就打开水龙头A接水,有一下几种机制select,epoll,iocp,kqueue,poll。 | 非阻塞,同步I/O |
信号驱动 I/O( signal driven IO) | 他加装多个水龙头,然后在水龙头的前端安装水滴传感器,当水龙头A的水滴传感器触发时,主动通知他,他就打开水龙头A接水。 | 非阻塞,同步I/O |
异步 I/O(asynchronous IO) | 他加装多个水龙头,然后在水龙头的前端安装水量传感器,并且将水龙头打开,然后他就去做其他的事情了,当水来时,水量传感器统计已取的水量,达到5000L时,将多个水桶的水汇聚到一个桶里,让他直接使用。 | 非阻塞,异步I/O |
I/O多路复用模式
模式 | 方法 | 说明 | 最大连接数 | 效率 | 消息传递方式 |
---|---|---|---|---|---|
select | 轮询 | 同步非阻塞 | FD_SETSIZE宏定义 | FD的增加会造成线性遍历速度慢的“线性下降性能问题” | 内核需要将消息传递到用户空间,都需要内核拷贝动作 |
kQueue | 回调 | 同步非阻塞 | 与epoll相似 | 与epoll相似 | 与epoll相似 |
iocp | 通知 | 异步 | 待了解 | 待了解 | 待了解 |
poll | 轮询 | 同步非阻塞 | 基于链表来存储没有最大连接数的限制 | 与select一样 | 与select一样 |
ePoll | 回调 | 同步非阻塞 | 连接数有上限,效率与连接数成反比 | socket活跃大会有性能问题 | 通过内核和用户空间共享一块内存来实现 |
Netty示例解析
以下是一个经典示例:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.mqtt.MqttDecoder;
import io.netty.handler.codec.mqtt.MqttEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.yb.iot.mqtt.handler.ChannelPipelineHandler;
import org.yb.iot.mqtt.handler.InboundMessageHandler;
import java.util.concurrent.TimeUnit;
public class NettyServer {
public static void main(String[] args) {
/**
* NioEventLoopGroup是处理I / O操作的多线程事件循环,多种实现可选。
* 使用多少个线程以及如何将它们映射到创建的通道取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。
* 在上文中提到,I/O多路复用机制有多种,Netty中提供了多种实现,,以Group尾缀的为多线程实现。
*/
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
/**
* 设置服务器的帮助程序类。
*/
ServerBootstrap serverBootstrap = new ServerBootstrap();
/**
* bossGroup通常称为“老板”,接受传入的连接。
* workerGroup通常称为“工人”,一旦老板接受连接并将注册的连接注册给工人,便处理已接受连接的流量。
*/
serverBootstrap.group(bossGroup, workerGroup)
/**
* 指定IO模型
*/
.channel(NioServerSocketChannel.class)
/**
* 配置自定义的事件处理器。
*/
.childHandler(new ChannelInitializer<SocketChannel>() {
@Autowired
private InboundMessageHandler inboundMessageHandler;
/**
* 初始化channel模型的上下文
* @param socketChannel
* @throws Exception
*/
@Override
public void initChannel(SocketChannel socketChannel) {
/**
* 事件处理器责任链,可以动态增删
* 可以进行例如协议编码解码,心跳,内容长度,粘包分包处理,读写处理等,
* Netty已经默认提供了多种标准协议的解码编码实现,全都在io.netty.handler.codec.*下,如MQTT,HTTP等。
*/
ChannelPipeline pipeline = socketChannel.pipeline();
/** 最大内容长度 */
pipeline.addLast(new HttpObjectAggregator(1024 * 1024 * 64));
/** 心跳时间 */
pipeline.addLast("idleStateHandler", new IdleStateHandler(120, 120, 120, TimeUnit.SECONDS));
/** 协议编码 */
pipeline.addLast(MqttEncoder.INSTANCE);
/** 协议解码 */
pipeline.addLast(new MqttDecoder(1024 * 1024 * 64));
/** 消息处理 */
pipeline.addLast(inboundMessageHandler);
}
})
/**
* 配置EventLoopGroup模型的参数
*/
.option(ChannelOption.SO_BACKLOG, 128)
/**
* 配置Channel模型的参数
*/
.childOption(ChannelOption.SO_KEEPALIVE, true);
/**
* 绑定并开始接受传入的连接,ChannelFuture支持I/O完成事件监听。
*/
ChannelFuture channelFuture = serverBootstrap.bind(1883).sync();
/**
* 注册等待Socket服务器关闭。
*/
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**
* 优雅的关闭并退出服务
*/
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
EventLoopGroup与Channel的实现对应关系如下:
单线程模型 | 多线程模型/主从线程模型 | 通道模型 | 模式 |
---|---|---|---|
EpollEventLoop.class | EpollEventLoopGroup.class | EpollServerSocketChannel.class | epoll |
KQueueEventLoop.class | KQueueEventLoopGroup.class | KQueueServerSocketChannel.class | kQueue |
NioEventLoop.class | NioEventLoopGroup.class | NioServerSocketChannel.class | select |
Netty重要组件:
类 | 说明 |
---|---|
Channel | 管道对象 |
ChannelPipeline | 管道事件处理器责任链 |
ChannelHandler | 管道事件处理器的接口 |
ChannelHandlerContext | 管道事件处理器的上下文 |
ChannelFuture | 管道异步操作的结果 |
ByteBuf | 管道一帧数据的对象,字节容器,通过引用计数的方式实现内存回收 |
参考文档
Netty Wiki
Netty 实战 中文版
Netty设计原理
并发编程网 Netty
linux下select/poll/epoll机制的比较
Linux IO模式及 select、poll、epoll详解
Netty 系列之 Netty 线程模型
深入研究Netty之线程模型详解
Netty学习之IO模型