Nginx mirror中(或者流量复制了后)怎么使用Lua读到response body?

Nginx mirror中怎么使用Lua读到response body?
我是用Lua脚本来接收 mirror的response body
我的conf文件如下

user  nginx;
worker_processes  auto;

error_log  /var/log/error.log  notice;
pid        /var/run/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" '
        '"$gzip_ratio" $request_time $bytes_sent $request_length';

    open_log_file_cache max=1000 inactive=60s;
    access_log  /var/log/access.log  main;
    error_log  /var/log/lua_error.log error;

    sendfile        on;

    keepalive_timeout  65;

    upstream backends {
        server localhost:9099;
    }
    upstream mirror1_backend {
        server localhost:9099;
    }
    upstream mirror2_backend {
        server localhost:9099;
    }
    upstream mirror3_backend {
        server localhost:9099;
    }
    upstream mirror4_backend {
        server localhost:9099;
    }
    upstream mirror5_backend {
        server localhost:9099;
    }
    upstream mirror6_backend {
        server localhost:9099;
    }
    upstream mirror7_backend {
        server localhost:9099;
    }
    upstream mirror8_backend {
        server localhost:9099;
    }
    upstream mirror9_backend {
        server localhost:9099;
    }
    upstream mirror10_backend {
        server localhost:9099;
    }

    server {
        listen 9099;
        location / {
            return 200;
        }
    }

    #gzip  on;

    server {
        listen       8000-8050;
        listen       9000-9050;
        server_name  _;

        lua_need_request_body on;
        set $resp_body "";
        body_filter_by_lua '
            local resp_body = string.sub(ngx.arg[1], 1, 1000)
            ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_body
            if ngx.arg[2] then
                ngx.var.resp_body = ngx.ctx.buffered
                ngx.log(ngx.ERR,"ngx.arg[1]===========", ngx.var.resp_body)
            end
        ';


        location / {
            mirror /mirror1;

            body_filter_by_lua_block {
                ngx.log(ngx.ERR,"ngx.arg[1]===========", ngx.arg[1])
                ngx.log(ngx.ERR,"ngx.arg[2]===========", ngx.arg[2])
            }

            proxy_pass  http://backends;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location = /mirror1 {
            internal;
            resolver 192.168.36.10 valid=10s;

            proxy_pass http://onnx-prep-ep-sh-28f42-default.seldon.svc.cluster.local:9000$request_uri;
            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # 在这个lua block里面读取发给mirror的request和response
            # 但是没有任何输出
            body_filter_by_lua_block {
                ngx.log(ngx.ERR,"ngx.arg[1]===========", ngx.arg[1])
                ngx.log(ngx.ERR,"ngx.arg[2]===========", ngx.arg[2])
            }

        }
        location = /mirror2 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror2_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            #mirror2_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror3 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror3_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            #mirror3_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror4 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror4_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #mirror4_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror5 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror5_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #mirror5_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror6 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror6_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #mirror6_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror7 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror7_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #mirror7_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror8 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror8_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #mirror8_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror9 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror9_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #mirror9_content_by_lua_file /app/bizlogging.lua;
        }
        location = /mirror10 {
            internal;
            resolver 192.168.36.10 valid=10s;
            proxy_pass http://mirror10_backend$request_uri;

            proxy_method $request_method;

            mirror_request_body on;
            proxy_pass_request_body on;

            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Real-PORT $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #mirror10_content_by_lua_file /app/bizlogging.lua;
        }
    }

    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
}

流量通过mirror复制后,转发给下游服务 http://onnx-prep-ep-sh-28f42-default.seldon.svc.cluster.local...$request_uri;
我想获取发给这个服务的response body,应该如何获取到?

我已经添加了Lua文件,并尝试在Lua文件中获取response,但是我发现只能获取response的header而不能获取response的body。不知道有什么Lua写法可以获取response body?
看上去mirror里会把所有的response扔掉。有没有办法复制一份流量,并且不扔掉response?

刚看到AI回复说可以在Lua脚本里直接发请求,这个可以,在Lua脚本里自己控制发请求给下游。
但是还需要控制traffic。比如说有50%请求可以发给service1,50%发给service2,在lua脚本里怎么能控制traffic呢?

阅读 792
avatarAI BotBETA

在 Nginx 的 mirror 指令中,Nginx 默认不会将响应体(response body)发送给 mirror 指定的后端服务,只发送请求头和请求体。这是因为 mirror 指令主要用于监控或日志记录,而不是处理响应数据。因此,直接在 mirror 指令的上下文中通过 Lua 脚本获取响应体是不可行的。

如果你需要捕获并处理响应体,你可以考虑以下几种方法:

1. 使用 subrequest 代替 mirror

使用 ngx.location.capturengx.location.capture_multi 在 Lua 脚本中发起子请求,这样你可以访问和处理响应体。这种方法需要你更改配置,不再使用 mirror,而是改用 Lua 脚本进行子请求的发起和响应处理。

例如,在适当的 Lua 脚本位置(如 access_by_lua_blockrewrite_by_lua_block),你可以添加如下代码:

local res = ngx.location.capture('/some_internal_location', {
    method = ngx.req.get_method(),
    body = ngx.req.get_body_data() or '',
    headers = ngx.req.get_headers(),
    ...
});

if res.status == 200 then
    ngx.log(ngx.ERR, "Captured response body: ", res.body);
end

然后,在 Nginx 配置中定义 /some_internal_location,指向你希望镜像到的服务。

2. 自定义 Nginx 模块

如果你需要更高级的控制,并且不介意修改 Nginx 的源代码,你可以编写一个自定义的 Nginx 模块来捕获和处理响应体。这通常涉及到对 Nginx 内部机制有深入的了解,并且需要编译 Nginx 以包含这个模块。

3. 使用其他中间件或工具

考虑使用其他支持流量复制并处理响应的中间件或服务,如 HAProxy、Envoy 或专门的日志和监控工具。这些工具可能提供更灵活的配置选项,包括捕获和转发响应体。

4. 修改 Nginx 源代码(不推荐)

如果你非常熟悉 Nginx 的内部实现,并且没有其他可行的解决方案,你可以考虑修改 Nginx 的源代码来支持 mirror 指令处理响应体。然而,这种方法需要大量的工作,并且可能引入稳定性问题,因为你需要维护一个自定义的 Nginx 版本。

结论

对于大多数情况,使用 ngx.location.capture 或类似的 Lua 脚本方法来发起子请求是处理响应体的最简单和最直接的方法。这允许你在不改变 Nginx 标准行为的情况下,灵活地捕获和处理响应数据。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题