百万⻓连接连接数优化

背景:

原先⻓连接网关支持支撑1W的⻓连接,继续往上提升数量就上不去了,后面此功能归到我这里优化。

目前通过优化后,实际压测,达到50W的数量支撑。有了数量支撑后,接着开始优化连接数量性能,因为其实衡量长连接的性能主要是业务支撑(下推,上报),完成了连接数量支撑只是一个最基本的支撑

下面开始分享相关连接数量优化的过程以及排查思路。

  • IO模型
    压测客户端会产生的线程问题
  • TCP四元组
    客户端能发起的连接数
  • Too many open files异常(文件句柄)
    服务端能够支撑的连接数
  • 压测客户端
    压测的客户端具体如何配置
  • 压测流程
    压测的整体链路是如何的
  • ⻓连接服务优化
    针对服务端的应用进行优化
  • 内核网络参数调优
    建立大量连接通过fd即可,但是在连接建立后,⻓连接的数据的上下行也需要内核上的调优。

IO模型

IO指的是Input/Ouput,即输入输出。

一般IO模型分为五种:

  • 阻塞IO
  • 非阻塞IO
  • 多路复用IO
  • 信号驱动IO模型
  • 异步IO
    本文就不对其进行描述了,可网上自行查询

⻓连接服务器使用的Netty,使用的是Reacotor模型。Reactor模式是一种基于事件驱动的模式,适合做海量数据的
事件,属于同步非阻塞的一种NIO实现模式。

对于不同模型而言,使用的线程数也是不一样的。

如Netty的Reactor模型,主从可以设置个位数的线程来监听客户端的连接,但是旧版tomcat,则是阻塞IO的则是
一个线程一个连接。

在使用客户端机器压测时,如jmeter压测,是使用一个线程对应一个⻓连接,如果系统对线程数量限制的话,可能会出现超出系统可支撑线程数量的异常,具体需要根据不同系统配置。

TCP四元组

在这里插入图片描述
每创建一个TCP socket 会包含四元组信息。

TCP连接四元组分别是是源IP地址、源端口、目的IP地址和目的端口,当创建了一个⻓连接时,也会随之一个新的TCP四元组的对象。

TCP 四元组可以唯一的确定一个连接,注:如果任意一个变量不同,就可以代表是一个完全不同的新连接。

由于历史原因,分别是由16位来存储源端口号(ip)和目标端口号(port),这个值是short类型的,大小也是2^16-1,因为历史原因造成的不可改变的标准,所以连接范围的最大值是65535。

TCP也分成五/七元组,这里就不细说了,问题的本质都一样。

源端口

一个四元组可以支撑的连接数是由net.ipv4.ip_local_port_range 这个内核参数确定的,其表示向外连接的端口范
围。

在理想情况下,作为客户端,如果对同一个TCP请求数量(ip+端口值固定)超过65535一般会报错:Cannot assignrequested address

但是系统默认配置实际上是很小的。

[root@localhost msg-gateway]# sysctl -a |grep local_port
net.ipv4.ip_local_port_range = 32768 61000 # 只能支撑 28232的数量
  • 修改配置
vim /etc/sysctl.conf
net.ipv4.ip_local_port_range = 1024 65000 #能支撑63976数量
sysctl -p #输入此命令生效
  • 数量是不是只能限制在65535范围?
    答案是否定的
    根据四元组信息的定义。
    如果IP还有PORT其中一个发生了改变,那么他就是一条新的连接,也可以支撑到65535的数量。
    所以Liunx实际上可以支撑无数个⻓连接(如果系统抗得住)

  • 提升压测的数量

    • 客户端支持多IP
      网卡上配置多ip,在进行⻓连接压测的时候使用不同ip,相同端口去建立⻓连接请求
    • ⻓连接服务端支持监听多端口,客户端对其同一IP,不同端口建立⻓连接
      liunx在3.9内核版本中,引入了SO_REUSEPORT配置,其支持多个进程或者线程绑定到同一端口,提高服
      务器程序的吞吐性能。
      Netty在 4.0.16版本也进入了支持了该配置

Too many open files 异常

测试高并发,或者是测试过多的并发连接时难免会遇到这个问题。
这次⻓连接压测过程,通过内核环缓冲区日志也有看到相关报错。

# dmesg | tail -n 20
[63286.239125] too many open files
[63286.239129] too many open files
[63286.239133] too many open files
[63286.239136] too many open files

按照经验来说,出现此问题就是通过设置两个地方

  • ulimit -n xxxx (xxxx为可支持的文件句柄数)
  • 设置/etc/security/limits.conf 的nproc值

其实并没有完全解决此问题,下面来描述一下具体配置。

文件句柄以及文件描述符

服务端能支撑的⻓连接连接数,就是文件句柄的数量,只要配置得当,百万并不是问题。
文件句柄是什么

理解文件句柄之前,先看一下进程的数据结构。

  • 进程数据结构(task_struct)
    在这里插入图片描述

如上的task_struct,在Linux系统中进程的数据结构如上(线程和进程的数据结构一致)。
在进程创建时,系统会构造task_stuct数据结构初始化一个进程。
我们抛出问题的文件句柄/描述符就在数据结构里的 “files” 里。

数据结构的其他就不具体展开描述了,下面继续解释files。

进程会使用一个files_struct 结构(如图所示:files指向的files_struct)来记录文件描述符的使用情况,
files_struct 结构称为用户打开文件表。

  • files指针指向的链路
    在这里插入图片描述
    如图链路所示
    1.files_struct 中存在一个叫做fdtable结构,其名为文件描述符表
    2.fdtable包含的file **fd 这个数组,他的数组下标就是文件描述符,并且file数组的大小是由系统内核决定的
    3.文件描述符的 0、1、2 三个描述符总是默认分配给标准输入、标准输出和标准错误。( 命令2>&1 由来)
    4.数组元素中记录了当前进程打开的每一个文件的指针,这个文件是 Linux 中抽象的文件,可能是磁盘上的一个文件,也可能是一个 socket。

总:
一个进程在运行中会打开很多资源,如文件file、通讯连接socket、正在监听的端口等,都统称为文件句柄。
在linux系统中,任何东⻄都是文件,所以当一个进程打开的文件句柄数超过系统内核限制(file数组大小)时,就会报出Too many open files。

文件句柄数量配置

实现

在知道相关配置之前,先看一下liunx系统创建socket相关内核对象的流程
分成两个部分

  • 申请句柄号(寻找fdtable中可以用的文件数组)
  • 创建真正的 file 内核对象

申请句柄号

寻找可用文件数组文件(file获取申请可用fd )

判断是否超过 soft nofilefs.nr_open,如果超过了,则报错“Too many open files”

  • 进程级:单个进程可打开的最大数量,通过fs.nr_open参数可修改
  • 用户级:指定用户可打开的最大数量,修改/etc/security/limits.conf,为上文的soft nofile

这两个参数的作用一样,但是都必须设置。
Linux之所以分两个参数来控制,是因为 fs.nr_open 是系统全局的,而soft nofile 则可以分用户来分别控制

创建file内核对象
申请句柄号后,会调用相关方法来申请 file 内核对象
这时候会使用 fs.file-max 这个系统参数比较是否超过整个系统上可打开的最大文件数

  • 系统级:当前系统可打开的最大数量,通过fs.file-max参数可修改

具体配置
注:以下fd都设置为可支撑100万

  • 系统级/进程级限制
vi /etc/sysctl.conf
fs.nr_open=1100000 #单个进程可打开的最大数量
fs.file-max=1100000 #当前系统可打开的最大数量
sysctl -p #使用sysctl -p 执行

系统内核参数配置必须使用 sysctl.conf 修改,包括查看具体内核值时,建议使用 sysctl -a |grep xxx (xx为字段名)。

因为如果通过echo /proc/sys/fs/nr_open 直接写入路径的方式会出现重启后失效的情况。

  • limits文件
vi /etc/security/limits.conf
* soft nofile 1000000
* hard nofile 1000000

1.如果想加大 soft nofile, 那么 hard nofile 也需要一起调整。
因为如果 hard nofile 设置的低, soft nofile 设置的再高都没用,实际生效的值会按二者里最低的来。

2.加大了 hard nofile,那么 fs.nr_open 也都需要跟着一起调整。
如果 hard nofile 设置的比 fs.nr_open 大了,后果比较严重。会导致该用户无法登陆。如果设置的是 * 的话,
那么所有的用户都无法登陆。

3.确认以下文件是有配置 session required pam_limits.so
/etc/pam.d/login
/etc/pam.d/system-auth
/etc/pam.d/sshd
/etc/ssh/sshd_config文件配置是否UsePAM=yes
否则在重启后依旧会失效。

  • ulimit (不推荐)
    这是一个经典命令
ulimit -n xxxx

设置后,启动进程就可以和配置limits.conf的效果一致(但是仍不得超过fs.nr_open 以及fs.file-max配置的大小)
但是不推荐此命令,只支持当前用户,重启or开新shell就无效(除非机器不能重启)。

  • 查看是否生效
    可以直接输入pid,查看进程的内存映射文件。
cat /proc/pid/limits

压测流程

架构图

在这里插入图片描述
服务是由nginx作为网关,请求分发到⻓连接服务集群中

存在问题

单独压测⻓连接服务
单独压测⻓连接服务器到100W不存在问题,在文件句柄/内存/网络内核支撑充足的情况下,数量再往上也不会存
在这里插入图片描述

在问题,如上图所示

  • 接入nginx
    在这里插入图片描述

由于需要接入nginx,会存在一个问题,也就是最上面四元组限制的问题。
即port+ip固定的情况下,nginx和单台⻓连接服务最大只能达到65535的数量,如上图所示。

解决

如最上面的TCP四元组描述的,TCP 四元组可以唯一的确定一个连接,如果任意一个变量不同,就可以代表是一个完全不同的新连接

为解决此问题,为⻓连接服务设置了多个端口 /多个ip,并且让nginx对相同ip的两个端口做负载,进行数量扩容。
在这里插入图片描述
上诉方式也可以使用keepalive vip 的方式

开始压测

压测客户端可以使用jmeter进行压测。
可以通过配置多ip的场景进行压测,也可以用不同机器进行压测。
但是需要配置好端口区间/句柄,才能保证数量客户端单台达到6W

  • windows参考
    在这里插入图片描述

  • liunx参考

vi /etc/sysctl.conf
fs.file-max = 100000
fs.nr_open = 100000
net.ipv4.ip_local_port_range = 1024 65535
sysctl -p

1.永久修改fd

vi /etc/security/limits.conf
* soft nofile 70000
* hard nofile 70000
注意:hard nofile 不能高于 fs.nr_open,否则重启后无法登录

2.不打算永久修改fd

ulimit -n 70000
  • mac参考

1.开启性能模式:https://support.apple.com/zh-cn/HT202528
(注开启了性能模式后,相关参数会自己翻倍,但是会导致启动变慢)

2.修改limit
https://www.dazhuanlan.com/ethan168/topics/1554420

压测机器

目前可以使用的压测机器如下

⻓连接服务器: 190.9.49.35
nginx网关服务器:190.0.40.198
压测客户端:
1)190.0.40.72
2)190.0.40.200

两种压测场景

本次压测两种场景方式

单独压测⻓连接服务器,达到100W

由于单独压测⻓连接网关,nginx服务器也可以拿来当作压测客户端
⻓连接服务器: 190.0.40.35
压测客户端 :
1)190.0.40.72
2)190.0.40.200
3)190.0.40.198

3个客户端压测100W的情况下,由于每台的ip+port只能支持6W。

对⻓连接网关服务,给进程开放 6 个端口就行(3台x6万连接x6个端口 =108万) ,同理,让运维给⻓连接服务器
设置多ip策略也行(6个ip)。

  • 访问端口:
    190.0.40.35:9000
    190.0.40.35:9001
    190.0.40.35:9002
    190.0.40.35:9003
    190.0.40.35:9004
    190.0.40.35:9005
  • 图示
    在这里插入图片描述

压测nginx网关服务器,达到10W

在压测流程章节中说过。
nginx数量无法像单独压测⻓连接服务器一样可以达到100W,所以要求的达到10W即可。
⻓连接服务器: 190.0.40.35
nginx网关服务器:190.0.40.198
压测客户端:
1)190.0.40.72
2)190.0.40.200

对⻓连接网关服务,开放 2 个端口就行(nginx与同一个⻓连接服务器建立两个端口负载,可以支撑 12W的⻓连接) ,同理,让运维给同一个⻓连接服务器设置多ip负载也行(2个ip)。

  • 端口:
    190.0.40.35:9000
    190.0.40.35:9002

nginx负载的配置:

#⻓链接网关-ws
upstream newbas.msg.gw1 {
server 190.0.40.35:9002 weight=1 max_fails=10 fail_timeout=30s;
server 190.0.40.35:9000 weight=1 max_fails=10 fail_timeout=30s;
}

压测结果查看

客户端运行:

在这里插入图片描述

以下是压测到50万的情况,由于压测客户端还存在其他服务,多台合并起来内存不够支撑100W

  • 每台客户端启动情况
    在这里插入图片描述

  • 连接数
    在这里插入图片描述

  • cpu
    在这里插入图片描述

  • 内存
    在这里插入图片描述

  • 内核资源
    在这里插入图片描述
    在这里插入图片描述

网络内核参数调优

其实内核参数优化,首先应尽可能使用最新的Linux内核。

TCP的最佳实践以及影响其性能的底层算法一直在与时俱进,而且大多数变化只在最新内核中才有实现。一句话,让你
的服务器跟上时代是优化发送端和接收端TCP栈的首要措施。 《Web性能权威指南》

通过之前修改句柄后,理论上⻓连接服务的fd(文件句柄数)可以支持100万连接(socket)了,但是客户端直接以百万的数量冲上去一般也不会直接成功,
因为只是空连接支持,携带的报文以及数据的上下行也会影响,需要慢慢调优。

调整句柄数量足够大后,后续侧重点在网络/内存/CPU开销的情况。

内存

每一条TCP连接需要file,socket等内核对象,在理论上来说,一条空TCP连接需要消耗3.3KB。
所以在创建够多的TCP前提下,需要足够多的内存。100W条⻓连接大约需要3.14G的内存。
如果在内存足够的情况下,支撑一百万的空连接还是没问题的,但是需要考虑到服务用到的内存(JVM等)

[root@localhost ]# free -h
total used free shared buffers cached
Mem: 15G 10G 5.4G 12M 297M 2.7G

网络

Liunx网络包接收的链路很⻓,网卡驱动、协议栈,内核ksoftirqd线程等等,下面罗列了一些这次调优排查时遇到的问题。
但是因为链路很⻓,实际上排查问题的时候要考虑的东⻄也是会比较多,如果感兴趣可以去网上学习一下Liunx网络接收包的流程,下文只是提供一下这次排查/优化内容。
在这里插入图片描述
如上图所示,可以对照下方文章进行阅读(此文章罗列了从硬件网卡到协议栈中网络包的排查工具):
网络排查

下面是这次压测调优时用到的排查工具

dmesg 查看内核日志

在进行压测过程中,用户级的日志(进程日志),并不会告诉你具体什么原因导致连接数量上不去了。
这时候需要系统层面的日志:

  • /var/log/message
  • journalctl
  • dmesg

通过如下语句可以对日志出错的每一处出现的场景进行优化,如下
dmesg | tail -n 50
dmesg用来显示内核环缓冲区(kernel-ring buffer)内容,内核将各种消息存放在这里,检查系统 tcp 相关的异
常。

查看网络包的状态

在这里插入图片描述
Liunx网络收包过程链路较⻓,可以通过此统计命令排查具体出现了什么问题

获取socket统计信息

大多数情况下,查看socket统计信息都是使用netstat
在这里插入图片描述
当服务器的socket连接数量变得非常大时,无论是使用netstat命令还是直接cat /proc/net/tcp,执行速度都会很
慢。

这种情况下建议使用ss命令,ss是Socket Statistics的缩写。

可以用来获取socket统计信息,类似netstat的内容,但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比netstat更快速更高效。

  • 查看端口为9000的tcp连接数
[root@localhost ~]# ss -tan 'sport = :9000' | awk '{print $(NF)" "$(NF-1)}' | sed 's/:
[^ ]*//g' | sort | uniq -c
19500
1 Address Peer
  • 查看端口使用的进程
[root@localhost ~]# ss -nlp | grep 9000
tcp LISTEN 0 1024 :::9000 :::* users:
(("java",pid=1170,fd=717))
  • 统计socket信息
[root@localhost ~]# ss -s
Total: 79631 (kernel 80631)
TCP: 78743 (estab 78669, closed 16, orphaned 0, synrecv 0, timewait 3/0), ports 0
Transport Total IP IPv6
* 80631 - -
RAW 0 0 0
UDP 30 14 16
TCP 78727 256 78471
INET 78757 270 78487
FRAG 0 0 0

发送数据/建立⻓连接时调优

在如此海量并发环境中,下面会举例一些此次过程中出现的问题。

单纯修改参数确实有效,但是还是建议了解TCP协议栈,以及内核参数的实际含义才能更好的知道问题是如何解决的。

建立连接时过多TIME_WAIT

在压测过程中,查看当前建立的TCP数量,通过ss命令

[root@localhost ~]# ss -s
Total: 79631 (kernel 80631)
TCP: 78743 (estab 278669, closed 16, orphaned 0, synrecv 0, timewait 3459/0), ports 0
Transport Total IP IPv6
* 80631 - -
RAW 0 0 0
UDP 30 14 16
TCP 78727 256 78471
INET 78757 270 78487
FRAG 0 0 0
net.ipv4.tcp_tw_reuse = 1 //如果服务端和客户端都开启了tcp_timestamps
net.ipv4.tcp_max_tw_buckets = 20000//kernel中最多存在的TIME_WAIT数量
net.ipv4.tcp_fin_timeout = 30 //服务器主动关闭连接时,socket保持在FIN-WAIT-2状态的最大时间
[root@localhost ~]# netstat -s | grep -E 'drop|overflow'
13586 outgoing packets dropped
29 ICMP packets dropped because they were out-of-window
1258811 times the listen queue of a socket overflowed
1287372 SYNs to LISTEN sockets dropped

从上信息看到,已经建立了278669条连接,TIME_WAIT有3千多个,对于TIME_WAIT数量过多,需要分成客户端以及服务端讨论,但是TIME_WAIT数量实际不会造成什么影响。

但是对于我们压测的场景(⻓连接数量的保持)来说,核心关注的一点是 net.ipv4.tcp_max_tw_buckets 。
如果TIME_WAIT的数量超过这个值,则会主动关闭新增的连接,所以需要优化TIME_WAIT数量的增⻓
针对客户端/服务端出现TIME_WAIT,有篇文章解释得挺不错的:
TIMEWAIT处理

总结参数:

net.ipv4.tcp_tw_reuse = 1 //如果服务端和客户端都开启了tcp_timestamps
net.ipv4.tcp_max_tw_buckets = 20000//kernel中最多存在的TIME_WAIT数量
net.ipv4.tcp_fin_timeout = 30 //服务器主动关闭连接时,socket保持在FIN-WAIT-2状态的最大时间

排查网络包状态

网络包的状态非常多,可以核心关注几个地方

  • 半连接/全连接队列是否溢出
[root@localhost ~]# netstat -s | grep -E 'drop|overflow'
13586 outgoing packets dropped
29 ICMP packets dropped because they were out-of-window
1258811 times the listen queue of a socket overflowed
1287372 SYNs to LISTEN sockets dropped

建议理解TCP半连接和全连接的场景。

连接队列大小对连接数没影响,但是如果队列满了,会丢包,导致客户端connect超时/中断。

1.半连接
只要保证 net.ipv4.tcp_syncookies = 1 就不会出现半连接队列丢包

2.全连接队列
配置服务端(⻓连接服务)的backlog参数,以及设置net.core.somaxonn内核参数。
全连接队列最大大⻓度是服务端时传入的backlog和net.core.somaxonn问较小的那个值。如果需要加大全连接
队列⻓度,需要修改内核的somaxconn参数。
服务端以及系统内核设置后可以通过 ss -nltp |grep 端口 查看是否生效

  • socket 缓冲区是否出溢出
[root@localhost ~]# netstat -s | grep "buffer errors"
2465 receive buffer errors
2315 send buffer errors

需要设置TCP接收/发送缓冲区,下面有提到

too many orphaned sockets

关注dmesg的信息,出现了too many orphaned sockets报错。
其表示系统的sockets资源耗尽,需要寻找具体哪个内核资源出现了耗尽的情况,需要逐步排查

  • TCP接收缓冲区

如果每条TCP发送数据,需要对TCP内核对象设置接收缓存区。
接收缓存区大小是可以配置的,通过sysctl命令就可以查看。

$ sysctl -a | grep rmem
net.ipv4.tcp_rmem = 4096 87380 8388608 //TCP接收缓存(用于TCP接收滑动窗口)的最小值、默认值、最
大值,单位是字节
net.core.rmem_default = 212992 //内核套接字接收缓存区的默认大小
net.core.rmem_max = 8388608 //内核套接字接收缓存区的最大大小

tcp_rmem参数值分别对应low,pressure,high三个阈值,

第一个值是为TCP连接所需分配的最少字节数。该值默认是4K,最大为8MB多。
即,有数据发送的时候内核需要至少为对应的socket再分配4K内存,甚至可能更大。

  • TCP发送缓冲区

同内核发送数据时,也需要在内存分配发送缓冲区

$ sysctl -a | grep wmem
net.ipv4.tcp_wmem = 4096 65536 8388608 //TCP发送缓存(用于TCP发送滑动窗口)的最小值、默认值、最
大值,单位是字节
net.core.wmem_default = 212992 //内核套接字发送缓存区的默认大小
net.core.wmem_max = 8388608 //内核套接字发送缓存区的最大大小

tcp_wmem中的第一个值是发送缓存区的最小值,默认也是4K。当然了如果数据很大的话,该缓存区实际分配的也会比默认值大。

内核接收/发送数据时,都需要消耗CPU去执行接收/发送过程

所有调优的配置总结

打开/etc/sysctl.conf文件,如下添加或改动,然后执行sysctl -p命令即可生效。

#文件句柄相关
fs.file-max = 1100000
fs.nr_open = 1100000
#源端口相关
net.ipv4.ip_local_port_range = 1024 65000
# time_out相关
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_max_tw_buckets = 20000
#全/半链接相关
net.core.somaxonn = 8096
net.ipv4.tcp_syncookies = 1
# 缓冲区相关
net.ipv4.tcp_rmem = 4096 87380 8388608
net.core.rmem_default = 212992
net.core.rmem_max = 8388608
net.ipv4.tcp_wmem = 4096 65536 8388608
net.core.wmem_default = 212992
net.core.wmem_max = 8388608

并且配合limit配置文件,即可成功支持百万连接

vi /etc/security/limits.conf
* soft nofile 1000000
* hard nofile 1000000

CPU

使用TOP即可

top (输入1)
Cpu0: 31.3%us, 4.7%sy, 0.0%ni, 82.5%id, ... 0.2%si, 0.0%st
Cpu1: 32.3%us, 7.4%sy, 0.0%ni, 51.5%id, ... 0.1%si, 0.0%st
Cpu2: 36.6%us, 4.5%sy, 0.0%ni, 77.7%id, ... 0.1%si, 0.0%st
Cpu3: 35.9%us, 3.6%sy, 0.0%ni, 79.3%id, ... 0.4%si, 0.0%st
Cpu4: 37.7%us, 4.9%sy, 0.0%ni, 75.3%id, ... 0.3%si, 0.0%st
Cpu5: 33.6%us, 6.6%sy, 0.0%ni, 68.1%id, ... 0.1%si, 0.0%st
Cpu6: 38.1%us, 4.9%sy, 0.0%ni, 75.7%id, ... 0.4%si, 0.0%st
Cpu7: 31.1%us, 5.8%sy, 0.0%ni, 71.4%id, ... 0.1%si, 0.0%st
  • us:用户空间占用CPU百分比(Host.cpu.user)
  • sy:内核空间占用CPU百分比(Host.cpu.system)
  • ni:用户进程空间内改变过优先级的进程占用CPU百分比
  • id:空闲CPU百分比(Host.cpu.idle)
  • wa:等待输入输出的CPU时间百分比
  • hi:硬件中断
  • si:软件中断
  • st:实时

某一项过高的情况排查,用户(%user)、Nice(%nice)、系统(%system) 、等待 I/O(%iowait) 、中断
(%irq)以及软中断(%softirq)这几种不同 CPU 的使用率。比如:

  • us和ni 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进程的性能问题
  • sy 高,说明内核态占用了较多的 CPU,所以应该着重排查内核线程或者系统调用的性能问题
  • wa 高,说明等待 I/O 的时间比较⻓,所以应该着重排查系统存储是不是出现了 I/O 问题
  • si和hi 高,说明软中断或硬中断的处理程序占用了较多的 CPU,所以应该着重排查内核中的中断服务程序
  • st 高,那么可能跟同物理机下的其他虚拟机存在资源抢占有关
  • 每一个核的CPU情况是否平均:如果不平均需要从多个地方定位,如中断,网卡队列,应用层(如这次的
    nginx多核cpu配置)等

每种情况都有不同的排查方式,此文就不描述
从上面的情况来看,出现问题的是用户态进程占用较多CPU,下面进入详细排查⻓连接服务

⻓连接服务优化

业务优化

这里就略过了

Netty 调优

Netty进程多端口

在上方压测场景的章节,需要设置⻓连接服务多个端口的情况

在开发相关代码时,需要确定liunx的内核支持SO_REUSEPORT配置,liunx版本要高于等于3.9

[root@localhost ~]# uname -rs
Linux 3.10.0-957.el7.x86_64
  • 相关实现
public void start() throws InterruptedException {
try {
	List<Integer> ports = Arrays.asList(9000, 9001,9002,9003,9004,9005);
	Collection<Channel> channels = new ArrayList<>(ports.size());
	for (int port : ports) {
		Channel serverChannel = server.bind(port).sync().channel();
		channels.add(serverChannel);
	}
	for (Channel ch : channels) {
		ch.closeFuture().sync();
	}
		log.info("server started");
	} finally {
	// 释放线程池资源
	workerGroup.shutdownGracefully().sync();
	bossGroup.shutdownGracefully().sync();
	}
}
public WebSocketServer() {
	bossGroup = new NioEventLoopGroup();
	workerGroup = new NioEventLoopGroup(64);
	server = new ServerBootstrap();
	server.option(ChannelOption.SO_REUSEADDR, true)
	...........
}

Netty核心配置

核心线程

  • bossGroup
    线程数量为1

  • workerGroup
    线程数量设置为 cpu核心数*2

⻓连接服务器Netty配置的值
以下是这次Netty配置的参数

  • ChannelOption.CONNECT_TIMEOUT_MILLIS =30000
    超时时间设置,这个值可以参照实际客户端的情况,进行比较配置
  • ChannelOption.SO_BACKLOG = 1024
    全连接队列大小,理解这个参数需要理解TCP半连接和全连接的场景。backlog对连接数没影响,但是如果队列满了,会丢弃握手请 求,导致客户端connect超时/中断。
    注:backlog参数,都是在服务端的Socket配置。同时全连接队列最大大⻓度是服务端时传入的backlog和
    net.core.somaxonn问较 小的那个值。如果需要加大全连接队列⻓度,需要修改内核的somaxconn参数。
  • ChannelOption.TCP_NODELAY = true
    相关传输的报文不会太大,此参数在牺牲延时,可以有效提高网络的有效负载

以上为这次用到的配置参数,其他Netty可配制的参数可以参考: https://blog.csdn.net/wangguodong1993/articl
e/details/124665327

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值