在nginx

时间:2019-06-27 18:59:44

标签: nginx lua nginx-location mirror mirroring

我有2个环境(envA,envB)。 envA需要将其请求镜像到envB,并另外两次调用envB,其中包含来自envA中响应的信息。 envA对envB的响应不感兴趣,这本质上是一劳永逸的情况。目的是确保envA的操作和性能绝不会受到对envB的调用的影响。我们选择使用nginx作为代理,并使其进行镜像。我们还编写了一个lua脚本来处理我上面描述的逻辑。

问题在于,即使来自envA服务的响应迅速返回,nginx仍保留envA响应返回给调用方的过程,直到完成对envB的其他3次调用为止。我想以某种方式摆脱这种障碍。

我们的团队没有任何熟悉过lua或nginx的人,所以我敢肯定,我们所拥有的并不是最好/正确的方法...但是到目前为止,我们一直在做调整连接并读取超时,以确保我们将任何阻塞减少到最短的时间。但这只是不能使我们到达想要的位置。

经过研究,我发现https://github.com/openresty/lua-nginx-module#ngxtimerat其中;据我了解与在Java中创建ScheduledThreadPoolExecutor相同,只是将作业排队到其上并将其自身与原始请求流隔离开,从而消除了阻塞。但是我对范围如何变化了解得不够多,以确保我没有搞乱数据/变量明智的方法,而且我也不确定要使用哪个库来调用envB,因为我们一直在使用ngx到目前为止,根据上面链接中的文档,.location.capture在使用ngx.timer.at时不是可选的。因此,对于任何关于如何正确使用ngx.timer.at或其他方法来实现此目标的见解,我将不胜感激。

这是我们正在使用的lua代码。我已经很困惑了 但我们所拥有的是骨头,主要部分是content_by_lua_block部分

http {
    upstream envA {
        server {{getenv "ENVA_URL"}};
    }
    upstream envB {
        server {{getenv "ENVB_URL"}};
    }

    server {
        underscores_in_headers on;
        aio threads=one;
        listen       443 ssl;

        ssl_certificate     {{getenv "CERT"}};
        ssl_certificate_key {{getenv "KEY"}};

        location /{{getenv "ENDPOINT"}}/ {
            content_by_lua_block {
                ngx.req.set_header("x-original-uri", ngx.var.uri)
                ngx.req.set_header("x-request-method", ngx.var.echo_request_method)
                resp = ""
                ngx.req.read_body()

                if (ngx.var.echo_request_method == 'POST') then
                    local request = ngx.req.get_body_data()
                    resp = ngx.location.capture("/envA" .. ngx.var.request_uri, { method = ngx.HTTP_POST })
                    ngx.location.capture("/mirror/envB" .. ngx.var.uri, { method = ngx.HTTP_POST })
                    ngx.location.capture("/mirror/envB/req2" .. "/envB/req2", { method = ngx.HTTP_POST })
                    ngx.status = resp.status
                    ngx.header["Content-Type"] = 'application/json'
                    ngx.header["x-original-method"] = ngx.var.echo_request_method
                    ngx.header["x-original-uri"] = ngx.var.uri
                    ngx.print(resp.body)
                    ngx.location.capture("/mirror/envB/req3" .. "/envB/req3", { method = ngx.HTTP_POST, body = resp.body })
                 end
            }
         }

        location /envA {
            rewrite /envA(.*) $1  break;
            proxy_pass https://envAUrl;
            proxy_ssl_certificate     {{getenv "CERT"}};
            proxy_ssl_certificate_key {{getenv "KEY"}};
        }

        ###############################
        # ENV B URLS
        ###############################
        location /envB/req1 {
            rewrite /envB/req1(.*) $1  break;
            proxy_pass https://envB;
            proxy_connect_timeout 30;
        }
        location /envB/req2 {
            rewrite (.*) /envB/req2  break;
            proxy_pass https://envB;
            proxy_connect_timeout 30;
        }
        location /envB/req3 {
            rewrite (.*) /envB/req3 break;
            proxy_pass https://envB;
            proxy_connect_timeout 30;
        }
     }
}

就我们所看到的问题而言...在通过代理访问envA与不使用envA时,看到的响应时间(秒)增加了。

2 个答案:

答案 0 :(得分:1)

发出第一个答案大约五分钟后,我记得有一种正确的方法来进行这种清理活动。

函数ngx.timer.at允许您安排某个函数在一定时间后运行,包括0,在当前处理程序完成后立即执行。您可以使用它来安排清理任务和其他操作,以便将响应返回给客户并且请求以干净的方式结束。

这是一个例子:

content_by_lua_block {
    ngx.say 'Hello World!'
    ngx.timer.at(0, function(_, time)
        local start = os.time()
        while os.difftime(os.time(), start) < time do
        end
        os.execute('DISPLAY=:0 zenity --info --width 300 --height 100 --title "Openresty" --text "Done processing stuff :)"')
    end, 3)
}

请注意,由于我没有进行任何设置来检查它是否真的被调用,因此我使用zenity来显示带有消息的弹出窗口。


编辑:我可能应该提到,要在计划的事件中发送HTTP请求,您需要使用cosocket API,该API不支持即开即用的HTTP请求,但是Google快速搜索弹出了this库似乎正是这样做的。

答案 1 :(得分:0)

编辑:很快我就找到了一个更好的解决方案(请参见我的其他答案),但我也将其搁置一旁,因为至少可以从某种意义上知道这样做确实有价值(而且您可能不应该这样做)

我想出的最快的方法就是这个

        content_by_lua_block {
            ngx.say 'Hello World!'
            local start = os.time()
            ngx.flush()
            ngx.req.socket:close()
            while os.difftime(os.time(), start) < 4 do
            end
        }

首先使用ngx.flush()将实际输出刷新到客户端,然后仅使用ngx.req.socket:close()关闭连接。可以肯定的是,这不是最干净的选择,但是在大多数情况下,它是可行的。如果可以找到更好的解决方案,我会发布另一个答案:)