Broken pipe异常分析及解决

Broken pipe异常分析报告

1.错误描述

ClientAbortException: java.io.IOException: Broken pipe

可能出现原因:

TCP服务端write数据时,收到SIGPIPE信号(连接已经终止)

场景:

  1. TCP握手尚未结束时,连接已经close;
  2. 服务端收到一次read,但write了多次;
  3. 连接通道被占满,新连接被拒绝时,client中断了所有连接。

2.分析过程

2.1.初步排查

每次出现该异常时,总是伴随/im/getUsercInfos.json接口的调用。

2.1.1具体现象
  • 异常记录时间=接口请求记录时间(server连接write前记录)-(2至10)ms;
  • 总是ios设备爆出;
  • 总是h端爆出。
2.1.2分析
  1. 可能性一: 
    在请求该接口时,ios设备在某种情况下会中断该请求,导致TCP连接中server端无法向client端write数据。即在server端write之前连接已经close,write时出现异常。 
    由于只有h端出现,但h、b客户端代码一致,不太可能单独出现,故可能性较低。

  2. 可能性二: 
    在第一次连接已经正常握手,并正常close后,server端在此执行write操作。即对一个对端已经关闭的socket调用两次write,报出SIGPIPE信号,导致异常。 
    由于只有ios设备有该问题,故可能性较低。

  3. 可能性三: 
    请求接口本身问题或并发调用问题,引起了连接close或连接过多超限导致client端close连接。 
    目前acceptCount配置100,观察日志,报错时间区间中并没有如此大量的并发请求,且client端收到拒绝信息时,不确定是否会中断所有请求(理论上不会)。故可能性较低。

2.2.细致排查

伴随该异常的接口,入参和出参量较大(猎头端im对话列表1000-3000个是常态),接口处理时间长,且伴随不同程度的连续请求。

2.2.1具体现象
  • 入参传入emNames数量600-4000个,约15000-100000字符长度;
  • 出参返回约60000-500000字符长度;
  • 执行时常约500-4000ms;
2.2.2分析
  1. 可能性一: 
    处理报文过长,client端无法解析/处理过大报文,导致本次及下次请求。 
    客户端系统对于大报文的处理问题需要测试和调研,故有可能性。

  2. 可能性二: 
    处理时间过长,导致当client端并发请求时,当上次请求尚未完成,下次请求会close上次请求,以本次为准。 
    调研后发现client端是阻塞请求,但需要进行实际测试,故有可能性。

  3. 可能性三: 
    处理时间过长,在握手过程中client自行中断了连接。 
    由于执行时间较长,频率较高,客户或设备自己可能触发kill进程或关闭连接等操作,故有可能性。

3.测试过程

3.1.场景设计

  1. 服务端: 
    返回参数量大、处理时间长的接口
  2. 客户端: 
    h端客户端;大量输入参数;并发调用接口的工具
  3. 接口: 
    URL:/im/getTests.json 
    入参:emNames (List,每条约24字符) 
    出参:PageForm 
    逻辑:根据emNames数量,组装并返回同等数量的PageForm(每条约250字符),根据需求sleep若干时间(3000-5000ms)。

3.2.测试报告

3.2.1.IOS
  1. ios,4000参数,3次并发,服务端无sleep: 无问题 
    这里写图片描述

  2. ios,4000参数,3次并发,服务端sleep 3000ms: 无问题 
    这里写图片描述

  3. ios,2000参数,20次并发,服务端sleep 5000ms,中途client切出: 无问题 
    这里写图片描述

  4. ios,2000参数,20次并发,服务端sleep 5000ms,中途client中断: 发现问题,完全符合异常现象 
    这里写图片描述
    这里写图片描述

3.2.2.ANDROID
  1. android,2000参数,10次并发,服务端sleep 5000ms,中途client中断:发现问题,完全符合异常现象 
    这里写图片描述
    这里写图片描述

4.结论

4.1.问题原因

client端用户在杀死进程时,接口的TCP请求尚未完成(未完成的原因是处理时间长)。 
导致server端write数据时,收到SIGPIPE信号,抛出Broken pipe异常。 
但由于已经杀死了进程,并不会对用户产生任何影响。

4.2.其他结论

  1. ios和android在处理1M以内(尚不清楚有没有最大值)大小报文时,没有问题
  2. ios和android均是阻塞请求,本次请求不会对上次请求造成影响,即使上次请求尚未完成
  3. ios切换至后台,短时间内(5000ms以内,最大值根据系统不同而不同)并不会中断已经存在的TCP连接。
  4. android切换至后台,并不会中断已经存在的TCP连接。
  5. ios、android杀死进程会一并关闭已经存在的TCP连接。
  6. android框架(公司基于google改造的框架)默认并发5线程一组。

5.解决方案

由于kill进程我们无法控制,故只能通过降低接口处理时间,减少用户kill进程时未完成的TCP连接数量。

具体:

  1. 会话列表翻页
  2. 会话列表限制展示数量
  3. 客户端分组获取会话列表数据

猜你喜欢

转载自blog.csdn.net/dreamhai/article/details/80878805