高并发数据采集场景下Nginx代理Netty服务的优化配置

高并发数据采集场景下,要优化Nginx反向代理来支持多个Netty数采服务并保证稳定的性能,可以从以下几个方面对Nginx进行优化配置。

直连模式(直接通过 Nginx 处理与后端 Netty 服务的连接,而不作为反向代理),很多配置将无法使用。以下是一些无法应用于直连模式的配置和原因:

  1. proxy_* 配置 : 直连模式下,Nginx 直接处理客户端请求并与后端服务建立连接,因此 proxy_connect_timeoutproxy_read_timeoutproxy_send_timeoutproxy_buffersproxy_buffer_size
    proxy_* 相关配置将不再适用。这些配置是针对代理模式设计的,用于调整 Nginx 转发请求到后端时的行为。
  2. proxy_request_buffering offtcp_nodelay
    • proxy_request_buffering off 主要用于避免缓冲请求体,在反向代理模式下用于优化实时性。但在直连模式下,Nginx 会直接处理客户端与后端服务的连接,因此不需要该配置。
    • tcp_nodelay on 仍然有效,它禁用 Nagle 算法,有助于减少小数据包的延迟,但这个配置本身不依赖于代理模式。
  3. accept_mutex
    • accept_mutex on 配置用于避免多个工作进程同时接受连接(即避免"惊群效应"),在直连模式下,如果 Nginx 不作为反向代理而直接与后端建立连接,这个配置也通常不需要,因为连接接入和处理模式不同。
  4. upstream 和健康检查
    • 健康检查配置(如 nginx_upstream_check_module)是针对 Nginx 作为反向代理时用于检查后端服务健康状态的配置。直连模式下如果没有进行负载均衡或使用代理服务器,这些配置将无法使用。
  5. keepalive_timeoutkeepalive_requests
    • 这些配置在直连模式下仍然可能需要,但其意义略有不同。keepalive_timeout 会影响连接在空闲时的保持时间,keepalive_requests
      限制了每个连接的最大请求数。虽然这些配置不完全依赖于代理模式,它们在直连模式中依然重要,特别是在长连接场景下。
  6. worker_processesworker_cpu_affinity
    • 这些配置用于调整 Nginx 的多进程并行处理能力,对于直连模式下的高并发连接优化仍然适用。

适用于直连模式的配置:

  • worker_processesworker_cpu_affinity:对于高并发场景,仍然需要调整工作进程和 CPU 核心绑定。
  • worker_connections:调整每个工作进程能够处理的最大连接数,适用于高并发的直连模式。
  • keepalive_timeoutkeepalive_requests:优化连接的保持时间和请求数。
  • tcp_nodelay on:减少数据包的延迟,适用于实时性要求高的场景。
  • epollmulti_accept:优化事件处理机制,适用于高并发的 Linux 系统。

1. 调整Nginx工作进程与CPU绑定

  • 默认情况下,Nginx的worker_processes数量通常与CPU核心数一致。对于4核CPU,可以设置为4

    nginx 复制代码
    worker_processes 4;
  • 同时可以通过worker_cpu_affinity来将每个worker进程与一个CPU核心绑定,减少上下文切换的开销,提高CPU缓存命中率:

    nginx 复制代码
    worker_cpu_affinity 0001 0010 0100 1000;

​ 这些数字分别对应CPU 0CPU 1CPU 2CPU 3 这4个CPU核心。

2. 调整Nginx连接数与并发处理能力

  • 通过worker_connections设置每个worker的最大连接数。由于每秒有大量数据流入,适当调高这个值很重要。假设每秒有数千连接,可以设置一个较大的值,比如:

    nginx 复制代码
    events {
        worker_connections 4096;
    }

    这意味着每个worker可以处理最多4096个连接,结合4个worker,理论最大连接数可达16384。

    当前场景下,每秒有3000条消息上报,但这并不意味着总的连接数是固定的。每个采集点可能保持一个长连接来持续发送数据(连接数相对较少),也可能每次都建立新的连接(连接数会更高)。worker_connections设置的目的是保证每个Nginx worker可以处理足够多的并发连接。

3. 优化Nginx网络模型(使用epoll)

  • 在Linux系统下,epoll是高效的I/O多路复用模型,适合高并发环境。确保在events块中使用epoll

    nginx 复制代码
    events {
        use epoll;
        multi_accept on;
    }

4. 增加请求缓存与队列能力

  • 由于数据量大,可以通过设置accept_mutexon来避免惊群效应,确保请求在多个worker之间合理分发:

    nginx 复制代码
    accept_mutex on;

​ **惊群效应(THP)**是指当多个进程或线程等待同一个事件发生时,事件触发时所有进程或线程都被唤醒,争夺资源,导致性能下降。在Nginx的上下文 中,如果多个worker进程同时被唤醒来处理一个新的连接请求,而实际上只有一个worker能够成功处理这个请求,其它worker被唤醒但不能工作,就会造成 资源浪费。通过启用accept_mutex,可以确保每次只有一个worker被唤醒来接受新的连接请求,减少不必要的竞争和浪费,从而避免惊群效应。

5. 避免缓存

  • 默认情况下,Nginx 会在接收到客户端的完整请求后,先将请求体缓存在内存或磁盘中,然后再将整个请求一次性发送给后端服务。对于数据采集的场景,一般要求实时性较高,我们需要立即将数据转发给后端服务,而不需要等待整个请求完成,对于半包粘包问题由后端服务处理。

    nginx 复制代码
    proxy_request_buffering off;
  • 启用 TCP_NODELAY 选项,禁用 Nagle 算法。这可以减少数据包传输的延迟,有助于实时性要求较高的数据采集场景。

    nginx 复制代码
    tcp_nodelay on;

6. 优化缓冲区与响应处理

  • 根据1KB的数据大小,可以优化Nginx的缓冲区设置,确保它能高效处理数据。

  • 调整客户端请求体缓冲区大小 (client_body_buffer_size) 和代理缓冲区大小 (proxy_buffers):

    nginx 复制代码
    client_body_buffer_size 16k;
    proxy_buffer_size 16k;
    proxy_buffers 8 32k;
    proxy_busy_buffers_size 64k;

Nginx是流式处理的:Nginx在接收客户端请求时,会分批处理数据,而不是将整个流量积攒到缓冲区里。即使每秒钟传输的数据总量是3MB(3000 X 1KB ≈ 3MB),Nginx会在接收到数据时直接转发给后端,而不是缓存所有数据。Nginx在处理每个连接时,会根据流入的数据流量和后端响应的速度进行动态处理,不会把所有数据一次性存入缓冲区。

缓冲区的作用是处理单次请求的流量: client_body_buffer_sizeproxy_buffers 的大小主要是为了处理单次请求的数据(1KB),确保不会频繁写入磁盘。client_body_buffer_size应该设置为略大于单条数据的大小,一般为16KB或者其倍数,这将允许Nginx在不写入临时文件的情况下,处理单个或多个小型数据块。

下面是这几个参数的详细解释:

  • client_body_buffer_size定义客户端请求体的内存缓冲区大小 。当客户端向Nginx发送数据时,Nginx会把请求体缓存在内存里,直到数据达到一定大小。如果请求体超过这个配置的大小,Nginx就会将超出的部分写入磁盘的临时文件中。在这次的场景中,每条消息大约1KB大小,设置 client_body_buffer_size 为 16KB 的意义在于,可以有效地处理客户端发送的数据而不需要频繁地写入磁盘。如果单条数据小于这个值,所有数据都可以暂存在内存中,加快处理速度并减少磁盘I/O。写入磁盘会影响性能。

如果应用对消息的QoS要求较高的话,下面的配置会比较有用,但是一般情况下,不是特别重要的实时数据是不会用到的。如果没有后端响应要回传客户端的话,建议使用**proxy_buffering off;**关闭对代理数据的缓存处理。

  • proxy_buffer_size这是 Nginx 读取后端服务器响应时的单次读取缓冲区。每次从后端获取响应时,Nginx会先将响应暂时放在这个缓冲区中。例如:proxy_buffer_size 设置为 16k,Nginx每次从后端服务器读取16KB的数据,存放在这个缓冲区。
  • proxy_buffers是一组缓冲区,用来暂时存储从后端读取的数据,等待发送到客户端。proxy_buffers 8 32k 表示 Nginx 分配了 8 个 32KB 大小的缓冲区,来存储从后端获取的数据。也就是说,Nginx 可以在内存中临时存储最多 8 * 32KB = 256KB 的响应数据。proxy_buffer_size 中读取的数据会被进一步存放到 proxy_buffers 。当 proxy_buffers 中的数据准备好后,Nginx开始将这些数据发送给客户端。
  • proxy_busy_buffers_size主要控制当Nginx在向客户端发送数据时,仍然处于"繁忙状态"的缓冲区大小。
    • 繁忙状态 是指缓冲区中的数据已经开始发送到客户端,但还没有完全发送出去。
    • Nginx会在后台继续读取后端服务器的数据,并将这些数据存放到缓冲区中,而同时Nginx会在前台将已经读取到的数据发送给客户端。这个过程中,proxy_busy_buffers_size 用来限制这个前台数据传输过程中,仍在被使用的缓冲区(繁忙缓冲区)的最大大小。
    • 如果 proxy_busy_buffers_size 设置为 64k,则表示在发送给客户端的过程中,Nginx最多允许 64KB 的缓冲区数据仍然处于忙碌状态(即正在被发送或等待发送)。
    • 当繁忙缓冲区超过这个值时,Nginx会停止从后端读取更多数据,直到客户端接收完这些数据,从而避免前后端的流量不平衡。

7. 增加客户端连接保持与超时时间

  • 在数据采集场景中,客户端可能会持续发送数据。因此可以增加客户端连接的保持时间,减少重复建立连接的开销:

    nginx 复制代码
    keepalive_timeout 60;
    keepalive_requests 0;
  • keepalive_timeout这是Nginx 在关闭这个连接之前等待的时间。当客户端发送完数据后,连接会被保持一段时间,而不是立即关闭。如果在 keepalive_timeout 的时间内有新的数据发送,Nginx 会继续使用这个连接,从而避免了重新建立连接的开销。

  • keepalive_requests是为了防止单个连接持续占用资源过久,限制在同一个保持连接(Keep-Alive连接)中,客户端最多可以发送的请求数量,而对于现在的数据采集场景,不需要限制每个长连接的请求数。

8. 优化反向代理配置

  • Nginx作为反向代理,负责将请求转发给后端Netty服务,配置时需要确保Nginx可以高效转发大流量的数据。

  • 设置合适的proxy_connect_timeoutproxy_read_timeout来保证大数据传输:

    nginx 复制代码
    proxy_connect_timeout 5s;
    proxy_read_timeout 120s;
    proxy_send_timeout 120s;

​ 这三个参数主要用于控制Nginx与后端服务器(Netty服务)之间的连接和数据传输。proxy_connect_timeout定义了Nginx与后端服务器建立连接的超时时间;proxy_read_timeout定义了Nginx从后端服务器读取响应的超时时间;proxy_send_timeout设置了Nginx发送请求到后端服务器的超时时间。

多网卡支持海量长连接

​ 在反向代理过程中,NGINX 作为客户端与后端采集服务节点建立连接。在这种情况下,单个 IP 地址最多能够创建约 6 万个长连接。为了支持更多的连接,可以选择部署多个 NGINX 服务器或者配置多个 IP 地址。

​ 配置多个 IP,需要使用 NGINX 内置的 split_clients 模块来定义一个变量 $multi_ip,根据客户端的 IP 地址和端口号进行请求分流,需要确保所使用的 IP 地址在本地可用。

nginx 复制代码
stream {
 split_clients "$remote_addr$remote_port" $multi_ip {        
    20% 192.168.0.105;        
    20% 192.168.0.106;        
    20% 192.168.0.107;        
    20% 192.168.0.108;        
    * 192.168.0.109;    
  }

  upstream netty_servers {
    server 192.168.0.211:8083;
    server 192.168.0.212:8083;
  }

  server {
    listen 8083;

    proxy_pass netty_servers;
    proxy_bind $multi_ip;
  }
}

9. 内核参数优化

  • 可以调整Linux内核参数来配合Nginx,尤其是针对高并发场景的网络连接与文件描述符限制:

    bash 复制代码
    sysctl -w net.core.somaxconn=65535
    sysctl -w net.core.netdev_max_backlog=65535
    sysctl -w net.ipv4.tcp_max_syn_backlog=65535
    ulimit -n 65535

10. SSL 缓存和复用

  • 因为需要交换密钥等操作,SSL 握手过程很耗时,如果使用SSL,可以考虑开启SSL缓存和复用来优化SSL连接处理。通过启用 SSL Session 缓存,Nginx 可以将每个 SSL 会话的状态保存起来,在后续的连接中直接使用之前的会话状态,避免重复的 SSL 握手过程。

    nginx 复制代码
    ssl_session_cache shared:SSL:10m;  # 共享内存中缓存SSL会话,大小为10MB
    ssl_session_timeout 10m;           # SSL会话缓存的超时时间为10分钟
    ssl_protocols TLSv1.2 TLSv1.3;     # 使用现代的安全协议
    ssl_prefer_server_ciphers on;	   # 优先使用服务器指定的加密套件
  • 当客户端与服务器第一次建立连接后,服务器会生成一个 Session ID 并保存在缓存中。后续相同客户端与服务器的连接可以重用这个会话 ID,跳过完整的握手过程,只需要进行快速的重新协商。

  • 通过 ssl_session_timeout 保证了在复用的时间窗口内(10分钟内),可以重用会话。过了这个时间后,必须重新进行完整握手,这样可以确保长期会话不会被滥用。

11. 添加健康检查机制

  • Nginx 可以通过配置来定期检测后端服务的健康状况(主动健康检查)。健康检查机制有助于及时发现并隔离故障的服务实例,防止请求被路由到不可用的服务上。nginx_upstream_check_module 模块不是 Nginx 的默认模块,需要单独编译或安装。

    nginx 复制代码
    upstream backend {
        server netty1.example.com:8080;
        server netty2.example.com:8080;
        server netty3.example.com:8080;
    
        # 健康检查配置
        check interval=3000 rise=2 fall=3 timeout=1000 type=http;
    
        # 指定健康检查的路径
        check_http_send "HEAD /health HTTP/1.0\r\nHost: $host\r\n\r\n";
        check_http_expect http_200;
    }

    参数详解:

    • interval=3000:表示 Nginx 每隔 3000 毫秒(即 3 秒)检查一次后端服务器的健康状态。
    • rise=2:表示后端服务器被认为健康之前,必须连续成功响应两次健康检查请求。
    • fall=3:表示后端服务器被认为不健康之前,连续失败三次健康检查请求。
    • timeout=1000:表示健康检查请求的超时时间为 1000 毫秒(即 1 秒)。如果在这段时间内没有得到响应,健康检查就会认为失败。
    • type=http:表示健康检查采用 HTTP 协议,默认情况下 Nginx 会发送一个 HEAD 请求到后端服务器的默认监听地址。
    • check_http_send 指令用于定义 Nginx 向后端服务器发送的健康检查请求的具体内容。
      • HEAD:表示这是一个 HTTP HEAD 请求,HEAD 请求与 GET 请求类似,但不会返回请求体的内容,只返回响应头。
      • /health:这是请求的路径,通常用于健康检查。Netty 服务应该监听这个路径,并在收到请求时返回相应的健康状态。
      • HTTP/1.0:这是请求使用的 HTTP 版本。虽然现在常用的是 HTTP/1.1,但由于 HEAD 请求通常不需要响应体,使用 HTTP/1.0 也是常见的做法。
      • Host: $host:这是请求中的 Host 头部字段,用于指示请求的目标主机。$host 是一个变量,它会被替换为实际请求的目标主机名。
      • \r\n:这是换行符,用于分隔不同的 HTTP 请求头字段,最后一个是请求的结束标志。
    • check_http_expect指的是望收到 200 OK 状态码。

    SpringBoot工程定义健康检查的接口

    java 复制代码
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HealthController {
    
        /**
         * 处理 HEAD 请求。
         *
         * @return ResponseEntity 表示响应的状态码
         */
        @RequestMapping(value = "/health", method = RequestMethod.HEAD)
        public ResponseEntity<Void> health() {
            return ResponseEntity.ok().build();
        }
    }

    虽然使用Nginx的请求超时和重试机制可以减少不健康服务的影响,但最佳实践仍然是显式配置健康检查,以确保请求只被转发到健康的后端服务。

  • 被动健康检查是 Nginx 的内置功能,无需额外安装模块或配置复杂的健康检查逻辑。对于一般的反向代理需求,这种机制已经足够应对常见的故障情况。

    nginx 复制代码
    upstream backend {
        server 192.168.0.211:8083 weight=3 max_fails=3 fail_timeout=30s slow_start=30s;
        server 192.168.0.212:8083 weight=1 max_fails=3 fail_timeout=30s slow_start=30s;
        server 192.168.0.213:8083 backup;
        server 192.168.0.214:8083 down;
    }
    • max_fails=3 表示如果一个服务器在 fail_timeout 时间内连续失败 3 次,则认为该服务器不可用。
    • fail_timeout=30s 表示在这个时间段内,如果某个服务器连续失败达到 max_fails 次数(即 3 次),则该服务器会被标记为不可用。

    自动切换机制如下:

    1. 当 Nginx 尝试向 192.168.0.211:8083 发送请求但未能成功时(例如连接超时、读写错误等),它不会立即停止尝试,而是继续在同一时间窗口内重试。
    2. 如果 192.168.0.211:8083 在接下来的 30 秒内连续失败 3 次,Nginx 会将其标记为不可用,并暂时不再向其发送新的请求。
    3. 一旦某个服务器被标记为不可用,Nginx 会根据配置的负载均衡算法(默认为轮询)选择其他可用的服务器来处理请求。在这个例子中,它会尝试将请求发送到 192.168.0.212:8083
    4. 被标记为不可用的服务器会在 fail_timeout 时间(30 秒)结束后重新参与请求分配,周而复始,直到服务恢复正常。
    5. 对于刚刚恢复的服务器,Nginx会在接下来的 slow_start=30s 内逐渐增加其接收的请求数量,而不是立即恢复正常负载。

12. 错误处理与重试策略

当出现网络异常或后端服务故障时,可以配置 Nginx 来自动重试请求,或者根据返回的状态码执行特定的操作,如重定向到另一个 URL 或返回特定的错误页面。

nginx 复制代码
location / {
    proxy_pass http://backend;
    
    # 自动重试策略
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    proxy_next_upstream_tries 2;
    proxy_next_upstream_timeout 10s;
    
    # 错误处理
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

proxy_next_upstream 指令定义了当遇到哪些类型的错误时 Nginx 将尝试切换到下一个可用的后端服务器。error指与后端服务器的连接失败;timeout指与后端服务器连接超时;invalid_header指当从后端服务器接收到无效的响应头;其他就是后端服务响应的状态码。

proxy_next_upstream_tries 则定义了在切换到下一个后端之前尝试的次数。error_page 指令用于定义当出现特定错误码时,返回的页面。

​ 这些配置不仅保护了站点免受常见攻击,还提高了系统的整体安全性。在实际部署时,可以根据具体需求调整配置项,确保既提高了安全性又不影响正常的服务运作。

​ 需要说明的是,即便没有额外配置,Nginx 会对特定的错误(如 errortimeouthttp_500 等)尝试自动重试。但是,默认重试的次数没有明确的数值,而是由后端服务器的数量决定,因为重试行为与 upstream 中定义的后端服务器列表紧密相关。Nginx 默认会尝试将请求转发到 upstream 配置中的其他服务器,直到尝试过所有健康的服务器或没有可用服务器为止。如果只有一个后端服务器,即使失败了也不会重试。如果有多个后端服务器,Nginx 会按配置的负载均衡算法逐个尝试。

​ 默认情况下,重试不限制具体次数,而是受每个后端服务器响应超时时间(proxy_connect_timeoutproxy_read_timeout 参考优化反向代理配置中关于此参数的说明 )以及 proxy_next_upstream_timeout 的影响。如果没有设置 proxy_next_upstream_timeout,Nginx 会继续重试直到用尽后端服务器或客户端连接超时。

相关推荐
ping某1 天前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz3 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工3 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智3 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_3 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
古城小栈3 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
施努卡机器视觉3 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造