这个问题有些怪异。
我们之前有一些 win 下的 c++服务端程序,最近把他转到了 linux 下面。
服务端程序提供 tcp 接口来让客户端程序连接。服务端程序提供了一个简单的 echo 服务来确认双方正常连接(一般所说的心跳检测)。每个客户端 3 秒左右一个 echo 请求,服务端收到之后立刻回复一个 echo 回复包。
问题:
在差不过 200 个客户端程序连接上之后。 客户端发出的包,会有一定几率超过 1 秒才收到回复。
目前 c++ 底层是 epoll 直接操作的。为了防止 epoll 操作有问题。我们尝试了这个 echo 程序和客户端的程序的基础 echo 逻辑 都用 golang 重新实现了一遍。
目前,我们在公司内网\腾讯云、 centos6\centos7 、 裸 linux\docker 容器 NAT 。都测试过。
都或多或少,有这个现象。
我想问,这个是正常的吗? 还是 linux 下 tcp 有什么需要特殊设置的部分?
请大神略微指点一二。 或者略微指点一二关键字也可~~~。
1
yuuyuu 2016-02-19 22:17:53 +08:00
换个角度思考,不是 tcp 设置问题,是心跳协议设计的有问题?比如产生了伪心跳?
|
2
hncqp 2016-02-19 22:37:18 +08:00 via iPhone
貌似性能问题?压测过没
|
3
redsonic 2016-02-19 22:53:37 +08:00
如果是多核环境把服务器进程绑到靠后面的 cpu 上面再测。
|
4
zhicheng 2016-02-19 22:57:34 +08:00 via Android
nodelay ?
|
5
zado 2016-02-19 23:08:41 +08:00
我也遇到过类似的问题,后来发现传送的数据多一点延迟就会减少,以后就是每次传送都加上一定数量的无用数据.
|
7
skydiver 2016-02-19 23:12:19 +08:00 via iPad
Nagle 算法?
|
8
Strikeactor 2016-02-19 23:13:48 +08:00 1
@zado Nagle 算法造成的?
|
9
redsonic 2016-02-19 23:16:28 +08:00
windows 也是缺省开 nagle 吧
在这里顺便问个问题,简单的心跳协议多数人会选择 tcp ? 为什么不用 udp 或 rawsocket ,简单而且延迟小。 |
10
k9982874 2016-02-19 23:27:06 +08:00 via iPhone
我觉得还是实现有问题,延迟一秒也太夸张了。检查一下线程有没有互锁或者资源竞争。
|
12
snnn 2016-02-19 23:32:17 +08:00 via Android
楼主换 libevent 吧。因为你明显对 TCP 底层东西不熟,就不要自己抡 epoll 了。
|
13
redsonic 2016-02-19 23:44:48 +08:00
@snnn 我的意思是服务器程序另开一个线程用 udp 或 rawsocket 专门处理心跳,避开 tcp ,毕竟 tcp 是流,很多不熟悉的人会遇到 5 楼的那种问题,此外实际应用中还有 netfilter ,很多隐藏坑在等着新手。 其次 TCP 不设置 keepalive 的话根本不关心有没有数据来往。
|
14
billlee 2016-02-19 23:54:21 +08:00
@redsonic 很多时候另外用一个线程发心跳就不请作用了啊。不在 TCP 上用心跳 keep-alive, 服务器向给客户端发条消息,然后收到一个 reset, 原来这条连接被 NAT 网关丢掉了。
|
15
shakespark 2016-02-20 00:04:19 +08:00
1.抓包分析服务器端是否在收到后立刻发出响应,排除客户端和服务器之间的网络问题
2.如果抓包发现发送就慢,那就在收到和发出时加日志,先排除程序的问题 3.如果确定是程序收到后立刻发出答复了,那就要看 tcp 是啥原因没及时送出去了 |
16
redsonic 2016-02-20 00:04:26 +08:00
@billlee 嗯,我懂了,你说的心跳是业务心跳,就是如果业务代码跑顿了也算是丢心跳?
NAT 网关丢状态我确实没考虑到,受教。 之前一直没有遇到中间有 NAT 的情况,每台设备都有公网 ip 。 |
17
snnn 2016-02-20 00:16:40 +08:00 via Android
@redsonic 你没明白我的意思。你单开 UDP 做 keepalive 不解决 TCP 连接会断的问题啊。
|
18
redsonic 2016-02-20 00:44:30 +08:00
@snnn 14 楼说的那个和你的意思不一样吗? 其实就是客户发一些数据保持整条会话,防止中间丢状态,连接关闭。我已开始理解的是业务监控方面的问题。
|
19
skydiver 2016-02-20 04:10:38 +08:00 via iPad
@snnn TCP 不发心跳不会断。发心跳只为了及时发现连接已经断了。要不然两边都不发数据,就发现不了断了。
|
20
redsonic 2016-02-20 04:54:58 +08:00 1
@skydiver 单纯从协议讲不会,但考虑到 NAT 还是有可能会断的,客户端的 NAT 发现长时间的会话空闲可能就会把该会话清除,另外运营商有一类叫做会话追踪的设备更不会长时间保持一个普通家用宽带用户的会话。简单讲就是连接保活。
|
21
est 2016-02-20 09:10:50 +08:00
SOMAXCONN
|
24
snnn 2016-02-20 09:19:14 +08:00 via Android
BTW ,我是做网游的
|
25
gamexg 2016-02-20 09:22:44 +08:00
golang 默认就关闭了 Nagle 。
|
26
eas OP 我的 cpp 程序和 golang 程序都是关闭了 nagle 的。
cpp 我不了解非常。但是 go 肯定多核跑的。 但是延迟 1 秒以上好诡异啊 |
28
eas OP @snnn 我这 libuv 的也有在做,现在还没完整实现。主要是在不同的语言( c++ with epoll, golang ),并且都关闭 nagle 的情况下,陆续都出现了延迟 1 秒以上返回心跳回复的包。
200 左右的客户端。之前还有一些业务逻辑在这个 tcp 上跑。现在业务逻辑全部去除,写了纯粹的 echo 的服务端和客户端的实现。还是能看到这些现象 |
29
zhicheng 2016-02-20 15:55:34 +08:00 2
TCP 协议本身没有心跳,不会自已断开。 client 和 server 建立连接之后如果没有通信,你即使拔掉网线再插上连接都不会断。
心跳一定要在业务层做,并且一定不能单独开线程或进程。不然仅仅是一个检测网络是否连通,进程或系统是否挂掉的工具,这些用第三方就可以了。 |
32
leeyiw 2016-02-20 19:27:11 +08:00
给 LZ 一个排查的思路:
在 client 和 server 端把 echo 的日志打印出来,然后配合 tcpdump 把包也抓下来,遇到延时高的请求,这两者可以具体定位到是应用层处理有问题 or 网络的问题 or 协议栈的问题。 |
33
tftk 2016-02-20 19:43:12 +08:00
TCP 中的连接, keepalive 都是在两端抽象出来的概念,不是现实中的线,网络不通就会断,心跳包的存在也仅仅只是为了探测一下两端是否还在线。
|
34
firefox12 2016-02-20 20:40:06 +08:00
tcpdump 先在 2 边抓包 确定, 是服务器发包慢, 客户端收包慢 还是网络传输问题。
200 客户端都出现这个问题 C1000k 怎么办 |
35
snnn 2016-02-20 21:50:32 +08:00
@firefox12 嗯,我觉得你说的有道理。建议楼主认真考虑下这个。看看服务器端是否是每 3 秒收到了一个包, request 和 response 直接的间隔是多少。
|
36
eas OP |
37
yangxin0 2016-02-21 10:53:48 +08:00 via iPhone
检查服务器 liaten 的 backlog 是否过小
|
38
firefox12 2016-02-21 19:48:08 +08:00
之前有简单 tcpdump 过,好像是收发包都速度快的,好像是网络传输的问题。
..... 这是什么情况,看服务器的 tcpdump 数据 看 recv 和 send 的时间差是多少 如果是 1 秒多 那么就是服务器的问题,如果是 0.001 秒 那么服务器就没问题,要么就是客户端和网络。 然后看客户端的 send 和 recv 时间查,情况同上。 如果 2 个都是 0.001 秒 那么就是网络问题。 还有可能就是 客户端的 send 时间戳 你算得不对。 |