baidu / sofa-pbrpc

A light-weight RPC implement of google protobuf RPC framework.
Other
2.13k stars 655 forks source link

RpcClient退出时hang住问题记录 #110

Closed zd-double closed 8 years ago

zd-double commented 8 years ago

问题现象

近期在线上出现RpcClient在退出时会hang住导致应用程序不能正常退出的现象

问题原因

  1. 通过对线上环境进行观察,发现RpcClient退出时仍然有连接处于established状态。
  2. 通过线下模拟,server先断开后启动,发现RpcClient在同一远端地址上(remote_endpoint)建有多个连接,client不能正常退出且有连接established。
  3. 通过加日志复现,同时对代码进行分析发现在存在以下时序场景:
    1. t1 时刻
      thread1 对失败的连接执行close() (rpc_byte_stream.h:66),第一步: 将该连接状态设为STATUS_CLOSED, 执行socket shutdown;
    2. t2 时刻
      thread1 在close()第二步: 执行on_close()操作,首先将该连接上所有请求的回调依次执; thread2 在此时执行FindOrCreateStream,首先对_stream_map加锁;
    3. t3 时刻
      thread1 执行on_closed()第二步:执行RpcClientImpl::OnClosed()的回调,对_stream_map加锁失败; thread2 此时已对_stream_map上锁,发现对应连接状态STATUS_CLOSED,重新创建连接,并插入_stream_map替掉之前的连接,完成后thread2 对_stream_map打开锁;
    4. t4 时刻
      thread1对_stream_map加锁成功,删掉对应的连接,但此时连接已经是thread1新创建的连接; thread2 对新创建的连接异步connect;

这种时序场景下,thread2新创建的连接被thread1误以为是关闭的连接移出_stream_map,该活动的连接无法在RpcClient析构时关掉,导致关联的io_service.run()不能返回,进而导致线程池阻塞。

解决方案

在RpcClientImpl::OnClosed()删除关闭的连接时,进行一次判断:只有当stream状态为关闭才从_stream_map删除。实现见#109

cyshi commented 8 years ago