自学内容网 自学内容网

(持续更新)linux网络编程中需要注意的内核参数与网络机制

目录

零、基本说明

一、内核参数

二、相关机制

1、GRO

(1)适用场景

(2)优缺点

(3)相关操作

2、Nagle 算法

(1)基本规则

(2)优缺点

(3)相关操作

3、Socket IO 复用 


零、基本说明

        本篇博客会持续更新网络编程中经常遇到的内核参数调整和相关网络功能优化机制,一切以提高服务器性能为目的。具体参数调整,根据业务需求来。

        若读者在日常工作中遇到了相关的问题,也可以提出来一起讨论学习。

一、内核参数

        由于默认的 Linux 内核参数考虑的是最通用的场景,这明显不符合用于支持高并发访问的网络服务器的定义,所以需要修改 Linux 内核参数,使得网络服务器拥有更高的性能。

        在优化内核时,可以做的事情很多,不过,我们通常会根据业务特点来进行调整,下面是只针对最通用的、使 TCP 服务器支持更多并发请求的网络参数做简单说明。

# 设置系统全局可以打开的最大文件描述符数量,ulimit -a 可以查看用户级文件描述符数量的限制
fs.file-max = 6815744  

# 允许重用处于 TIME_WAIT 状态的套接字,用于处理高并发连接(系统中总会存在大量的 TIME_WAIT 状态的连接)。
net.ipv4.tcp_tw_reuse = 1

# 设置 TCP 连接在没有数据传输时保持活动状态的时间(秒),设置小些可以快速清理掉无效连接
net.ipv4.tcp_keepalive_time = 600

# 设置 TCP 连接在关闭时等待 FIN 包(FIN-WAIT-2)确认的时间(秒)。
net.ipv4.tcp_fin_timeout = 30

# 设置 TIME_WAIT 状态的套接字最大数量。如果超过这个数字,TIMEWAIT 套接字将立刻被清除并打印警告信息。该参数默认为 180000,过多的 TIME WAIT套接字会使 Web 服务器变慢。
net.ipv4.tcp_max_tw_buckets = 5000

# 设置本地端口范围,控制服务器在发起连接时所使用的临时源端口的范围
net.ipv4.ip_local_port_range = 1024     61000

# 设置 TCP 接收缓冲区的大小(字节),分别为最小值、默认值和最大值。
net.ipv4.tcp_rmem = 4096        32768   262142
# 设置 TCP 发送缓冲区的大小(字节),分别为最小值、默认值和最大值。
net.ipv4.tcp_wmem = 4096        32768   262142

# 设置网络设备的最大积压队列长度。当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。这个参数表示该队列的最大值。
net.core.netdev_max_backlog = 8096

'''
注意:
  滑动窗口的大小与套接字缓存区会在一定程度上影响并发连接的数目。每个 TCP 连接都会为维护 TCP 滑动窗口而消耗内存,这个窗口会根据服务器的处理速度收缩或扩张。
  以 Nginx 为例,参数 wmem_max 的设置,需要平衡物理内存的总大小和Nginx 并发处理的最大连接数量(由 nginx.conf 中的 worker processes 和 worker connections 参数决定)而确定。
  当然如果仅仅为了提高并发量使服务器不出现 Out Of Memory 问题而去降低滑动窗口大小,那么并不合适,因为滑动窗口过小会影响大数据量的传输速度。rmem_default、wmem_default、rmem_max、wmem_max这4 个参数的设置需要根据我们的业务特性以及实际的硬件成本来综合考虑。
'''
# 设置默认的接收缓冲区大小(字节)。
net.core.rmem_default = 262144
# 设置默认的发送缓冲区大小(字节)。
net.core.wmem_default = 262144
# 设置接收缓冲区的最大值(字节)。
net.core.rmem_max = 2097152
# 设置发送缓冲区的最大值(字节)。
net.core.wmem_max = 2097152

# 该参数与性能无关,启用 TCP SYN Cookies,用于防止 SYN 泛洪攻击。
net.ipv4.tcp_syncookies = 1

# 设置 TCP SYN 请求队列的最大长度,将其设置得大一些,当服务器繁忙来不及 accept 新连接的情况时,Linux 不至于丢失客户端发起的连接请求
net.ipv4.tcp_max_syn_backlog = 1024

二、相关机制

1、GRO

  GRO 是一种全球单播(Unicast)数据包接收优化技术。它允许多个数据包在一次中断中被处理,从而减少 CPU 的利用率和提高数据传输效率。当网络接口接收到多个数据包时,GRO 会将这些数据包聚合在一起,然后一次性触发一个中断来处理这些数据包。这样,就减少了中断的次数,提高了系统的整体性能。

(1)适用场景

  GRO 适用于高流量、低延迟的网络环境,如数据中心、云计算平台等。在这些环境中,GRO 可以显著减少 CPU 的负载,提高网络吞吐量。

(2)优缺点

        优点:可提高网络性能,通过减少中断次数,GRO 可以显著提高网络吞吐量和降低 CPU 的利用率。同时由于减少了中断次数,还降低了系统的能耗。

        缺点:某些情况下,GRO 可能会导致数据包的处理延迟增加,因为它需要等待更多的数据包到达后才能触发中断。在一些对实时性要求较高的应用中,可能需要关闭GRO以确保数据的及时处理。

        在配置 GRO 功能时,需要根据具体的应用场景和需求进行权衡。如果系统对实时性要求较高,或者网络流量较小,那么关闭GRO 可能是一个更好的选择。反之,如果系统需要处理大量的网络流量,并且对延迟的要求不是特别高,那么开启GRO 将有助于提高系统的整体性能。

(3)相关操作

# 关闭网卡 ens33 的 gro 功能
ethtool -K ens33 gro off
# 开启网卡 ens33 的 gro 功能
ethtool -K ens33 gro on

2、Nagle 算法

  Nagle 算法是 TCP/IP 协议中的一种优化机制,主要目的是减少网络中小数据包的数量,从而降低网络拥塞,提高网络带宽的利用率。

(1)基本规则

        在任意时刻,最多只能有一个未被确认的小段。小段为小于 MSS(最大分段大小)的数据块,未被确认是指数据发出去后未收到对端的 ACK。

        如果要发送的数据量达到了 MSS,则立即发送;

        如果数据量小于 MSS 但之前所有发出去的包都已经收到了确认(ACK),也立即发送;

        如果数据量小于 MSS 并且还有未被确认的包,则将数据缓存起来,当收到之前未被确认包的 ACK 或缓存的数据达到 MSS 时再将缓存的数据发送出去。

(2)优缺点

优点

  • 减少了网络中的小包数量,降低了网络负载;
  • 减少了网络拥塞的可能性、提高了网络带宽的利用率;
  • 多个小包合成一个大包发送,减少了协议头的开销;

缺点

  • 增加了发送延迟,特别是对于需要快速响应的应用(如远程终端操作);
  • 很可能导致TCP粘包问题,使接收方应用难以分辨消息边界。

        在Linux系统中,Nagle 算法默认是启用的。对于大多数应用,默认启用Nagle算法是有益的。在实现应用层协议时,需要考虑Nagle算法可能带来的影响,特别是在处理小数据包时。对于需要低延迟的应用(如在线游戏、远程桌面等),可能需要禁用Nagle算法。

(3)相关操作

        在Linux系统中,Nagle 算法默认是启用的。使用 TCP_NODELAY 选项可以禁止 Nagle 算法。此时,应用程序向内核递交的每个数据包都会立即发送出去。

适用 Nagle 时需要注意

  • Nagle 算法与 TCP 的 Delayed ACK 机制确认结合使用时,可能会导致性能问题。Delayed ACK 会推迟发送 ACK,而 Nagle 算法又在等待 ACK,这可能会导致不必要的延迟。
  • Nagle 算法和 CORK 算法非常类似,但是它们的着眼点不一样。Nagle 算法主要避免网络因为太多的小包而拥塞,而 CORK 算法则是为了提高网络的利用率。

        下面 Demo 创建了一个 TCP 客户端,使用 C 语言创建一个 TCP 套接字,连接到服务器,并禁用 Nagle 算法以减少延迟。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char *message = "Hello, Server!";
    int flag = 1;

    // 创建套接字
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8090);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connection failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 禁用 Nagle 算法
    if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) < 0) {
        perror("setsockopt failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 发送数据
    send(sockfd, message, strlen(message), 0);
    printf("Message sent\n");

    // 关闭套接字
    close(sockfd);
    return 0;
}

3、Socket IO 复用 

       Socket IO 复用是网络编程中用于处理多个客户端连接的技术。其目的是在单线程或少量线程下,通过高效的机制来管理和响应多个并发的 I/O 请求,从而避免因频繁创建和销毁线程所带来的资源消耗和性能瓶颈。

        主要的技术有 select、poll 和 epoll,其中需要特别关注 epoll(Nginx 在 linux 下就是基于 epoll 实现高并发网络连接)。epoll 基于事件驱动,通过红黑树高效管理文件描述符,避免了不必要的遍历,使得时间复杂度降低到 O(1)。此外,epoll 还支持边缘触发和水平触发两种模式,提供了更高的灵活性和性能。

        具体参考如下连接:

Linux下的三种 IO 复用_linux的io复用-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/qq_37437983/article/details/144174475?spm=1001.2014.3001.5501


原文地址:https://blog.csdn.net/qq_37437983/article/details/144430407

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!