Netty-网络协议与Netty常见面试题

TCP三次握手过程

在这里插入图片描述

为什么需要三次握手

TCP是可靠的传输协议,三次握手能保证数据可靠传输又能提高传输效率。
两次握手不能保证连接已建立
四次握手没有必要

TCP四次挥手

在这里插入图片描述

为什么要有TIME_WAIT状态

  1. 可靠终止TCP连接,如果最后一个ACK报文因网络原因被丢弃,此时server因为没有收到ACK而超时重试FIN报文,处于TIME_WAIT状态的client可以继续对FIN报文回复,向server发送ACK报文
  2. 保障迟来的TCP报文段有足够的时间被识别和丢弃,连接结束了网络中的延迟报文也应该被丢弃掉,以免影响立刻建立的新连接。

SYN洪水攻击

属于DOS攻击的一种,发送大量的半连接请求,耗费CPU和内存。
客户端在短时间内模拟大量不存在的IP地址,向服务器不断发送SYN报文,服务器回复ACK,并等待客户端回复,由于源地址不存在,服务器需要不断重试直至超时,这些伪造的SYN 报文被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。

如果设计QQ,网络协议该如何设计

登录采用TCP和Http,发送消息采用UDP协议,内网传文件采用P2P。

  1. 登录过程,client采用tcp发送消息,http协议下载信息,登录之后使用tcp状态保持在线状态
  2. 发送消息,采用udp,但是需要通过服务器端转发,在上层应用确保消息的可靠传输,如果消息失败则会提示发送失败
  3. 内网传输文件不需要经过服务器

Netty的特点

  1. 高性能、异步事件驱动的NIO框架
  2. 使用高效的socket底层,对epoll空轮询引起的cpu占用飙升在内部做了处理,避免了直接使用NIO
  3. 采用多种encoder和decoder的处理,对TCP的粘包和分包做了处理
  4. 可配置IO线程数、TCP参数,TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存池的方式循环利用ByteBuf
  5. 通过引用计数器即时申请释放不再引用的对象,降低GC

Netty的线程模型

Netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池:boss线程池和worker线程池,其中boss线程池负责处理请求的accept事件,并交给worker线程池,worker线程池负责处理请求的read和write事件,由对应的handler处理

  1. 单线程模型:所有I/O 操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor 线程上完成的。既要接收客户端的连接请求,向服务端发起连接,又要发送/读取请求或应答/响应消息。
  2. 多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 连接请求;NIO 线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操作问题。但在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。
  3. 主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端连接,将 SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,重新注册到Sub线程池的线程上,用于处理I/O 的读写等操作,从而保证mainReactor 只负责接入认证、握手等操作;

TCP粘包和拆包的原因及解决方法

TCP是以流的方式处理数据,一个完整的包可能会被TCP拆分成多个包发送,也可能将很多小的包封装成一个大的数据库发送。
拆包:应用发送写入的字节大小大于socket发送缓冲区的大小
粘包:应用发送写入的字节大小小于发送缓冲区的大小,需要将应用多次写入的数据合起来发送
解决办法:

  1. 消息定长:FixedLengthFrameDecoder
  2. 包尾增加特殊字符分割:如行分隔符,LineBasedFrameDecoder
  3. 将消息分为消息头和消息体,LengthFieldBasedFrameDecoder

零拷贝实现

Netty 的接收和发送ByteBuffer 采用DIRECT BUFFERS,使用堆外直接内存进行Socket 读写,不需要进行字节缓冲区的二次拷贝。
Netty 提供了组合Buffer 对象,可以聚合多个ByteBuffer 对象,用户可以像操作一个Buffer 那样方便的对组合Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小Buffer 合并成一个大的Buffer。
Netty 的文件传输采用了transferTo 方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write 方式导致的内存拷贝问题。

如何解决JDK的selector BUG

Selector BUG:若Selector 的轮询结果为空,也没有wakeup 或新消息处理,则发生空轮询,CPU 使用率100%。
Netty:对Selector 的select 操作周期进行统计,每完成一次空的select 操作进行一次计数,若在某个周期内连续发生N 次空轮询,则触发了epoll 死循环bug。重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel 从旧的Selector 上去除注册,重新注册到新的Selector 上,并将原来的Selector 关闭。

如何单机支持百万长连接

  1. 操作系统
    在系统层面,最高的并发数量取决于用户单一线程同时可打开的文件数量的限制,ulimit
  2. Netty调优
    2.1 合理的线程数:主要设置Accceptor线程池(Boss group)以及worker线程池,服务端可同时监听多个端口,一般是CPU核数×2
    2.2 心跳优化:
    2.2.1 要能够及时检测失效的连接,并将其剔除,防止无效的连接句柄积压,导致OOM 等问题
    2.2.2 设置合理的心跳周期,防止心跳定时任务积压,造成频繁的老年代GC(新生代和老年代都有导致STW 的GC,不过耗时差异较大)
    2.2.3 使用Nety 提供的链路空闲检测机制
    Nety 提供了三种链路空闲检测机制,利用该机制可以轻松地实现心跳检测
    (1)读空闲,链路持续时间T 没有读取到任何消息。
    (2)写空闲,链路持续时间T 没有发送任何消息
    (3)读写空闲,链路持续时间T 没有接收或者发送任何消息
    对于百万级的服务器,一般不建议很长的心跳周期和超时时长
    2.3 接收和发送缓冲区优化
    2.4 IO线程和业务线程分离:如果服务器不做复杂的业务逻辑处理,则可以调大NIOEventLoop线程池方式,直接在IO线程中执行业务,减少一次线程上下文切换;如果业务逻辑复杂,那么将业务线程池分离,线程池可以和IO线程绑定,减少锁竞争。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值