Netty — 编解码与协议
📋 本文节选自《Netty 面试题》,返回 查看全部章节
17. Reactor 多线程模型
一个Acceptor线程,一组 NIO 线程,一般是使用自带线程池,包含一个任务队列和多个可用线程场景:可满足大多数场景,当Acceptor需要做负责操作的时候,比如认证等耗时操作 ,在高并发情况下也会有性能问题
18. Reactor 主从线程模型
Acceptor不在是一个线程,而是一组 NIO 线程,IO 线程也是一组 NIO 线程,这样就是 2 个线程池去处理接入和处理 IO场景:满足目前大部分场景,也是 Netty推荐使用的线程模型BossGroup 处理连接的WorkGroup 处理业务的
19. Netty 使用 NIO 而不是 AIO
在linux系统上,AIO 的底层实现仍然使用 epoll,与 NIO 相同,因此在性能上没有明显的优势Netty 整体架构是 reactor 模型,采用 epoll机制,IO 多路复用,同步非阻塞模型Netty是基于 Java NIO 类库实现的异步通讯框架特点: 异步非阻塞,基于事件驱动,性能高,高可靠性,高可定制性。
20. Echo服务
回显服务,用于调试和检测的服务
21. EventLoop和EventLoopGroup
高性能RPC框架 3 个要素:IO 模型、数据协议(http,brotobuf/thrift)、线程模型EventLoop 好比一个线程,一个 EventLoop可以服务多个Channel,一个Channel只有一个EventLoop,可以创建多个EventLoop来优化资源的利用,也就是EventLoopGroup一个Cahnnel 一个连接,EventLoopGroup 负责EventLoopNIO(单线程处理多个Channels) BIO(一个线程处理一个Channels)事件:accept,connect,read,writeEventLoopGroup 默认创建线程数是 CPU 核数 * 2
22. Bootstrap
1、 group:设置线程中模型,Reactor线程模型对比EventLoopGroup;
1、 单线程;
EventLoopGroup g = new NioEventLoopGroup(1);
ServerBootstrap strap = new ServerBootstrap();
strap.group(g)1、 多线程;
2、 主从线程;
23. channel
NioServerSocketChannelOioServerSocketChannelEpollServerSocketChannelKQueueServerSocketChannel
24. childHandler
用于对每个通道里面的数据处理
25. childOption
作用于被 accept之后的连接
26. option
作用于每个新建立的 channel,设置 TCP 连接中的一些参数
- ChannelOption.SO_BACKLOG 存放已完成三次握手的请求的等待队列的最大长度 Linux 服务器 TCP 连接底层知识: syn queue: 半连接队列,洪水攻击(伪造 IP 海量发送第一个握手包),tcp_max_syn_backlog (修改半连接 vi /etc/sysctl.conf) accept queue:全连接队列 net.core.somaxconn 当前机器最大连接数 系统默认的somaxconn参数要足够大,如果 backlog 比 somaxconn大,则会优先用后者
- ChannelOption.TCP_NODELAY 默认是 false,要求高实时性,有数据时马上发送,就将该值改为 true 关闭 Nagle 算法 (Nagle 算法会积累一定大小后再发送,为了减少发送次数)Nagle算法只允许一个未被 ACK 的包存在于 网络 (tcp_synack_retries = 0 加快回收半连接,如果收不到第三个握手包 ACK,不进行重试,默认值是 5,每次等待 30S,半连接会 hold住大约 180s,tcp_syn_retries 默认值是 5,客户端没收到 SYN+ACK 包,客户端也会重试 5 次发送 SYN 包)
27. childOption
作用于被 accept之后的链接
28. childHandler
用于对每个通道里面的数据处理
29. Channel
- Channel 客户端和服务端建立的一个连接通道
- ChannelHandler 负责Channel的逻辑处理
- ChannelPipeline 负责管理 ChannelHandler的有序容器 一个Channel包含一个ChannelPipeline,所有 ChannelHandler都会顺序加入到ChannelPipeline中。 Channel当状态出现变化,对触发对应的事件
![ ][nbsp 5]
状态:
- channelRegistered channel注册到一个EventLoop,和Selector绑定
- channelUnRegistered channel已创建,但是未注册到一个EventLoop里面,也就是没有和Selector绑定
- channelActive 变为活跃状态,连接到了远程主机,可以接受和发送数据
- channelInActive channel处于非活跃状态,没有连接到远程主机
30. ChannelHandler和ChannelPipeline
ChannelHandler生命周期:handlerAdded:当ChannelHandler添加到ChannelPipeline调用handlerRemoved:当ChannelHandler从ChannelPipeline移除时调用exceptionCaught:执行抛出异常时调用ChannelHandler有 2 个子接口:
![ ][nbsp 6]
ChannelInboundHandler(入站): 处理输入数据和Channel状态类型改变,适配器ChannelInboundHandlerAdapter(适配器设计模式),常用SimpleChannelInboundHandlerChannelOutboundHandler(出站):处理输出数据,适配器ChannelChannelPipeline:好比厂里的流水线一样,可以在上面添加多个ChannelHandler,也可以看成是一串ChannelHandler 实例,拦截穿过Channel的输入输出 event,ChannelPileline实现了拦截器的一种高级形式,使得用户可以对事件的处理以及ChannelHandler之间交互获得完全的控制权
31. ChannelHandlerContext
![ ][nbsp 7]
1、 channelHandlerContext是连接ChannelHandler和ChannelPipeline的桥梁;ChannelHandlerContext 部分方法是和 Channel以及ChannelPipleline重合,好比调用 write方法Channel,ChannelPipeline,ChannelHandlerContext都可以调用写方法,前 2 者会在整个管道流里传播,而 ChannelHandlerContext只会在后续的 Handler里传播
2、 AbstractChannelHandlerContext;双向链表结构,next/prev 后继、前驱节点
3、 DefaultChannelHandlerContext是实现类,但是大部分都是父类完成,整个只是简单的实现一些;方法,主要就是判断 Handler的类型fire调用下一个 handler,不fire就不调用
32. Handler执行顺序
InboundHandler顺序执行,OutboundHandler逆序执行
channel.pipeline().addLast(new OutboundHandler1());
channel.pipeline().addLast(new OutboundHandler2());
channel.pipeline().addLast(new InboundHandler1());
channel.pipeline().addLast(new InboundHandler2());InboundHandler1 InboundHandler2 OutboundHandler2 OutboundHandler1InboundHandler1之间通过fireChannelRead()方法调用InboundHandler通过ctx.write(msg),传递到OutboundHandlerctx.write(msg)传递消息,Inbound需要放在结尾,在 outbound之后,不然outboundHandler不会执行,使用 channel.write(msg),或者 pipline.write(msg),就不用考虑(传播机制)客户端: 发起请求再接受请求,先 outbound再inbound服务端:先接受请求再发送请求,先inbound再outbound
33. ChannelFuture
netty中所有 I/0 操作都是异步的,意味着任何 I/0 调用都会立即返回,而ChannelFuture会提供有关的信息I/0 操作的结果或状态未完成:当 I/0 操作开始时,将创建一个新的对象,新的最初是未完成的,它既没有成功,也没有被取消,因为 I/0 操作尚未完成。已完成:当 I/0 操作完成,不管是成功、失败还是取消,Future都是标记为已完成的,失败的时候也有具体的信息,例如原因失败,但请注意,即使失败和取消属于完成状态。
![ ][nbsp 8]
注意:不要在 IO 线程内调用Future对象的sync和await方法,不能在 channelhandler中调用 sync 和await