创建连接失败时发生了什么

00:00/00:00

这次带来的音乐是<<幽灵公主>>的插曲, Ashitaka And San, 音乐出现在影片结尾, 山兽神融入大山消失以后, 山上被烧掉的树重新发芽的场景中, 在看电影之前, 听过这首曲子, 但是并没有太大的感触, 但是在看到这个场景, 看着花草慢慢发芽, 看着整座山变绿, 音乐响起, 我很难描述当时的感觉, 希望大家没看过的可以去看一下

Alt text3bb28590-27bd-4fb0-a84e-fef7b6541792


 

  • 对方拒绝了你的连接并向你扔了一个错误码

连接失败是很常见的错误, 网络连接经过的步骤很多, 从源, 经过网络到达目的, 可能遇到的异常有很多, 结合平时的经验, 在这里写一点, 希望有人能从中获益

五元组分配问题

 

在创建连接时, 客户端进程首先向内核申请一个 socket, socket 由(源 ip, 源 port, 目标 ip, 目标 port, 协议) 五元组 组成, 在这个阶段可能出现第一个错误, 五元组是不能够重复的, 如果所有可能的组合都分配出去了, 就会出现分配失败

一般来说, 源 ip 只有一个(本机网卡地址, 如果有多个网卡或者绑定多个地址可能有多个), 受 int32数据结构限制, 源 port 有65535个(65535个肯定是够用了, 某人说), 目标 ip 有很多个, 发生这种错误时, 往往是频繁向某个固定的服务发送请求, 即固定的目标ip 和 port, 协议有好几种, 常见的有 tcp/udp/icmp 等等

一个常见的情况, 在本机向固定的 ip:port 使用短连接, 不停发送请求, 在一段时间后, 会发现无法发送了, 就是这种错误引起的

在客户端主动关闭连接以后, 虽然句柄释放了, 但是为了防止对端延迟发送的 ack 包干扰新连接, TIME_WAIT 状态还是存在的, 存在的时间可以调整, 一般是4分钟, TIME_WAIT 状态的连接也是占据五元组的, 在使用 tcp 连接短连接请求固定 ip:port时, 五元组中能够变化的元只有本地端口一种, 即平均每秒 qps 达到时便无法再分配新端口

客户端大量使用短连接向外发送请求的场景下很容易遇到这个问题

解决这个问题的几种思路是:

  1. 短连接改长连接, 减少本地端口占用次数, 同时避免了创建连接的网络来回消耗
  2. 修改TIME_WAIT 持续时间, 修改 MSL 时间有风险
  3. 打开端口重用, 有风险

fd 耗光

五元组申请完成之后, 需要与 fd 绑定, 每个进程有可用的 fd 上限, 如果可用的 fd 为0, 会报错

每个进程可用的 fd 数目可以配置, root 可以修改上限, 普通用户可以缩小进程可用的 fd, 但是不能超过 root 设置的上线

fd 耗光可能与程序有泄露, 打开的文件过多等有关系, 在确认没有问题的情况下, 可以调整数值

某些神奇的 bug

  1. 某些版本的libcurl 在连接超时设置为1000ms 以下时直接失败, 影响所有使用 libcurl 的上层应用, 如 php 的 curl 函数

服务端

防火墙问题

  1. 防火墙做了端口限制或者来源 ip 限制, 拒绝了连接
    检查 iptables 等配置即可
  2. nf_conntrack:tablefull,droppingpacket
    多看 dmesg, 一般的莫名其妙的异常都能在这里找到记录, 如 oom 的信息等等
    nf_conntrack是一个记录连接状态的模块, 在活跃的连接较多时, 有可能用满默认的记录空间, 造成丢包

可以根据情况调大记录空间大小, 关闭模块或者使用额外的标记不记录某些连接的状态

fd 耗光

同客户端, 在服务端, 每个连接都要消耗一个 fd, 如果有较多的客户端使用长连接并带有连接池, 或者服务端处理这个连接耗时过长导致连接累积, 都有可能出现耗光的问题

网卡软中断耗光cpu

网卡在接收到数据包时会产生中断, 硬中断由网卡发出, cpu 处理从网卡到内核的拷贝, 硬中断的处理是屏蔽同类中断的, 而且处理过程只有拷贝, 比较简单, 数据由内核到用户态是由软中断处理的, 这个过程包含了协议匹配, ip 层检错, 链路层检错等逻辑, 较为复杂, 处理消耗更多的 cpu, 在数据包较多的情况下, 容易造成 cpu 被软中断处理所耗光, 造成丢包

irq 是硬件产生中断时发送到 cpu 的标识, 有一个 irqbalance 进程可以根据负载情况决定把哪个 irq 发送到哪个 cpu 上, 如果不满意他的表现, 可以手动将网卡的 irq 绑定到指定的 cpu 上,
某些网卡可以产生多个 irq, 因此可以绑定到多个 cpu 上, 以避免中断全部由某个 cpu 处理, 这种网卡成为多队列网卡, 虽然支持, 默认也是不开的, 需要自行打开多队列并绑定cpu, 如果不打开多队列, 绑定多 cpu 是无效的(原因未知)

accept 不及时

连接创建之后, 放在队列里, 如果进程没有调用 accept取走, 队列满了以后会造成新连接不能创建的问题

所以应该尽可能使accept 步骤轻量, accept 之间不要处理复杂逻辑, 一般取出来交给其他线程处理, 自己本身要足够快

网络

不是很了解, 略过, 以后如果有机会了解, 会补上

打赏

4 thoughts to “创建连接失败时发生了什么”

  1. 不明觉厉(为啥评论不是必填项,姓名邮箱是……?)

评论已关闭