《从应用到内核:三种流量转发方案深度对比》

一、前序介绍

1、为什么需要流量转发?

● 业务场景引入:微服务、数据库代理、边缘网关、高可用架构

● 核心问题:如何将客户端请求高效、可靠地转发到后端?

● 本文目标:通过 原理 + 实操,带你掌握三种主流方案的选型与落地

2、OSI的 3/4/7 层区别在哪里?

● L7(应用层):看得懂HTTP头、URL、Cookie,可做路由、鉴权、WAF、压缩、缓存、重写、A/B复制等。

● L4(传输层):只关心TCP/UDP连接与端口,做不到基于URI/Cookie的策略,但可做SNI预读与直通。

● L3(网络层):基于IP的转发/NAT,不涉及内核态/用户态数据拷贝,性能最好,功能最"朴素"。

3、场景应用场景

● Web网关:Nginx7层 (TLS终止/路由/限流) --> 后端服务。

● 数据库/缓存:Nginx stream做4层负载(或直连) --> 后端实例: 需要源端IP 就上PROXY协议。

● 内网入口:iptables 做DNAT把少量端口打到特定机器,追齐极致性能,零改造。

二、环境准备

1、资源列表

● (请求发起机器)EC2:172.31.50.171

● (作为流量转发机器) EC2:172.31.50.172

● (接收流量转发)Aliyun-ALB: 对外监听80端口,http://alb-v6pg0tz2zs69n0k0m2.cn-hangzhou.alb.aliyuncsslb.com

● (接收流量转发)Aliyun-NLB: 对外监听80端口,挂载两个VIP,172.31.50.174、172.18.139.235

● (后端服务)ECS:172.31.50.173

2、Nginx安装

登录172.31.50.172机器,参考Nginx官方安装方案---链接

启动Nginx服务

注意:在CentOS中如果默认源安装的nginx版本低,不包含stream模块,会导致方案二中的配置文件无法识别。

3、后端服务部署

登录172.31.50.173机器,使用Python3启动以下脚本

server.py 复制代码
from http.server import BaseHTTPRequestHandler, HTTPServer

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write(b"Hello from Python!")

if __name__ == "__main__":
    server = HTTPServer(('0.0.0.0', 8089), MyHandler)
    print("Server started on port 8089")
    server.serve_forever()

python3 server.py

三、方案实施

1、方案一:Nginx 7层协议转发

修改Nginx.conf的文件内容如下,然后重启Nginx

复制代码
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            #root   html;
            #index  index.html index.htm;
            #proxy_pass http://localhost:8080;
            #proxy_pass http://game-server:8080;
            proxy_pass http://alb-v6pg0tz2zs69n0k0m2.cn-hangzhou.alb.aliyuncsslb.com;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    include servers/*;
}

#在部署Nginx的机器上,操作重启

nginx -s reload

#在请求发起服务器上,触发请求

curl 172.31.50.172:8080

2、方案二:Nginx 4层流量转发

修改Nginx.conf的文件内容如下,然后重启Nginx

复制代码
# -------------------------------
# 全局配置
# -------------------------------
# 指定 Nginx 运行的用户和用户组(可选,生产环境建议指定)
# user  nginx;

# 启动进程数,建议设置为 CPU 核心数
worker_processes  auto;

# 错误日志位置,级别可选:debug, info, notice, warn, error, crit
error_log  /usr/local/nginx/logs/error.log warn;

# 指定 PID 文件位置
pid        /usr/local/nginx/logs/nginx.pid;


# -------------------------------
# Events 配置
# -------------------------------
events {
    # 使用 epoll 模型,高效处理大量并发连接(Linux 推荐)
    use epoll;

    # 每个 worker 进程允许的最大连接数
    worker_connections  1024;

    # 启用多路复用 accept,提高性能
    multi_accept on;
}

# ================================
# Stream 模块配置(四层代理)
# ================================
stream {

    # -------------------------------
    # 自定义日志格式
    # -------------------------------
    log_format proxy '$remote_addr [$time_local]' '$protocol $status $bytes_sent $bytes_received' '$session_time "$upstream_addr"' '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';

    access_log  /usr/local/nginx/logs/tcp-access.log proxy;

    # 关闭日志文件缓存,便于 logrotate
    open_log_file_cache off;

    # -------------------------------
    # Upstream:定义 NLB 后端
    # -------------------------------
    upstream nlb_backend {
        # NLB 双VIP地址
        server 172.31.50.174:80 max_fails=3 fail_timeout=15s;
        server 172.18.139.235:80 max_fails=3 fail_timeout=15s;
    }

    # -------------------------------
    # Server:监听 MySQL 客户端请求
    # -------------------------------
    server {
        # 监听 8080 端口
        listen 8080;

        # 转发到 nlb 后端
        proxy_pass nlb_backend;

        # 超时设置
        proxy_timeout 30s;

        # 响应数
        proxy_responses 1;

        # 访问日志
        access_log /var/log/nginx/tcp-nlb.log proxy;

        # 错误日志
        error_log /var/log/nginx/tcp-nlb-error.log error;
    }
}

#在部署Nginx的机器上,操作重启

nginx -s reload

#在请求发起服务器上,触发请求

curl 172.31.50.172:8080

3、方案三:iptables 网络转发

1)首先在EC2上配置流量转发的ip_tables规则

在172.31.50.172上添加转发规则

#开启Linux服务器的IP转发能力

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

sysctl -p

#配置入口流量预处理转发规则

iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.31.50.174:80

#配置回路流量处理规则

iptables -t nat -A POSTROUTING -p tcp -d 172.31.50.174 --dport 80 -j MASQUERADE

2)查看配置规则

sudo iptables -t nat -L -n -v

3)配置流量转发的日志--可选

#配置入口流量转发日志

iptables -t mangle -A PREROUTING -p tcp --dport 8080 -j LOG --log-prefix "nf_debug_in"

#配置回路流量转发日志

iptables -t mangle -A POSTROUTING -p tcp -d 172.31.50.174 --dport 80 -j LOG --log-prefix "nf_debug_out"

4)流量测试

PS:直接在EC2上发起curl 127.0.0.1无法转发,需要在另外一台机器发起

比如 EC2 172.31.50.173

5)查看日志

sudo tail -f /var/log/messages | grep "nf_debug

6)删除ip_tables规则

iptables -t nat -D PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.31.50.174:80

iptables -t nat -D POSTROUTING -p tcp -d 172.31.50.174 --dport 80 -j MASQUERADE

四、场景选项与对比

对比维度 Nginx 七层代理 Nginx 四层代理 iptables
工作层级 L7(应用层) L4(传输层) L3/L4(网络/传输层)
数据处理方式 解析 HTTP 协议,可读取 URL、Header、Cookie 等 不解析应用层,只转发 TCP/UDP 流 不解析应用层,仅修改 IP/Port
性能 ⭐⭐⭐高,但有协议解析开销 ⭐⭐⭐⭐ 较高,接近线速 ⭐⭐⭐⭐⭐ 极高,纯内核态
CPU/内存开销 高(需解析、缓冲、TLS 解密) 中(仅转发,支持长连接) 极低(内核处理,无用户态拷贝)
功能丰富性 ✅✅✅ 极强 • 基于 URL 路由 • HTTPS 终止 • 缓存、压缩、限速 • WAF、灰度发布 ✅✅ 强 • 负载均衡 • 健康检查 • proxy_protocol • 日志记录 ✅ 有限 • 端口转发(DNAT) • 防火墙规则 • 简单负载(配合 ipvs)
是否经过用户态 ✅ 是(数据进入 Nginx 用户进程) ✅ 是(数据进入 Nginx 用户进程) ❌ 否(纯内核态,无数据拷贝)
配置复杂度 中高(需理解 HTTP 语义) 中(需理解 TCP 语义) 高(命令行复杂,易出错)
可维护性 高(配置文件清晰,支持 reload) 高(模块化配置) 低(规则易混乱,难调试)
适用协议 HTTP/HTTPS TCP/UDP TCP/UDP/ICMP 等所有 IP 协议
典型应用场景 Web 服务、微服务网关、静态资源代理 MySQL、Redis、Kafka、自定义 TCP 服务 防火墙、NAT、边缘路由、容器网络
扩展性 高(支持 Lua、动态模块) 中(支持 stream 模块) 低(依赖内核)
安全性 高(可集成 WAF、JWT 认证) 中(可做访问控制) 高(底层防火墙)

五、从浅入深的落地建议

  1. 先7层,再4层,最后3层
    a. 需求从"功能为王"到"性能为王"的递进,Web/API先上7层,中台类中间件连通选4层,极简内核映射时再考虑iptables;
  2. 统一真实客户端信息
    a. 7层:X-Forwarded-For / X-Forwarded-Proto
    b. 4层:若后端支持,启用PROXY protocol;否则只能看到代理源。
  3. 健康检查与容灾
    a. 7/4层可内建检查; iptables需要依赖外部监控+自动切换脚本或路由协议(较为复杂)。
  4. 长连接与内核调优
    a. keepalive、worker_rlimit_nofile、net.core.somaxconn、net.ipv4.ip_local_port_range、reuseport等
  5. Kubernetes场景
    a. 北向七层入口:Ingress(Controller 用Nginx);
    b. 东西向与中间件:Service(L4)或Nginx stream;
    c. 节点级/容器网络:CNI/iptables 规则由kube-proxy/ebpf控制,尽量别手工硬改;

六、结束语

这里也总结个三板斧:能用7层就别无脑走4层,能用4层就别硬啃内核,真到极致性能再上L3/NAT。

理解分层的边界与取舍,你的流量控制转发就会既稳又快,可万般演变应对业务的千万变化!