近期在做充电桩项目,桩设备与系统之间的连接方式,一种是基于Socket协议,一种是Mqtt这种消息中间件的,当初为了快,直接用socket搞了个异步处理接收桩,在少量问题可能对系统不会有太大问题
可随着桩数量增加,就应该用更高效的框架了,比如netty。继IO整理之后,整理一篇关于Netty的文章。
了解更多JAVA后台知识整理:JAVA后台系列目录
1. Netty中的反应器模式实现
反应器模式(Reactor模式)的应用场景:Nginx、Redis、Netty
组成:Reactor反应器线程:负责响应IO事件,并且分发到Handlers处理器
Handlers处理器:非阻塞的执行业务处理逻辑
Socket 对于每一个新的网络连接都分配给一个线程,对于大量连接时这个是致命的缺陷
在反应器模式中,需要进行attach和attachment结合使用:在选择键注册完成之后,调用attach方法,将Handler实例绑定到选择键;
当IO事件发生时,调用attachment方法,可以从选择键取出Handler实例,将事件分发到Handler处理器中,完成业务处理
Netty对于Channel进行了封装,底层依赖了NIO的成员SelectableChannel
Netty中反应器的组件:NioEventLoopGroup 相当于NIO中的Selector和Thread的结合,可以拥有线程监听的两种功效
Handler: 出站和入站两种类型
Netty的Pipeline通道处理流水线
ChannelPipeline:一条双向链表,类似水流一样的流动,从一边流向另一边,可以自然停止和主动停止两种方式,并且双向流动
2. Bootstrap引导类
父子通道的感念:NioServerSocketChannel(服务器端)、NioSocketChannel(客户端)
EventLoopGroup轮询组:就是两个监听Boss和worker的关系
启动案例代码:
public class NettyDiscardServer {
private final int serverPort;
ServerBootstrap b = new ServerBootstrap();
public NettyDiscardServer(int port) {
this.serverPort = port;
}
public void runServer() {
//创建reactor 线程组
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
EventLoopGroup workerLoopGroup = new NioEventLoopGroup();
try {
//1 设置reactor 线程组
b.group(bossLoopGroup, workerLoopGroup);
//2 设置nio类型的channel
b.channel(NioServerSocketChannel.class);
//3 设置监听端口
b.localAddress(serverPort);
//4 设置通道的参数
b.option(ChannelOption.SO_KEEPALIVE, true);
b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
//5 装配子通道流水线
b.childHandler(new ChannelInitializer<SocketChannel>() {
//有连接到达时会创建一个channel
protected void initChannel(SocketChannel ch) throws Exception {
// pipeline管理子通道channel中的Handler
// 向子channel流水线添加一个handler处理器
ch.pipeline().addLast(new NettyDiscardHandler());
}
});
// 6 开始绑定server
// 通过调用sync同步方法阻塞直到绑定成功
ChannelFuture channelFuture = b.bind().sync();
Logger.info(" 服务器启动成功,监听端口: " + channelFuture.channel().localAddress());
// 7 等待通道关闭的异步任务结束
// 服务监听通道会一直等待通道关闭的异步任务结束
ChannelFuture closeFuture = channelFuture.channel().closeFuture();
closeFuture.sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8 优雅关闭EventLoopGroup,
// 释放掉所有资源包括创建的线程
workerLoopGroup.shutdownGracefully();
bossLoopGroup.shutdownGracefully();
}
}
ChannelOption通道选项:
SO_RCVBUF和SO_SNDBUF、TCP_NODELAY、SO_KEEPALIVE、SO_REUSEADDR、SO_LINGER、SO_BACKLOG、SO_BROADCAST
3. Channel通道
主要方法说明:
1. ChannelFuture connect(SocketAddress address) 连接远程服务器,客户端使用
2. ChannelFuture bind(SocketAddress address)服务器端监听使用
3. ChannelFuture close() 关闭连接
4. Channel read() 读取数据入站处理
5. ChannelFuture write(Object o)启用出站处理
6. Channel flush() 缓冲区写到对端
EmbeddedChannel仅仅是模拟入站与出站的操作,包含writeInbound和writeOutbound方法,测试时很好用的一个工具
4. Handler业务处理器
入站处理器主要方法
执行流程:handlerAdded()→ channelRegistered() →channelActive() →数据传输的入站回调→ channelInactive()→ channelUnregistered() →handlerRemoved()
出站处理器主要方法
ChannelInitializer通道初始化处理器,输入入站拿到新连接通道作为实际参数,往它的流水线中装配Handler业务处理器
5. PipeLine通道流水线
流水线入站顺序调用,出站逆序调用,就好比一列火车,每节车厢在进入火车站,然后火车掉头,从另一个反向出站
Channel、Handler、ChannelHandlerContext三者的关系:Channel通道拥有一条ChannelPipeline通道流水线,
每一个流水线节点为一个ChannelHandlerContext上下文对象,每一个上下文中包裹了一个ChannelHandler通道处理器。
HeadContext与TailContext头尾上下文
6. ByteBuf缓冲区
ByteBuf缓冲区组件来替代Java NIO的ByteBuffer缓冲区组件
优势:
Pooling (池化,这点减少了内存复制和GC,提升了效率);复合缓冲区类型,支持零复制;不需要调用flip()方法去切换读/写模式;可扩展性好
可以自定义缓冲区类型;读取和写入索引分开;方法的链式调用; 可以进行引用计数,方便重复使用
四个组成
第一个部分是已用字节,表示已经使用完的废弃的无效字节;
第二部分是可读字节,这部分数据是ByteBuf保存的有效数据,从ByteBuf中读取的数据都来自这一部分;
第三部分是可写字节,写入到ByteBuf的数据都会写到这一部分中;
第四部分是可扩容字节,表示的是该ByteBuf最多还能扩容的大小。
三个属性
readerIndex(读指针)
writerIndex(写指针)
maxCapacity(最大容量)
三组方法
容量方法
写入方法
读取系列
ByteBuf的引用计数
Allocator分配器PoolByteBufAllocator和UnpooledByteBufAllocator,在大量连接情况下,池化分配器可以减少内存的持续消耗
缓冲区的类型
7. 核心组件 编码器 和解码器
ByteToMessageDecoder 抽象类,在设计上使用了模板模式,decode抽象方法
ByteToMessageCodec抽象类上的encode方法
8. 粘包、拆包统称半包问题
什么是半包问题:在数据发送端和接收到结果不一致
半包问题产生的原因:主要是读取操作系统缓冲区大小限制产生,读取多了就粘包了,读取少了就半包了
netty如何解决该问题的:
(1)可以自定义解码器分包器:基于ByteToMessageDecoder或者ReplayingDecoder,定义自己的用户缓冲区分包器。
(2)使用Netty内置的解码器。如可以使用Netty内置的LengthFieldBasedFrameDecoder自定义长度数据包解码器,对用户缓冲区ByteBuf进行正确的分包。
9. 序列化和反序列化 JSON和Protobuf
了解更多JAVA后台知识整理:JAVA后台系列目录