NGINX 如何处理 TCP/UDP

你好,哈布尔!

在本文中,我们将了解 NGINX 如何处理 TCP/UDP 连接:从接受请求到日志记录。

NGINX 中的 TCP/UDP 处理架构

NGINX 是使用事件循环(也称为事件循环)建立在非阻塞 I/O 模型之上的。这允许您同时处理数以万计的连接。在 TCP/UDP 级别,NGINX 使用所谓的流模块,这些模块独立于 HTTP 块工作。TCP/UDP 会话处理的主要阶段如下:

  1. 接受后

  2. 预访问

  3. 访问

  4. SSL认证

  5. 预读

  6. 内容

  7. 日志

这些阶段中的每一个都完成自己的任务。

Post-accept:每个连接的 path 的开头

在 NGINX 内核接受新连接(通过非阻塞 accept() 和系统调用(如 epoll))后,Post-accept 阶段立即开始。这是启动模块的地方,如果客户端的 IP 地址来自中间设备(例如,负载均衡器或反向代理),则会更正客户端的 IP 地址。ngx_stream_realip_module

连接是异步接收的,这避免了大量并发连接的延迟,并且 ngx_stream_realip_module 模块本身使用特殊 Headers(例如 X-Real-IP)中的值将原始 IP 替换为真实 IP。

配置示例:

stream {
    server {
        listen 12345;

        # Используем модуль realip для определения реального IP
        real_ip_header X-Real-IP;
        set_real_ip_from 192.168.1.0/24;

        # Продолжаем маршрутизацию трафика
        proxy_pass backend;
    }
}

即使客户使用 NAT,您也始终可以找到他的真实地址。

预访问:过滤

Pre-access 阶段,NGINX 会检查连接的 pre-parameters 。这是触发 type modules 以限制连接数量以及设置变量和配置参数的地方。ngx_stream_limit_conn_modulengx_stream_set_module

共享内存区域允许您按键 (例如 IP 地址) 跟踪活动连接。您还可以为连接的后续处理指定条件和规则。

配置示例:

stream {
    # Определяем shared memory зону для хранения информации о соединениях
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    server {
        listen 12345;

        # Ограничиваем количество соединений с одного IP до 10
        limit_conn addr 10;

        # Здесь можно задать дополнительные параметры через ngx_stream_set_module
        # Например, установка кастомных переменных для дальнейшей обработки

        proxy_pass backend;
    }
}

因此,即使在这个阶段,您也可以扔掉坏人并节省资源。

访问:访问控制

Access 阶段是一个检查点,用于决定客户是否有资格访问服务。该模块检查访问规则,如果您使用它,则应用该指令。ngx_stream_access_modulenjs js_access

筛选配置示例:

stream {
    server {
        listen 12345;

        # Разрешаем доступ только из определённой подсети
        allow 192.168.1.0/24;
        deny all;

        proxy_pass backend;
    }
}

使用 njs 进行动态控制的示例:

stream {
    js_import my_access from js/my_access.js;

    server {
        listen 12345;
        js_access my_access.check;
        proxy_pass backend;
    }
}

在 js/my_access.js 文件中:

function check(ctx) {
    // Если IP не начинается с нужного префикса, отклоняем соединение
    if (!ctx.remoteAddress.startsWith("192.168.1.")) {
        return ctx.error(403);
    }
}
export default { check };

所以你可以想出某种适应需求的逻辑。

SSL认证

如果为 TCP/UDP 连接配置了 TLS/SSL,则会激活 SSL 阶段。该模块建立安全通道,执行 TLS 握手,验证证书并协商加密算法。ngx_stream_ssl_module

SSL 配置示例:

stream {
    server {
        listen 443 ssl;

        # Пути к SSL-сертификату и приватному ключу
        ssl_certificate     /etc/nginx/ssl/server.crt;
        ssl_certificate_key /etc/nginx/ssl/server.key;

        # Настройки протоколов и шифров для обеспечения безопасности
        ssl_protocols       TLSv1.2 TLSv1.3;
        ssl_ciphers         HIGH:!aNULL:!MD5;

        proxy_pass backend;
    }
}

普雷亚

Preread 阶段是最有趣的阶段之一。在这里,NGINX 将传入流的第一个字节读取到一个特殊的缓冲区中。为什么?以便 Journal 等模块可以在数据完全处理之前对其进行分析。对于 njs 用户,这是 js_preread 指令。ngx_stream_ssl_preread_module

读取并存储前 N 个字节(通常为 1-2 KB)以进行初步分析。

启用 preread 的示例配置:

stream {
    server {
        listen 443;

        # Включаем предварительное чтение для анализа первых байт
        ssl_preread on;

        proxy_pass backend;
    }
}

使用 njs 进行自定义分析的示例:

stream {
    js_import my_preread from js/my_preread.js;

    server {
        listen 443;
        js_preread my_preread.analyze;
        proxy_pass backend;
    }
}

在 js/my_preread.js 文件中:

function analyze(ctx) {
    // Если первые байты содержат ключевое слово, задаем переменную для последующей маршрутизации
    if (ctx.buffer && ctx.buffer.startsWith("SPECIAL")) {
        ctx.variables.special = true;
    }
}
export default { analyze };

这样您就可以提前了解流量的本质,甚至在主要处理之前就做出决定。

内容

内容阶段是完成所有数据工作的地方。在这里,NGINX 要么通过 直接将数据传递给后端,要么使用 njs 脚本(js_filter 指令)对其进行修改。proxy_pass

简单代理示例:

stream {
    upstream backend {
        server 127.0.0.1:8000;
        server 127.0.0.1:8001;
    }

    server {
        listen 12345;
        proxy_pass backend;
    }
}

通过 njs 进行动态过滤的示例:

stream {
    js_import my_filter from js/my_filter.js;

    upstream backend {
        server 127.0.0.1:8000;
        server 127.0.0.1:8001;
    }

    server {
        listen 12345;
        js_filter my_filter.process;
        proxy_pass backend;
    }
}

在 js/my_filter.js 文件中:

function process(ctx) {
    // Пример: заменяем все вхождения "foo" на "bar" в передаваемом буфере
    if (ctx.buffer) {
        ctx.buffer = ctx.buffer.replace(/foo/g, "bar");
    }
    return ctx;
}
export default { process };

日志

最后一个阶段 Log 负责记录服务器的工作结果。该模块记录处理时间、连接状态、客户端 IP 和其他有用信息。此外,您可以通过添加必要的变量来设置自定义格式,并且日志本身可以发送到 ELK 堆栈、Prometheus、Grafana 和其他工具。ngx_stream_log_module

简单日志记录设置的示例:

stream {
    server {
        listen 12345;

        access_log /var/log/nginx/stream_access.log;
        error_log  /var/log/nginx/stream_error.log;

        proxy_pass backend;
    }
}

使用 njs 进行自定义日志记录的示例:

stream {
    js_import my_logger from js/my_logger.js;

    server {
        listen 12345;
        js_log my_logger.customLog;
        proxy_pass backend;
    }
}

在 js/my_logger.js 文件中:

function customLog(ctx) {
    // Логируем важные параметры: IP клиента, длительность соединения, статус обработки
    console.log(`Client IP: ${ctx.remoteAddress}, Duration: ${ctx.duration}ms, Status: ${ctx.status}`);
}
export default { customLog };

作为行业专家提供的实用在线课程的一部分,您可以获得更相关的 IT 基础架构技能。 在目录中,您可以看到所有程序的列表,在日历中您可以注册公开课程。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇