ads:

关注以下公众号查看更多文章

 

      上一节我们建立了各种监控,容器的cpu、内存、接口和微服务的访问量、phpfpm进程被消耗的数量等,在grafana上展示,均没有发现异常,然而jmeter的结果断言,有一半的请求接口返回了Yar_Client_Transport_Exception curl exec failed,“Timeout was reached”。

        出现这个问题的原因是 yar框架底层基于curl,而并没有对curl进行优化导致部分请求被丢包。我们需要先启动一个初始化容器,在该容器中初始化网络参数优化docker容器的网络性能

        脚本如下

#!/bin/sh

echo "start optimize"

sysctl -w net.ipv4.tcp_max_syn_backlog=16384
sysctl -w net.ipv4.tcp_max_tw_buckets=32768
sysctl -w net.core.somaxconn=32768
sysctl -w net.ipv4.ip_local_port_range="10000 61000"

echo "optimize done"

TCP SYN_REVD, ESTABELLISHED 状态对应的队列

TCP 建立连接时要经过 3 次握手,在客户端向服务器发起连接时,对于服务器而言,一个完整的连接建立过程,服务器会经历 2 种 TCP 状态:SYN_REVD, ESTABELLISHED

对应也会维护两个队列:

  1. 一个存放 SYN 的队列(半连接队列)
  2. 一个存放已经完成连接的队列(全连接队列)

当一个连接的状态是 SYN RECEIVED 时,它会被放在 SYN 队列中

当它的状态变为 ESTABLISHED 时,它会被转移到另一个队列。所以后端的应用程序只从已完成的连接的队列中获取请求

如果一个服务器要处理大量网络连接,且并发性比较高,那么这两个队列长度就非常重要了。因为,即使服务器的硬件配置非常高,服务器端程序性能很好,但是这两个队列非常小,那么经常会出现客户端连接不上的现象,因为这两个队列一旦满了后,很容易丢包,或者连接被复位。所以,如果服务器并发访问量非常高,那么这两个队列的设置就非常重要了。

Linux backlog 参数意义

对于 Linux 而言,基本上任意语言实现的通信框架或服务器程序在构造 socket server 时,都提供了 backlog 这个参数,因为在监听端口时,都会调用系统底层 API: int listen(int sockfd, int backlog);

listen 函数中 backlog 参数的定义如下:

Now it specifies the queue length for completely established sockets waiting to be accepted,instead of the number of incomplete connection requests. The maximum length of the queue for incomplete sockets can be set using the tcp_max_syn_backlog sysctl. When syncookies are enabled there is no logical maximum length and this sysctl setting is ignored.If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN(128 default), it is silently truncated to SOMAXCONN.

backlog 参数描述的是服务器端 TCP ESTABELLISHED 状态对应的全连接队列长度。

ESTABLISHED列长度如何计算?

如果 backlog 大于内核参数 net.core.somaxconn,则以 net.core.somaxconn 为准,

即全连接队列长度 = min(backlog, 内核参数 net.core.somaxconn),net.core.somaxconn 默认为 128。

这个很好理解,net.core.somaxconn 定义了系统级别的全连接队列最大长度,

backlog 只是应用层传入的参数,不可能超过内核参数,所以 backlog 必须小于等于 net.core.somaxconn。

SYN_RECV队列长度如何计算?

半连接队列长度由内核参数 tcp_max_syn_backlog 决定,

当使用 SYN Cookie 时(就是内核参数 net.ipv4.tcp_syncookies = 1),这个参数无效,

半连接队列的最大长度为 backlog、内核参数 net.core.somaxconn、内核参数 tcp_max_syn_backlog 的最小值。

即半连接队列长度 = min(backlog, 内核参数 net.core.somaxconn,内核参数 tcp_max_syn_backlog)。

这个公式实际上规定半连接队列长度不能超过全连接队列长度,但是tcp_syncooking默认是启用的,如果按上文的理解,那这个参数设置没有多大意义

其实,对于 Nginx/Tomcat 等这种 Web 服务器,都提供了 backlog 参数设置入口,当然它们都会有默认值,通常这个默认值都不会太大(包括内核默认的半连接队列和全连接队列长度)。如果应用并发访问非常高,只增大应用层 backlog 是没有意义的,因为可能内核参数关于连接队列设置的都很小,一定要综合应用层 backlog 和内核参数一起看,通过公式很容易调整出正确的设置

在 TIME_WAIT 数量等于 tcp_max_tw_buckets 时,不会有新的 TIME_WAIT 产生。

tcp_max_tw_buckets 应该如何配置

如果不是类似 Nginx 之类的中间代理(即不担心端口耗尽),你通常不用关心这个值,使用官方默认的就好,甚至官方建议在内存大的情况下可以增加这个值。

类似 Nginx 之类的中间代理一定要关注这个值,因为它对你的系统起到一个保护的作用,一旦端口全部被占用,服务就异常了。 tcp_max_tw_buckets 能帮你降低这种情况的发生概率,争取补救时间。

这个值可能有什么不好的影响

在完全下面 2 条完全满足的情况下

  1. 当前服务器主动关闭连接
  2. 当前服务器 TIME_WAIT 数等于或大于 tcp_max_tw_buckets

可能会出现两种异常情况:
① 对端服务器发完最后一个 Fin 包,没有收到当前服务器返回最后一个 Ack,又重发了 Fin 包,因为新的 TimeWait 没有办法创建 ,这个连接在当前服务器上就消失了,对端服务器将会收到一个 Reset 包。因为这个连接是明确要关闭的,所以收到一个 Reset 也不会有什么大问题。(但是违反了 TCP/IP 协议)
② 因为这个连接在当前服务器上消失,那么刚刚释放的端口可能被立刻使用,如果这时对端服务器没有释放连接,当前服务器就会收到对端服务器发来的 Reset 包。如果当前服务器是代理服务器,就可能会给用户返回 502 错误。(这种异常对服务或者用户是有影响的)

建议

综合收益与成本,以下给出我的建议:
在只有 60000 多个端口可用的情况下,配置为

net.ipv4.tcp_max_tw_buckets = 55000

(在尽可能不违反 TCP/IP 协议的情况下保证系统的可用性)

镜像制作Dockerfile

FROM busybox

COPY docker-entrypoint.sh /usr/local/bin/

ENTRYPOINT ["docker-entrypoint.sh"]

增加初始化容器sysctl-optimizer

在项目的deployment.yaml文件中增加一个初始化容器,优化网络参数

      initContainers:
        - image: dockerhub.qingcloud.com/mustafa_public/netoptimizer:v1
          name: sysctl-optimizer
          securityContext:
            privileged: true

参考资料

Linux(debian)的网络内核参数优化来提高服务器并发处理能力

 相关链接

手把手教你部署nginx+php

php和nginx镜像合并 && 代码打包到镜像

nginx-php镜像安装常用软件

yaf && yar微服务/hprose微服务 镜像初始化

常用开发工具:php_codesniffer代码规范检查&修复、phpstan语法检查、phpunit单元测试

.gitlab-ci.yaml自动镜像打包&&互联网企业规范化上线流程(上)

kustomize/kubectl自动镜像部署&&互联网企业规范化上线流程(下)

apisix网关、JMeter压测  

prometheus/grafana监控数据收集与展示

k8s容器网络性能调优

supervisor进程管理

安装opcache和apcu

APM性能监测工具skywalking

链路跟踪工具zipkin

phpfpm和nginx配置

php整合apollo配置中心

php rdkafka操作kafka消息队列

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐