网络知识 娱乐 惊群与性能优化 - Linux C++网络编程(三十一)

惊群与性能优化 - Linux C++网络编程(三十一)

一:cpu占比与惊群

top -p pid,推荐文章:https://www.cnblogs.com/dragonsuc/p/5512797.html

惊群:1个master进程  4个worker进程

一个连接进入,惊动了4个worker进程,但是只有一个worker进程accept();其他三个worker进程被惊动,这就叫惊群;

但是,这三个被惊动的worker进程都做了无用功【操作系统本身的缺陷】;

官方nginx解决惊群的办法:锁,进程之间的锁;谁获得这个锁,谁就往监听端口增加EPOLLIN标记,有了这个标记,客户端连入就能够被服务器感知到;

3.9以上内核版本的linux,在内核中解决了惊群问题;而且性能比官方nginx解决办法效率高很多;

    reuseport【复用端口】,是一种套接字的复用机制,允许将多个套接字bind到同一个ip地址/端口上,这样一来,就可以建立多个服务器

    来接收到同一个端口的连接【多个worker进程能够监听同一个端口】;

大家注意一点:

a)很多 套接字配置项可以通过setsockopt()等等函数来配置;

b)还有一些tcp/ip协议的一些配置项我们可以通过修改配置文件来生效;

课后作业:

(1)在worker进程中实现ngx_open_listening_sockets()函数;

(2)观察,是否能解决惊群问题;

(3)如果在master进程中调用ngx_open_listening_sockets()函数,那么建议master进程中把监听socket关闭;

if (setsockopt(isock, SOL_SOCKET, SO_REUSEPORT,(const void *) &reuseport, sizeof(int))== -1)

二:性能优化大局观

a)性能优化无止境无极限

b)没有一个放之四海皆准的优化方法,只能够一句具体情况而定

c)老师在这里也不可能把性能优化方方面面都谈到,很多方面,大家都需要不断的探索和尝试;

从两个方面看下性能优化问题;

软件层面:

a)充分利用cpu,比如刚才惊群问题;

b)深入了解tcp/ip协议,通过一些协议参数配置来进一步改善性能;

c)处理业务逻辑方面,算法方面有些内容,可以提前做好;

硬件层面【花钱搞定】:

a)高速网卡,增加网络带宽;

b)专业服务器;数十个核心,马力极其强;

c)内存:容量大,访问速度快;

d)主板啊,总线不断升级的;

三:性能优化的实施

(3.1)绑定cpu、提升进程优先级

a)一个worker进程运行在一个核上;为什么能够提高性能呢?

cpu:缓存;cpu缓存命中率问题;把进程固定到cpu核上,可以大大增加cpu缓存命中率,从而提高程序运行效率;

worker_cpu_affinity【cpu亲和性】,就是为了把worker进程固定的绑到某个cpu核上;

ngx_set_cpu_affinity,ngx_setaffinity;

b)提升进程优先级,这样这个进程就有机会被分配到更多的cpu时间(时间片【上下文切换】),得到执行的机会就会增多;

setpriority();

干活时进程 chuyuR状态,没有连接连入时,进程处于S

pidstat - w - p 3660 1    看某个进程的上下文切换次数[切换频率越低越好]

cswch/s:主动切换/秒:你还有运行时间,但是因为你等东西,你把自己挂起来了,让出了自己时间片。

nvcswch/s:被动切换/秒:时间片耗尽了,你必须要切出去;

c)一个服务器程序,一般只放在一个计算机上跑,专用机;

(3.2)TCP / IP协议的配置选项

这些配置选项都有缺省值,通过修改,在某些场合下,对性能可能会有所提升;

若要修改这些配置项,老师要求大家做到以下几点:

a)对这个配置项有明确的理解;

b)对相关的配置项,记录他的缺省值,做出修改;

c)要反复不断的亲自测试,亲自验证;是否提升性能,是否有副作用;

五:TCP / IP协议的配置选项

(3.1)绑定cpu、提升进程优先级

(3.2)TCP / IP协议的配置选项

(3.3)TCP/IP协议额外注意的一些算法、概念等

a)滑动窗口的概念

b)Nagle算法的概念

c)Cork算法

d)Keep - Alive机制

e)SO_LINGER选项

五:TCP/IP协议的配置选项

a) TCP_DEFER_ACCEPT参数

用法范例:

setsockopt( listen_fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(int) )

作用:一般三路握手后我们就可以用accept()函数把这个连接从 已完成连接 队列 中就拿出来了,用了这个选项之后,只有客户端往这个连接上发送数据了,accept()才会返回【而不再是三次握手就返回】,那是否有可能有用户连着你不发数据【恶意攻击】,那我这个时候我用这个选项不会触发accept()返回,因为唤醒accept()肯定会有一些系统上下文切换的,这也是代价;

b)tcp参数,这些资料也是老师参考了一些网上对nginx的一些性能优化配置方法,大家可以参考借鉴,其实类似这样的参数非常多,大家完全可以自己百度慢慢认识;

/etc/sysctl.conf文件中有一些配置项,可能有的影响客户端,有的影响服务器端;

net.ipv4.tcp_syn_retries【客户端】:主动建立连接时,发送syn的重试次数;

net.ipv4.ip_local_port_range【客户端】:主动建立时,本地端口范围,大家都知道,客户端端口一般系统分配;

net.ipv4.tcp_max_syn_backlog【服务器】:处于SYN_RCVD状态,未获得对方确认的连接请求数;就是那个未完成连接队列的大小。第五章第四节有讲过listen队列;

net.core.somaxconn【服务器】:已完成连接队列【需要用 accept()取走】的大小受到该值的限制,此值是系统级别的最大的队列长度限制值;

net.ipv4.tcp_synack_retries【服务器】:回应 SYN 包时会尝试多少次重新发送初始 SYN,ACK 封包后才决定放弃

net.core.netdev_max_backlog:在网卡接收数据包的速率比内核处理这些包的速率快时,允许送到待处理报文队列的数据包的最大数目。缺省值1000,应对拼命发包攻击时可能有效;

net.ipv4.tcp_abort_on_overflow:超出处理能力时,对新来的syn连接请求直接回rst包;缺省是关闭的;

net.ipv4.tcp_syncookies:这个项也是防止一些syn攻击 用的,syn队列满的时候,新的syn将不再进入未完成连接队列,而是用一种syncookie技术进行三路握手,也就是说服务会计算出cookie然后把这个cookie通过syn/ack标记的包返回给客户端,客户端发送数据时,服务根据数据包中带的cookie信息来 恢复连接。这个选项可能有些副作用,因为syn/ack包会返回一个序列号信息,现在返回的信息变成了cookie,可能会使一些tcp协议的功能失效,大家用的时候要完全研究清楚这种参数;而且程序代码上是不是要做一些调整和配合都要搞清楚;因为老师没详细研究过这个参数,感觉这种方式似乎不需要accept()就能把 用户接入进来,所以感觉代码上有可能要调整;

net.ipv4.tcp_fastopen【客户端/服务器】:用于优化三次握手,默认不启用;

通过前面的学习,大家都知道了TCP的三次握手,其中的第三次握手是个ack包,大家抓一下包就会发现,这个包里一般是不携带额外数据的,但是这个包里是可以携带额外的数据的,怎么能携带上额外数据,有兴趣的同学可以测试,老师给大家提供一篇参照文章,“TCP连接建立的三次握手过程可以携带数据吗?”,因为这个涉及到tcp协议内的细节内容,老师不深入谈了:参考:http://0xffffff.org/2015/04/15/36-The-TCP-three-way-handshake-with-data/

三次握手大家都熟悉,syn,syn/ack,ack,下次重新连接还要进行三次握手,这很耗费性能,那如果syn/ack的时候给客户端回一个cookie,那么下次客户端重新连接到服务器的时候不用进行三次握手,而是 可以直接发送syn包,里边夹带cookie并夹带要发送的数据即可,那这样是不是省了好几步数据传输;不过要求c/s都要支持这种特性才能做到,因为客户端要发送一个带fast open cookie request请求的包给服务器的,而且你这种fastopen特性如果开启的话,可能还涉及到fastopen队列,这个队列的最大长度你可以也要考虑设置一下,这种探索或者说是代码怎么书写,如果大家有兴趣,可以自行探索,老师这里不带着大家一起探索;

net.ipv4.tcp_rmem:收数据缓存的最小值,默认值,最大值(单位:字节)

net.ipv4.tcp_wmem:发数据缓存的最小值,默认值,最大值(单位:字节)

tcp_adv_win_scale:这东西会把上边这个缓存拿出来一部分作为额外开销,这个数字用于确定拿出来多少作为额外开销;

其实还有很多的网络选项,老师就不在这里一一列举,大家可以通过各种搜索引擎来继续深入学习;

六:TCP/IP协议额外注意的一些算法、概念等

a)滑动窗口的概念:

tcp协议引入滑动窗口主要是为了解决高速传输和流量控制问题【限速问题】;老师希望大家对滑动窗口要有概念,这个概念和实现一般都会在操作系统内核里边干,但是老师仍旧希望大家能够了解相关的概念;

b)Nagle算法的概念:

这个算法是避免发送很小的报文,大家都知道,报文再小他也要有包头,那么我们把几个小报文组合成一个大一点的报文再发送,那至少我们能够少发好几个包头,节省了流量和网络带宽;

c)Cork算法:

Nagle更硬气,完全禁止发送小报文,除非累积到一定数量的数据或者是超过某一个时间才 会发送这种报文;

d) Keep-Alive机制:

用于关闭已经断开的tcp连接,这个咱们以往也提及过,那作为TCP/IP协议中的一个概念,这里也再次把他提及一下;

net.ipv4.tcp_keepalive_time:探测包发送周期单位是秒,默认是72002小时):如果2小时内没有任何报文在这个连接上发送,那么开始启动keep-alive机制,发送keepalive包。

net.ipv4.tcp_keepalive_intvl:缺省值75(单位秒)如果发送keepalive包没应答,则过75秒再次发送keepalive包;

net.ipv4.tcp_keepalive_probes:缺省值9,如果发送keepalive包对方一直不应答,发送9次后,如果仍然没有回应,则这个连接就被关闭掉;

e) SO_LINGER选项:

延迟关闭选项, 一般调用setsockopt(SO_LINGER),这个选项设置 在连接关闭的时候,是否进行正常的四次挥手有关;因为缺省的tcp/ip协议可能会导致某一通讯方给对方发送rst包以结束连接从而导致对方收到rst包而丢弃收到的数据,那么这个延迟选项可以避免给对方发送rst包导致对方丢弃收到的数据;

说的再白一点:这个选项用来设置延迟关闭的时间,等待套接字发送缓冲区中的数据发送完成;比较抽象,具体的大家可以百度一下;

延迟关闭并不是一个好事,所以大家要研究明白才能决定是否用它,咱们这个项目中,感觉没必要用;

四:配置最大允许打开的文件句柄数

cat /proc/sys/fs/file-max  :查看操作系统可以使用的最大句柄数

cat /proc/sys/fs/file-nr   :查看当前已经分配的,分配了没使用的,文件句柄最大数目

限制用户使用的最大句柄数

/etc/security/limit.conf文件;

    root soft nofile 60000  :setrlimit(RLIMIT_NOFILE)

    root hard nofile 60000

ulimit -n :查看系统允许的当前用户进程打开的文件数限制

ulimit -HSn 5000   :临时设置,只对当前session有效;

n:表示我们设置的是文件描述符

推荐文章:https://blog.csdn.net/xyang81/article/details/52779229


 

五:内存池补充说明

为什么没有用内存池技术:感觉必要性不大

TCMalloc,取代malloc();

库地址:https://github.com/gperftools/gperftools