为什么数据库和数据库连接池不采用类似java nio的IO多路复用技术使用一个连接来维护和数据库的数据交换? - 知乎
三种reactor模式
关于划分boss worker的理解
服务器通常会建立很多连接,比如10000个,如果对应建立10000个线程,线程切换代价大。大多数连接建立连接后不发数据或发送数据量少(所以NIO应对大文件传输时不具备优势~~),白白的占用一个线程**(阻塞也占用?)。操作系统提供多路复用epoll,一个线程管理多个连接。
netty线程模型
参数调优
linux
- /proc/sys/net/ipv4/tcp_keepalive_time
- ulimit -n
netty
- TCP_NODELAY:默认false,设置为true,不要nagle
- ChannelOption.SO_BACKLOG:1024
EventLoop
EventLoop执行任务
Netty中单独EventLoopGroup处理业务
EventLoop传递
Channel
ChannelInitializer
netty内部监听accept事件,收到accept事件后,调用initChannel()方法.
attr():保存一些信息
-
ChannelFuture同步&异步
ChannelFuture = channel.connect()
CloseFuture
ChannelHandlerContext.writeAndFlush() & NioSocketChannel.writeAndFlush()
EmbeddedChannel【测试使用】
ChannelGroup【管理一组Channel】
ByteBuf
自动扩容
- 支持两种内存:直接内存、堆内存
- ByteBuf buffer = ByteBufAllocator.DEFAULT.heapBuffer(10);
- ByteBuf buffer = ByteBufAllocator.DEFAULT.directBuffer(10); 默认
池化 - 可以重用池中 ByteBuf 实例,更节约内存,减少内存溢出的可能
- 4.1 以后,非 Android 平台默认启用池化实现,Android 平台启用非池化实现
ByteBuf 是通过引用计数的方式管理
- 每次调用 retain() 方法, 它的引用就加一
- get/set 不会改变读写指针,而 read/write 会改变读写指针
使用ctx.alloc().buffer()不要自定义buffer
decode是抽象方法
LengthFieldBasedFrameDecoder
如果收到半包,会等后面的数据。
通信协议
@Slf4j
@ChannelHandler.Sharable
public class MessageCodec extends ByteToMessageCodec<Message> {
@Override
public void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
// 1. 4 字节的魔数
out.writeBytes(new byte[]{1, 2, 3, 4});
// 2. 1 字节的版本,
out.writeByte(1);
// 3. 1 字节的序列化方式 jdk 0 , json 1
out.writeByte(0);
// 4. 1 字节的指令类型
out.writeByte(msg.getMessageType());
// 5. 4 个字节
out.writeInt(msg.getSequenceId());
// 无意义,对齐填充
out.writeByte(0xff);
// 6. 获取内容的字节数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(msg);
byte[] bytes = bos.toByteArray();
// 7. 长度
out.writeInt(bytes.length);
// 8. 写入内容
out.writeBytes(bytes);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
int magicNum = in.readInt();
byte version = in.readByte();
byte serializerType = in.readByte();
byte messageType = in.readByte();
int sequenceId = in.readInt();
in.readByte();
int length = in.readInt();
byte[] bytes = new byte[length];
in.readBytes(bytes, 0, length);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
Message message = (Message) ois.readObject();
log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerType, messageType, sequenceId, length);
log.debug("{}", message);
out.add(message);
}
}
MessageToMessageCodec二次编解码器【双向】(byte <-> object)
MessageToMessageDecoder会进行类型判断,不符合就传递给下一个handler。
StringDecoder
LineBasedFrameDecoder extends ByteToMessageDecoder 【解决粘包】
StringDecoder extends MessageToMessageDecoder
处理消息Handler
ChannelHandler的方法生命周期
客户端关闭,服务端Channel状态:
- channelActive()
- 连接数统计
- exceptionCaught(ChannelHandlerContext,Throwable):通道发生异常事件
channelInactive:Netty 是先将 Channel 关闭后,再回调 channelInactive 的
SimpleChannelInboundHandler
关注指定对象
- 释放消息。
channelRead0()
移除Handler
登录handler只用登录一次- 不用移除,使用SimpleChannelInboundHandler
处理指定请求就可以了。
合并handler
IdleStateHandler:检测假死,触发事件
- 要放在第一个handler
服务端设置读空闲检测,客户端设置写空闲检测
ChannelDuplexHandler
ChannelDuplexHandler则同时实现了ChannelInboundHandler和ChannelOutboundHandler接口。如果一个所需的ChannelHandler既要处理入站事件又要处理出站事件,推荐继承此类。
线程池执行handler
相当于tomcat中的worker?
pipeline.addLast(businessGroup, new OrderServerProcessHandler());
流量整形:GlobalChannelTrafficShapingHandler【一般不用】
接收响应
future
返回响应
ChannelPipeline执行顺序
ctx.write() & ctx.channel().write()区别
channelReadComplete【不直接flush】【不用】
- 一个读事件可能有多次读。(最多16次)
flushConsolidationHandler【增强flush】
它是flush增强,所以必须调用flush.它才帮你优化(减少你的flush次数)
其他
信息统计
堆外内存回收
开启native
高水位channel.isWritable()
ip过滤
心跳
Netty学习(五)—IdleStateHandler心跳机制_zhenyutu的博客-CSDN博客_idlestatehandler
————————————
ChannelHandler
ChannelPipeline
ChannelHandlerContext
编解码器
Netty注解
@Sharable
表示handler可以被重复add
Netty优化
减少flush,增加吞吐量
channelReadComplete
FlushConsolidationHandler
流量整形
开启native
高水位
空闲检测
- 服务端10s收不到请求就断掉。
- 客户端write idle check + keepalive
服务端:serverIdleCheckHandler
客户端:
ClientIdleCheckHandler
ClientIdleCheckHandler会捕获write idle,触发事件