Netty怎样切换三种IO模式
- 什么是经典三种IO模式 | 排队打饭 | BIO | jdk1.4之前 | | —- | —- | —- | | 点单、等待被叫 | NIO | jdk1.4(java.nio) | | 包厢模式(点菜、菜好了端上来) | AIO | jdk1.7 |
异步IO和同步的区别在于:菜好了谁来端的问题,模式二是得知菜烧好了客户自己停止在做的事去端菜;而模式三是客户一直做别的事,餐厅会把做好的菜端上来(就绪的io通过回调函数返回数据给应用进程)
- Netty对三种IO的支持
NIO | ||||
---|---|---|---|---|
Common | Linux | MacOS/BSD | ||
NioEventLoopGroup | EpollEventLoopGroup | KQueueEventLoopGroup | ||
NioEventLoop | EpollEventLoop | KQueueEventLoop | ||
NioServerSocketChannel | EpollSeverSocketChannel | KQueueServerSocketChannel | ||
NioSocketChannel | EpollSocketChannel | KQueueSocketChannel |
- 为什么netty仅支持NIO了
- 为什么不使用BIO
连接数高的情况下:阻塞->耗资源、效率低下
- 为什么移除AIO支持
- windows对AIO实现成熟,但很少用做服务器
- Linux AIO实现不够成熟
- Linux下AIO相较NIO性能提升不明显
- 为什么Netty有多种IO实现
通用的NIO实现(Common)在Linux下也是使用epoll,为什么要自己单独实现。
实现更好:
- netty暴露更多可控参数如
- jdk的NIO默认实现是水平触发
- netty是边缘触发和水平触发可切换
- netty实现的垃圾回收更少、性能更好
- NIO一定优于BIO么
BIO的优点:
- BIO代码简单
- 当连接数非常少、并发度低,BIO性能不输NIO.
Netty如何支持三种Reactor?
- 什么是Reactor及其三种版本
BIO | NIO | AIO |
---|---|---|
Thread-Per-Connection | Reactor | Proactor |
- 如何在Netty中使用Reactor模式
- 解析Netty对Reactor模式支持的常见疑问
- Netty如何支持主从Reactor模式的
// MainReactor
EventLoopGroup bossGroup = new NioEventLoopGroup();
// SubReactor
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
将serverSocketChannel注册绑定到bossGroup, serverSocketChannel创建的子socketChannel绑定到workerGroup.
- 为什么说netty的main reactor大多并不能用到一个线程组,只能线程组里一个?
- Netty给Channel分配Nio Event loop的规则是什么
- 通用模式的NIO实现多路复用器是怎么跨平台的
netty解决TCP粘包、半包
- 为什么tcp会出现粘包、半包问题?
粘包的主要原因:
- 发送方写入数据 < 套接字缓冲区大小
- 接收方读取套接字缓冲区数据不够及时
半包的主要原因:
- 发送方写入的数据 > 套接字缓冲区大小
- 发送的数据大于协议的MTU(最大传输单元),必须折包
从收发角度:
一个发送可以被多次接收,多个发送可能被一次接收
从传输角度:
一个发送可能占用多个传输包,多个发送可能共用一个传输包
根本原因: TCP是流式协议,消息无边界。
- 如何解决粘包和半包?
根本解决手段:找出消息边界固定长度 是指不足MTU的传统空符占位补齐.
netty的解决实现
常用二次编解码
- 为什么需要二次解码
- 一次编码器:ByteToMessageDecoder
解决粘包半包的问题 ByteBuf -> ByteBuf
- 二次编码器:MessageToMessageDecoder
ByteBuf -> java.Object
常用的二次编解码方式
- java序列化
- Marshaling
- XML
- Json
- MessagePack 占用空间少,可读性一般
- Protobuf 占用空间更少,可读性差
- 其他
选择编解码方式的要点
- 空间: 编码后占用空间
- 时间:编解码速度
- 可读性
- 多语言支持
注:这里的keepalive和tcp的KeepAlive原理相同,但和http的header中的keepavlie有本质区别,Http的keepAlive是区分长连接短连接
情景模拟
假如你是餐馆老板,有客户打电话订餐,当客户点了几个食物后,突然不说话了,
这时候,你会稍微等待一段时间倾听和等待客户再次说话(idle检测),
等待20秒后,客户还是没说话,这时候你基本认定客户那边可能出现状况(idle状态),
接下来,你会问:你还在吗?(keepalive_time: 一定时间后尝试询问连接)
仍无响应,你会再问:能听见吗?
喂!
说话啊!(keepalive_intvl:每隔一段时间询问是否连接可用)
尝试若干次后(keepalive_probes: 询问连接次数) , 仍无响应,则挂电话(关闭连接)。
- 为什么需要keepAlive?
订餐电话时,客户点了部分餐突然不说话了,这时候挂机就相当于keepAlive.
对于网络应用而言,keepalive是请求未使用却占用连接,这时候需要keepavlie标记连接为不可用。
- 怎样设计keepAlive? 以TCP为例
keepAlive特征:
- 出现机率比较小
- 判断连接不要用需要谨慎不能武断
TCP keepalive的核心参数:
- net.ipv4.tcp_keepalive_time=7200
- net.ipv4.tcp_keepalive_intvl=75
- net.ipv4.tcp_keepalive_probes=9
以上参数的意思: 当tcp keepalive打开(tcp默认关闭)时,tcp连接没有传递数据时,7200秒后发送keepalive消息,当没有收到确认消息时,会每隔75秒重发一次,一直尝试9次仍然无响应,则认定连接失败。
也就是认定tcp长连接失败需要:2小时11分钟(7200+75*9)
- tcp层有keepalive,为什么还需要应用层keepalive
- 协议分层,各层关注点不同:
传输层关注是否“通”,应用层关注服务是否可用,网络连接通畅不代表服务可用
- tcp层的keepalive默认关闭,且经过路由中转设备keepalive包可能被丢弃
- tcp层的keepalive时间过长
默认2小时太,虽然可配置,但属于系统参数,改动影响所有应用
- idle监测是什么
idle只负责监测
idea的作用:
- 配合发送keepalive
keepalive的演进:v1定时发送keepalive;v2idle检测,无数据传输超过一定时间时判定为idle发送keepalive
- 直接关闭连接
好处:快速释放恶意、损坏、很久不用的连接
坏处:简单粗暴,客户端需要重连
- 如何在netty中开启tcp keepalive和idle
-
Netty中的那些“锁”事
有序性
- 锁的分类
按竞争分:
乐观锁 juc原子包
悲观锁 syncronized
按等待锁的公平性:
公平锁和非公平锁
按是否可共享:
共享和独享
- netty玩转锁的五个关键点
- 减小锁的粒度
synchronized block instead of synchronized method
- 在意锁对象本身的大小,减少空间占用
volatile long + static AtomicLongFieldUpdater 替代 AtomicLong
- 提高锁的速度 提高并发性能
longAdder(jdk8) 规则 AtomicLong
ConcurrentHashMapV8 在用户jdk小于1.8的时候,使用netty的ConcurrentHashMapV8提高性能
- 不同场景使用不同的并发类
CountDownLatch 替代 Object.wait/notify
Jctools’ MPSC 替代 juc.LinkedBlockingQueue (MPMC:多生产者多消费者)
- 衡量好锁的价值 -> 能不用就不能
场景模拟:ktv包厢,
a. 一个服务员服务某几个固定包厢;
b. 多个服务员服务所有包厢。
表面上看b效率高,但b服务员之间的交流沟通成本也比较大(上下文切换)
netty的做法:
局部串行 + 整体并行 -> 一个队列+多个线程
1 降低开发难度、提升处理性能
2 避免锁带来的上下文切换和并发保护等开销
Netty如何玩转内存使用
- 内存使用技巧的目标
- 内存占用少 (空间)
- 应用速度快 (时间)
对java而言减少Full GC的Stop World时间
- netty内存技巧
- 能用基本类型就不要用包装类
- 应该定义为类变量static的就不要定义为实例变量
- 对分配内存预估 : hashMap指定size
- 0复制 组合模式等代替data复制
- 堆外(off heap)内存, jvm外部内存
- 内存池技巧
- apach common pool
- netty的Recycler