本文档主要列举离会、关闭进程、断网、重连等会导致sdk与服务端断开连接的场景的设计与实现,并试图解释其原理

1.Netty断链场景分析

1. Netty对断链的处理

简单来说Netty在检测到断开连接的情况下会抛出channelInactive事件(其实准确的说应该是de-register事件),这个事件会在pipeline的Handler中被传递和被处理,当然也可以选择不往下传递,即不调用fireChannelInactive(),对pipeline和handler处理事件等原理有兴趣可以看一下《netty in action》第16章,这里我们就重点关注哪些场景会触发channelInactive  

2. ChannelInactive触发场景

  • 客户端发送close帧(FIN包)
  • 客户端关闭进程(RST包)
  • 服务端或客户端主动调用channel.close()

以上为使用Netty官方示例测试得出的场景,需要注意的是直接断网并不会触发channelInactive,原因大概是由于直接断开网络并没有发送fin包,netty无法感知当前连接的存活状态,当然我们可以通过心跳超时来处理这种情况

另外心跳超时的情况也需要额外说明一下,在pipeline中添加IdleHandler可以配置一个自定义的心跳超时策略,例如我的服务中配置的是35s无新消息,当无新消息写入时,抛出一个心跳超时时间;但是心跳超时事件本身如果不去捕获netty是不会去做额外处理的,所以我的服务会在pipeline后面的heartbeatHandler中捕获心跳超时事件并主动关闭channel(对应上述第三种断链场景)

3. EventLoopGroup

顺带提一下Netty的线程处理模型,Netty主要分为两个线程的group,bossGroup和workerGroup,可以当成两个线程池,其中bossGroup一般只有一个线程,用来处理新连接的请求,而连接具体的IO和业务操作则放在workerGroup中完成。一般workerGroup中包含多个EventLoop(一个EventLoop可以理解成一个线程或者nio中的selector),多个channel可以注册到同一个EventLoop上,而对一个channel的处理从头至尾只会由同一个EventLoop来完成,想了解更多关于EventLoopGroup的细节也可以看下《netty in action》前几章的内容。这里提线程处理模型的原因主要是想说明,即使当客户端突然断开连接,netty也不会终止对前几个消息的处理,而是会等待前面的消息处理完再处理关闭事件,因为虽然netty是一个异步的框架,但是同一个channel的操作都是在同一个线程上顺序执行的

2.直接关闭客户端的场景

当客户端直接关闭/结束进程时,抓包信息如下

10.164.184.68    100.94.8.71       TCP    54    53435 → 80 [FIN, ACK] Seq=1 Ack=1 Win=1022 Len=0
100.94.8.71       10.164.184.68    TCP    60    80 → 53435 [FIN, ACK] Seq=1 Ack=2 Win=103 Len=0
10.164.184.68    100.94.8.71       TCP    54    53435 → 80 [ACK] Seq=2 Ack=2 Win=1022 Len=0

可以看到客户端主动发送了fin包,服务端会调用WsSubscribeHandler的channelInactive,触发一个自定义的channelInactiveEvent事件,之后主要执行bye和evict方法,其中bye主要是向mcs发送leaveRoom请求,evict主要是删除acs的相关缓存,这是断链场景下最简单的一个流程

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐