找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3065

积分

0

好友

425

主题
发表于 12 小时前 | 查看: 0| 回复: 0

根据 OSI 七层模型划分,其中这里四层指OSI模型中的传输层,主要包含TCP和UDP协议。之前介绍的七层协议(如HTTP、HTTP/2、WebSocket、gRPC)均运行在TCP之上,因此四层协议相对简单,学习时可对照七层反向代理的指令与流程进行理解。

Nginx Stream 四层反向代理

Nginx 通过 Stream 相关模块实现四层反向代理,它是一种不同于 http 模块的新模块。本小节将详细讲解stream模块的七个处理阶段,和 Stream 模块结构及对应阶段模块指令。

前面我们介绍过 http 模块有11个处理阶段,而 stream 模块仅有7个处理阶段,此七个阶段对应模块如下表:

阶段 模块
POST_ACCEPT realip
PREACCESS limit_conn
ACCESS access
SSL ssl
PREREAD ssl_preread
CONTENT return, stream_proxy
LOG access_log

对应于上表,我们逐一讲解每个阶段对应模块的指令与功能。

POST_ACCEPT 阶段:连接建立后触发,常被称为accept后的处理阶段。

  • realip 模块在此阶段通过proxy_protocol获取客户端真实IP地址,与http模块中POST_READ阶段的 realip 模块功能相似,但因TCP无HTTP头部,需依赖proxy_protocol传递真实IP。

PREACCESS 阶段:连接建立后访问触发,用于在访问控制之前进行前置预处理,例如限制连接数。

  • 含义与http模块中的preaccess一致。在http模块中区分limit_conn(基于连接)和limit_req(基于请求)。所以对于stream模块,仅存在limit_conn,无limit_req概念,因其面向TCP连接而非HTTP请求。

ACCESS 阶段: 连接建立后访问触发,用于控制访问权限,例如基于IP地址的访问控制。

  • 功能与http模块中的access阶段完全一致,支持allowdeny指令,可根据客户端IP地址允许或拒绝访问。

SSL 阶段:在SSL握手完成后触发,用于配置SSL证书和密钥。

  • stream模块新增阶段,http模块中不存在。用于处理SSL/TLS握手请求,由于 SSL位于TCP之上,因此可在stream上下文中使用 SSL。

PREREAD 阶段: 名称不够贴切,亦可理解为 ssl_preread ,用于在SSL握手前读取数据。

  • 在 SSL 握手过程中,客户端发送ClientHello消息,ssl_preread模块可从中提取信息并设为变量供后续使用。

CONTENT 阶段: 处理实际的数据传输,例如反向代理。

  • 包含stream_proxy模块,实现反向代理功能。
  • 包含return模块,可直接返回字符串或变量值,类似于http模块中的return指令。

LOG 阶段:记录日志,类似于http模块的log阶段。

  • http模块的log阶段相似,用于记录access_log,因TCP协议信息量少,日志记录内容也较少。

至此,我们简单介绍了stream模块的七个处理阶段,后续将会分别演示实践。

Nginx Stream 四层核心模块指令介绍

在 Nginx 中 ngx_stream_core_module 模块 (自1.9.0版本起可用) 默认是不构建的,若要启用需在构建时添加 --with-stream 参数。例如,作者使用 nginx/1.29.0 版本为例进行编译安装。

# 构建
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --error-log-path=/var/log/nginx/logs/error.log --http-log-path=/var/log/nginx/logs/access.log --lock-path=/var/run/nginx.lock --modules-path=/usr/local/nginx/modules --with-http_stub_status_module --with-http_realip_module --with-http_v2_module --with-http_ssl_module --with-http_slice_module --with-stream --with-cc-opt=-O2 --with-compat

# 编译安装
make -j $(nproc) && make install

为Nginx添加stream支持图

同样,Nginx stream 模块的核心指令可类比于http块,位于全局配置层级。stream 块下可定义 server 块。server 块中不再有 location 概念,因 location 是针对URL路径的,不适用于TCP。简单示例如下所示:

worker_processes auto;
error_log /var/log/nginx/error.log info;
events {
  worker_connections  1024;
}

# 指定全局 stream 模块配置
stream {
  # 定义上游服务器组,类似于 http 模块中的 upstream 块。
  upstream backend {
    hash $remote_addr consistent;
    server backend1.example.com:12345 weight=5;
    server 127.0.0.1:12345            max_fails=3 fail_timeout=30s;
    server unix:/tmp/backend3;
  }

  upstream dns {
    server 192.168.0.1:53535;
    server dns.example.com:53;
  }

  # 定义 server 块,类似于 http 模块中的 server 块。
  server {
    listen 12345;
    proxy_connect_timeout 1s;
    proxy_timeout 3s;
    proxy_pass backend;
  }

  server {
    listen 127.0.0.1:53 udp reuseport;
    proxy_timeout 20s;
    proxy_pass dns;
  }

  server {
    listen [::1]:12345;
    proxy_pass unix:/tmp/stream.socket;
  }
}

参考文档:https://nginx.org/en/docs/stream/ngx_stream_core_module.html

指令参数

  • stream : 提供配置文件上下文,在其中指定流服务器指令,对比等价 http 块。
    Syntax: stream { ... }
    Default: —
    Context: main
  • server:定义流服务器块,类似于HTTP模块中的server块,设置虚拟服务器配置,基于IP(基于IP地址)和基于名称(基于TLS服务器名称指示扩展(SNI, RFC 6066))。
    Syntax: server { ... }
    Default: —
    Context: stream
  • server_name:设置基于名称的虚拟服务器的服务器名,使用与 http 模块中的server_name指令相同。

    Syntax: server_name name ...;
    Default: server_name "";
    Context: server
    
    # 示例
    server_name example.com www.example.com;  # 第一个名称成为主服务器名称。
    server_name example.com *.example.com www.example.*;   # 通配符名称
    server_name .example.com;
    server_name www.example.com ~^www\d+\.example\.com$;   # 正则表达式名称
    server_name ~^(www\.)?(.+)$;                          # 利用正则元组捕获变量名称
    
    # 优先级说明:
    1. 精确匹配的名称。
    2. 通配符匹配的名称,最长匹配优先(例如,"*.example.com" 比 "*.sub.example.com" 更具体)。
    3. 正则表达式匹配的名称,最先定义的优先级最高。
    4. 所有其他情况,以第一个被匹配到的名称为主。
  • listen:监听端口或 UNIX sockets,支持TCP和UDP协议,支持 proxy_protocol 选项,用于接收携带真实客户端IP的协议数据。

    Syntax: listen address:port [default_server] [ssl] [udp] [proxy_protocol] [setfib=number] [fastopen=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
    Default: —
    Context: server
    
    # 参数说明,下述参数主要应用于 Unix
    setfib=number : 设置路由域的标识符,仅在FreeBSD上有效。
    fastopen=number : 为监听套接字启用TCP Fast Open,并限制尚未完成三次握手的连接队列的最大长度。
    backlog=number :该参数限制挂起连接队列的最大长度,默认情况下在 FreeBSD,DragonFly BSD和macOS上,backlog设置为-1,在其他平台上设置为511。
    rcvbuf=size : 用于监听套接字设置接收缓冲区大小(SO_RCVBUF选项)。
    sndbuf=size :用于监听套接字设置发送缓冲区大小(SO_SNDBUF选项)。
    accept_filter=filter :用于监听套接字设置接受过滤器的名称(SO_ACCEPTFILTER选项),适用于FreeBSD和NetBSD 5.0+。
    deferred :指示使用延迟的accept() (TCP_DEFER_ACCEPT 选项) 在Linux(1.25.5)引入。
    ipv6only=on|off :仅在IPv6上接受连接,或在IPv4和IPv6上都接受。
    reuseport :为每个工作进程创建一个单独的监听套接字,在Linux 3.9+、FreeBSD 12.0+和DragonFly BSD 5.0+上支持。
    so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt] :启用听套接字配置“TCP保持连接”行为或禁用TCP keepalive消息,并设置keepalive参数,例如:so_keepalive=30m::10 (keepidle=30m, keepintvl=1s, keepcnt=10)
    
    # 示例演示
    listen 127.0.0.1:12345;
    listen *:12345;
    listen 12345;                 # 同上
    listen localhost:12345;
    listen unix:/var/run/nginx.sock;
    listen 127.0.0.1:12345-12399;  # 监听多个端口范围
    listen 12345-12399;
  • server_names_hash_bucket_size:设置服务器名称哈希表存储桶的大小,默认为32/64/128字节。
    Syntax: server_names_hash_bucket_size size;
    Default: server_names_hash_bucket_size 32|64|128;
    Context: stream
  • server_names_hash_max_size:设置服务器名称哈希表的最大大小,默认为512字节。
    Syntax: server_names_hash_max_size size;
    Default: server_names_hash_max_size 512;
    Context: stream
  • tcp_nodelay : 启用或禁用TCP的使用 TCP_NODELAY 选项。该选项对客户机和代理服务器连接都是启用的。
    Syntax: tcp_nodelay on | off;
    Default: tcp_nodelay on;
    Context: stream, server
  • resolver : 配置名称服务器,用于将上游服务器的名称解析为地址。

    Syntax: resolver address ... [valid=time] [ipv4=on|off] [ipv6=on|off] [status_zone=zone];
    Default: —
    Context: stream, server
    
    # 例如:指定DNS服务器地址,并设置有效时间为30秒,以覆盖接受到的 TTL 值 。
    resolver 127.0.0.1 [::1]:5353 valid=30s;
    # 温馨提示:status_zone 参数(1.17.1)表示统计指定区域内DNS服务器的请求和响应信息,是在商业订阅版本中使用。
  • resolver_timeout : 设置名称解析超时时间,默认为30秒。
    Syntax: resolver_timeout time;
    Default: resolver_timeout 30s;
    Context: stream, server
  • variables_hash_bucket_size :设置变量哈希表的桶大小。
    Syntax: variables_hash_bucket_size size;
    Default: variables_hash_bucket_size 64;
    Context: stream
  • variables_hash_max_size : 设置变量哈希表的最大大小。
    Syntax: variables_hash_max_size size;
    Default: variables_hash_max_size 1024;
    Context: stream

另外,ngx_stream_core_module 核心模块还内置一些变量,用于在其它协作模块中使用。

(1) 获取客户端与服务端信息变量

  • $binary_remote_addr : 客户端的二进制地址。
  • $remote_addr : 客户端地址。
  • $remote_port : 客户端端口。
  • $server_addr : 接受连接的服务器地址。计算这个变量的值通常需要一次系统调用。为了避免系统调用,listen 指令必须指定地址并使用bind参数。
  • $server_port : 接受连接的服务器的端口。
  • $proxy_protocol_addr : 接收到的代理协议头中的客户端地址。
  • $proxy_protocol_port :接收到的代理协议头中的客户端端口。
  • $proxy_protocol_server_addr : 代理协议头中的服务器地址。
  • $proxy_protocol_server_port : 代理协议报头中的服务器端口。
  • $proxy_protocol_tlv_name : 获取TLV类型为“NAME”的代理协议头中的值。SSL TLV也可以通过TLV类型名称或其数值访问,两者都以SSL作为前缀_:

    $proxy_protocol_tlv_ssl_version
    $proxy_protocol_tlv_ssl_0x21
    
    # 常规TLV类型名称和数值如下所示:
    alpn (0x01) - upper layer protocol used over the connection
    authority (0x02) - host name value passed by the client
    unique_id (0x05) - unique connection id
    netns (0x30) - name of the namespace
    ssl (0x20) - binary SSL TLV structure
    
    # SSL 类型名称和数值如下所示:
    ssl_version (0x21) - SSL version used in client connection
    ssl_cn (0x22) - SSL certificate Common Name
    ssl_cipher (0x23) - name of the used cipher
    ssl_sig_alg (0x24) - algorithm used to sign the certificate
    ssl_key_alg (0x25) - public-key algorithm
    ssl_verify - certificate verification result,如果客户端提供证书并成功验证,则为零,否则为非零

(2) 获取通信传输信息及响应状态变量

  • $protocol: 获取与客户端通信的协议 TCP或UDP (1.11.4)。
  • $bytes_received:从客户端接收到的字节数。
  • $bytes_sent: 发送到客户端的字节数。
  • $connection: 连接序号。
  • $session_time: 会话持续时间(秒),分辨率为毫秒(1.11.4)。
  • $status : 会话状态(1.11.4)。状态码含义与http模块相似,其常见取值如下:
    • [x] 200: 会话正常结束。
    • [x] 400: 客户端数据无法解析,例如 proxy_protocol 格式错误。
    • [x] 403: 禁止访问,例如 access 模块限制IP或 limit_conn 触发。
    • [x] 500: 内部服务器错误。
    • [x] 502: 无法连接上游服务,例如 网关错误。
    • [x] 503: 服务不可用,例如当访问受到连接数量的限制时。

(3) 获取其它信息的变量

  • $hostname : 所在服务器的主机名,与 hostname 命令输出一致主机名。
  • $nginx_version : nginx 版本
  • $pid : 所属 worker 工作进程的 PID()
  • $msec : 时间戳(自1970年1月1日以来的秒数,小数部分为毫秒)
  • $time_iso8601: 使用 ISO 8601 标准格式的本地时间,例如:2018-11-14T15:55:37+08:00
  • $time_local: 通用日志格式的本地时间,例如:14/Nov/2018:15:55:37 +0800

牛刀小试

步骤 01.在源码编译后的 Nginx 配置文件中,添加一个 stream 块并在其中,创建一个四层 stream 服务以供测试:

tee /usr/local/nginx/conf/nginx.conf <<'EOF'
worker_processes auto;
error_log /var/log/nginx/error.log debug;
events {
  worker_connections  1024;
}

# stream 模块配置块
stream {
  # 定义一个四层TCP服务,监听 8090 端口
  server {
    listen 10.20.172.214:8090;
    return 'Hello World, Nginx Stream!
hostname: $hostname
pid: $pid
nginx_version: $nginx_version
protocol: $protocol
remote_addr: $remote_addr
remote_port: $remote_port
server_addr: $server_addr
server_port: $server_port
bytes_received: $bytes_received
bytes_sent: $bytes_sent
session_time: $session_time
status: $status
msec: $msec
time_iso8601: $time_iso8601\n';
  }
}
EOF

步骤 02.重新加载 Nginx 服务,在客户端使用打开 cmd 或 shell,使用 telnet 命令测试搭建的四层TCP服务。TCP 连接建立即触发处理流程,无需发送请求,且在 return 指令执行后立即关闭连接,不再进行后续代理操作。

$ nginx -t & nginx -s reload
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

# 在另外一台客户端测试四层TCP服务连接:
$ telnet 10.20.172.214 8090
Trying 10.20.172.214...
Connected to 10.20.172.214.
Escape character is '^]'.
Hello World, Nginx Stream!
hostname: weiyigeek_top
pid: 2187344
nginx_version: 1.29.0
protocol: TCP
remote_addr: 10.20.176.212
remote_port: 23648
server_addr: 10.20.172.214
server_port: 8090
bytes_received: 0
bytes_sent: 0
session_time: 0.000
status: 000
msec: 1768893874.932
time_iso8601: 2026-01-20T15:24:34+08:00
Connection closed by foreign host.

测试搭建的TCP四层服务图

至此,本小节介绍了 ngx_stream_core_module 模块的指令和变量,并演示一个简单的四层反向代理,利用 CONTENT 阶段的 return 指令返回客户端连接请求与服务端响应的一些测试信息。

火箭

END

希望这篇关于Nginx四层反向代理的实践解析,能帮助大家理解TCP/UDP层面的负载均衡配置。在大流量场景下,四层代理因其处理效率高,常作为高性能架构的核心组件。在实际部署中,结合恰当的监控手段对连接数和会话状态进行跟踪,是保障服务稳定性的关键。深入学习TCP/IP等底层网络协议,将有助于更好地进行内核调优与故障排查。欢迎在云栈社区继续交流相关技术细节。




上一篇:智能体长时间运行难题:解决跨会话遗忘问题的双层智能体架构方案
下一篇:市值归一化订单流因子:提升信噪比与知情交易识别
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-4 23:14 , Processed in 0.367362 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表