最近做性能测试时,发现连接第三方系统时会有约1%的交易提示如下错误

nested
exception is org.apache.commons.httpclient.ConnectTimeoutException: The host did
not accept the connection within timeout of 10000 ms

起先抱着能google就google的思路去找,结果没找到相应的解决方案,只能自己一步一步去排查了,下面记录下排查过程。

抓拍问题现场

涉及到网络的问题,先抓个包,tcpdump走一波。

tcpdump -i eth0 host xxx.xxx.xxx.xxx > tcp.dmp

节选了一次TCP连接的整个内容,从三次握手到报文发送到四次挥手,下面将一次通讯分为三个部分进行讲解

包格式讲解
时间戳 协议类型 请求方地址>接收方地址 Flags [xxx], seq xxx,......,length

协议类型:此处针对TCP/IP协议,为IP

Flags常见值及含义

标识 含义
S 即SYN,用于建立连接
. .即ACK,可与其他组合使用比如[S.]代表SYN的ACK回执
P 即PSH,指示接收方应立即将数据交给上层
F 即FIN,用于断开连接
三次握手
09:23:00.038908 IP 192.168.1.100.51777 > 192.168.1.200.39045: Flags [S], seq 2590465106, win 29200, options [mss 1460,sackOK,TS val 1752507811 ecr 0,nop,wscale 7], length 0
09:23:00.039329 IP 192.168.1.200.39045 > 192.168.1.100.51777: Flags [S.], seq 148046299, ack 2590465107, win 14480, options [mss 1460,sackOK,TS val 2174555693 ecr 1752507811,nop,wscale 7], length 0
09:23:00.039341 IP 192.168.1.100.51777 > 192.168.1.200.39045: Flags [.], ack 1, win 229, options [nop,nop,TS val 1752507811 ecr 2174555693], length 0

  1. 客户端发送SYN=1,seq=随机生成的序号到服务端,对应第一个包内容

  2. 服务端响应SYN=1,seq=服务端随机序号,ack=第一步中的seq+1给客户端,对应第二个包内容

  3. 客户端发送SYN=0,seq=第一步中的seq+2,ack=服务端的随机序号+1给服务端,对应第三个包

    至此连接建立完成

    TCP连接为全双工的,既可以C->S,亦可S->C

请求发送/响应接收
09:23:00.039406 IP 192.168.1.100.51777 > 192.168.1.200.39045: Flags [P.], seq 1:167, ack 1, win 229, options [nop,nop,TS val 1752507811 ecr 2174555693], length 166
09:23:00.039426 IP 192.168.1.100.51777 > 192.168.1.200.39045: Flags [P.], seq 167:2259, ack 1, win 229, options [nop,nop,TS val 1752507811 ecr 2174555693], length 2092
09:23:00.039778 IP 192.168.1.200.39045 > 192.168.1.100.51777: Flags [.], ack 167, win 122, options [nop,nop,TS val 2174555693 ecr 1752507811], length 0
09:23:00.039784 IP 192.168.1.200.39045 > 192.168.1.100.51777: Flags [.], ack 2259, win 155, options [nop,nop,TS val 2174555693 ecr 1752507811], length 0
09:23:00.533102 IP 192.168.1.200.39045 > 192.168.1.100.51777: Flags [P.], seq 1:972, ack 2259, win 155, options [nop,nop,TS val 2174555817 ecr 1752507811], length 971
09:23:00.533114 IP 192.168.1.100.51777 > 192.168.1.200.39045: Flags [.], ack 972, win 244, options [nop,nop,TS val 1752507935 ecr 2174555817], length 0

  1. 每发送一次报文,对端需要做一次ACK回执,保证TCP传输的可靠
  2. 包1和包2是客户端向服务端请求的,包3和包4是服务端回执的,包5是服务端响应的内容,包6是客户端对响应做的回执。
四次挥手
09:23:00.533121 IP 192.168.1.200.39045 > 192.168.1.100.51777: Flags [F.], seq 972, ack 2259, win 155, options [nop,nop,TS val 2174555817 ecr 1752507811], length 0
09:23:00.533166 IP 192.168.1.100.51777 > 192.168.1.200.39045: Flags [F.], seq 2259, ack 973, win 244, options [nop,nop,TS val 1752507935 ecr 2174555817], length 0
09:23:00.533479 IP 192.168.1.200.39045 > 192.168.1.100.51777: Flags [.], ack 2260, win 155, options [nop,nop,TS val 2174555817 ecr 1752507935], length 0

此处因为使用了HTTP协议,且在系统头中设置了Connection=close,所以在请求处理完成后,服务端便会将连接关闭。

  1. 第一步:服务端发送FIN到客户端,对应包1。
  2. 第二步:客户端回执ACK到服务端,对应包2。
  3. 第三步:客户端发送FIN到服务端,对应包2。

因为第二、第三步都是从客户端发送到服务端,TCP协议实现时在此处做了合并,将ACK和FIN一并发给对端,减少了网络消耗。

  1. 第四步:服务端回执ACK到客户端,对应包3,连接释放完成。

问题分析

包是抓到了,可是在性能测试时,业务量特别大,如果纯粹靠手工分析的话,怕是要找到地老天荒了,为此写了一段Python代码来分析tcpdump的包,因为本次的问题为请求连接有问题,必然是三次握手的时候存在问题,所以以下代码主要分析三次握手时的情况。

Packet.py

解析包内容

import re

class Packet(object):
'分组' timestamp = '00:00:00.000000' # 时间戳
from_addr = '0.0.0.0' # 源地址
to_addr = '0.0.0.0' # 目的地址
protocol_type = 'IP' # 协议类型 flags = '[S]'
seq = '0'
ack = '0'
length = 0 # 数据包长度 def __init__(self, packet_str):
hb = re.split(': ', packet_str)
header = hb[0]
body = hb[1] hs = re.split('[\s]', header)
self.timestamp = hs[0]
self.protocol_type = hs[1]
self.from_addr = hs[2]
self.to_addr = hs[4] bs = re.split(',\s', body)
for b in bs:
kv = re.split('\s', b, 1)
k = kv[0]
v = kv[1]
if 'Flags' == k:
self.flags = v
elif 'seq' == k:
if self.flags != '[P.]':
self.seq = v
elif 'length' == k:
self.length = int(v)
elif 'ack' == k:
self.ack = v def __str__(self):
return self.timestamp + self.protocol_type + self.from_addr + '==>' + self.to_addr + self.flags + str(
self.seq) + str(self.ack) + str(self.length)

ConnectionTimeoutAnalyze.py

分析dmp包内容

from tcpdump.Packet import Packet

client_addr = '192.168.1.100'
server_addr = '192.168.1.200.39045' packet_times = {} # 连接ID:时间
first_packets = [] #三次握手第一次的包 '''
第一步:syn=1,seq=xxx
第二步:syn=1,seq=yyy,ack=xxx+1
第三步:syn=0,seq=xxx+1,ack=yyy+1
'''
with open('C:\\Users\\xxx\\Desktop\\39045.dmp') as dmp:
for line in dmp.readlines():
line = str(line.strip())
packet = Packet(line)
if packet.from_addr.startswith(client_addr) and packet.flags == '[S]':
first_packets.append(packet) id = packet.from_addr + packet.to_addr + packet.flags + 'ack=' + packet.ack
if packet.flags != '[S.]':
id = id + 'seq=' + packet.seq packet_times[id] = packet.timestamp print('请求ID(客户端IP.端口\\请求序号)\t第一步时间\t\t第二步时间\t\t第三步时间')
for fpack in first_packets:
sync_time = fpack.timestamp # 第一步的时间
id = fpack.to_addr + fpack.from_addr + '[S.]' + 'ack=' + str(int(fpack.seq) + 1) # 第二步:ack=第一步的seq+1
ack_time = packet_times.get(id)
conn_time = None
if ack_time is not None:
conn_time = packet_times.get(fpack.from_addr + fpack.to_addr + '[.]ack=1seq=0') # 第三步:ack=1,seq无值
print(
'[' + fpack.from_addr + '\\' + fpack.seq + ']\t' + sync_time + '\t\t' + str(ack_time) + '\t\t' + str(conn_time))

通过代码分析,发现存在部分创建连接时第二步的时间为None的,因此可以得出结论为发出去的SYN请求包没有得到服务端的回执,可以光明正大的摔锅给网络/第三方了!

后记:

上班后,在服务端抓取客户端的TCP包。并找到上述第二步时间为None的包,用三次握手第一步的seq去服务端包中查找,找不到相应的包,可以断定,网络存在丢包的情况。

【问题记录】记一次ConnectionTimeout问题排查的更多相关文章

  1. 记一次用arthas排查jvm中CPU占用过高问题

    记一次使用arthas排查jvm中CPU占用过高问题.这工具屌爆了 碾压我目前使用的全部JVM工具. 安装 小试 curl -O https://arthas.aliyun.com/arthas-bo ...

  2. 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查)

    原贴如下 坑爹坑娘坑祖宗的87端口(记一次tomcat故障排查) 虽然我用的是PHPstudy部署的dedecms,还是一样栽倒这个坑里了. 总结经验:本地测试使用8000~9000的端口比较安全.

  3. 记一次NoHttpResponseException问题排查

    上传文件程序会有一定的概率提示错误,错误率大概在1%以下,错误信息是:org.apache.http.NoHttpResponseException , s3-us-west-1.amazonaws. ...

  4. 记一次tomcat故障排查(转)

    1~1024之间的端口号是保留端口,通常是为特定目的预留的.虽然你的问题不是由于保留端口引起的,但是仍然建议你不要随意使用保留端口作为自定义服务的端口,如果你能早早遵循这一规则压根就不会遇到这个问题. ...

  5. (BUG记录)记一次与其他系统交互协作时造成的锁表问题

    最近两日做公司电信某计费项目时,接收一个银行对账的任务,在完成对账后.电信和银行两方金额一致时需要进行充值.冲正操作保持金额一致.冲正服务是JAVA统一调用Tuxedo服务,这个服务已经是一个稳定可用 ...

  6. 记一次jvm异常排查及优化

    为方便自己查看,根据工作遇到的问题,转载并整理以下jvm优化内容 有次接到客服反馈,生产系统异常,无法访问.接到通知紧急上后台跟踪,查看了数据库死锁情况--正常,接着查看tomcat 内存溢出--正常 ...

  7. 记一次Windb死锁排查

    正在开会,突然线上站点线程数破千.然后一群人现场dump分析. 先看一眼线程运行状态 !eeversion 发现CPU占用并不高,19%,937条线程正在运行. 看看他们都在干什么. ~* e !cl ...

  8. 记一次使用windbg排查内存泄漏的过程

    一.背景 近期有一个项目在运行当中出现一些问题,程序顺利启动,但是观察一阵子后发现内存使用总量在很缓慢地升高, 虽然偶尔还会往下降一些,但是总体还是不断上升:内存运行6个小时候从33M上升到80M: ...

  9. 【Azure Redis 缓存】Azure Redis出现了超时问题后,记录一步一步的排查出异常的客户端连接和所执行命令的步骤

    问题描述 Azure Redis在使用的过程中,多次无规律的出现超时问题.抓取到客户端的异常错误后,想进一步的分析是何原因导致了如下异常呢? Timeout awaiting response (ou ...

随机推荐

  1. 吴裕雄--天生自然HTML学习笔记:HTML 基础- 4个实例

    HTML 标题 HTML 标题(Heading)是通过<h1> - <h6> 标签来定义的. 实例 <h1>这是一个标题</h1> <h2> ...

  2. 吴裕雄--天生自然python学习笔记:Beautiful Soup 4.2.0模块

    Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时 ...

  3. JSTL标签常用

    JSTL简介: 标准标签库JSTL的全名为:Java Server Pages Standard Tag Library. JSTL主要提供了5大类标签库: 1.       核心标签库: 为日常任务 ...

  4. python编程基础——集合

    集合是是基本数据类型的一种集合类型. 作用:去重 属性:intersection.union.difference.issubset 实例: list_1=[1,2,3,4,3,5,2,6,1]lis ...

  5. 腾讯自动化测试的AI智能

    引子: 本文是林奕在腾讯 DevDays 2018 分享内容的脱敏整理,介绍了 CSIG 测试开发中心(前 SNG 测试开发中心)在自动化测试领域所做的智能化尝试. 大致分成下面几部分: 使用AI面对 ...

  6. oppo互联网招聘-各类软件测试

    一.服务端测试专家 关键词:安全测试.白盒测试.性能测试.自动化.持续集成.服务端 岗位职责: 主导多个高日活产品的测试方案: 试点和推广自动化和持续集成: 改善测试相关流程和规范. 职位要求: 计算 ...

  7. 从赴美IPO绝迹 看那些烧成泡沫的互联网企业

    曾经,赴美上市是很多中国企业的终极梦想.然而在当下,随着中概股在美国股市股价的不断走低.中国赴美上市企业私有化速度的加快,大众才发现,原来美国股市并不是那么好混的.但不管怎样,赴美上市始终是一种荣耀. ...

  8. ES6学习笔记之变量的解构赋值

    变量的解构赋值 ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构. 数组的解构赋值 以前,为变量赋值,只能直接指定值: 1 2 3 var a = 1; var b = 2; ...

  9. 高阶函数---swift中的泛型介绍(一步步实现Map函数)

    说明 本文内容均出自函数式 Swift一书, 此处整理仅仅是为了自己日后方便查看, 需要深入研究的话, 可以点进去购买, 支持原作者 本书由 王巍–新浪微博大神翻译 OneV's Den 喵神博客 接 ...

  10. 达拉草201771010105《面向对象程序设计(java)》第十三周学习总结

    达拉草201771010105<面向对象程序设计(java)>第十三周学习总结 第一部分:理论知识 事件处理基础: 事件源:能够产生事件的对象都可 以成为事件源,如文本框.按钮等.一个事件 ...