我正在尝试配置 NGINX/OpenResty,以根据 SOAP 请求中存在的字符串将 SOAP 调用代理到 2 个不同的服务器。
我能做什么:我能够根据 SOAP 客户端调用的路径将请求代理到 2 个不同的服务器:
location /pathA {
proxy_pass http://www.ServerA.com/PathA/;
}
location /pathB {
proxy_pass http://www.ServerB.com/PathB/;
}
我不能做的事:
我无法根据请求的内容分离流量。我认为主要原因是我无法正确组装 LUA 脚本来提取信息并随后使用它来代理请求。
location / {
conten_by_lua '
ngx.req.read_body()
local match = ngx.re.match(ngx.var.request_body,"STRING TO FIND")
if match then
proxy_pass http://www.ServerA.com/PathA/;
else
proxy_pass http://www.ServerB.com/PathB/;
我怎样才能做到这一点?
我安装了 OpenResty 并且 LUA 运行良好。
我记得我读到过一篇文章,如果请求不是 HTTP POST,则ngx.req.read_body()
无法工作。对吗?
感谢您的帮助。
答案1
您几乎自己做到了,我唯一会做的不同的事情就是使用rewrite_by_lua
而不是content_by_lua
:
location / {
set $proxy "";
rewrite_by_lua '
ngx.req.read_body()
local match = ngx.re.match(ngx.var.request_body, "STRING TO FIND")
if match then
ngx.var.proxy = "www.ServerA.com"
else
ngx.var.proxy = "www.ServerB.com"
end
';
proxy_pass http://$proxy$uri;
}
如果请求不是 HTTP POST 或具有空主体,ngx.req.read_body()
将返回nil
,因此最好添加额外的检查:
location / {
set $proxy "";
rewrite_by_lua '
ngx.req.read_body()
local body = ngx.var.request_body
if (body) then
local match = ngx.re.match(body, "STRING TO FIND")
if match then
ngx.var.proxy = "www.ServerA.com"
else
ngx.var.proxy = "www.ServerB.com"
end
else
ngx.status = ngx.HTTP_NOT_FOUND
ngx.exit(ngx.status)
end
';
proxy_pass http://$proxy$uri;
}
您还可以限制允许的方法:
location / {
limit_except POST { deny all; }
...
}
还有一件事。使用此配置,如果您使用域名而不是 IP 地址指定后端,则需要resolver
指令。您可以使用本地名称服务器(如果有),或者使用外部服务器,例如 Google 公共 DNS(8.8.8.8)或 ISP 为您提供的 DNS。
答案2
下面是我在 openresty 中用来根据发布的 json 主体区分 dev 和 prod webhook 的代码片段:
server {
listen 8080;
resolver 114.114.114.114 8.8.8.8 valid=30s;
location /webhook {
limit_except POST { deny all; }
set $proxy "";
rewrite_by_lua_block {
local devWebhook = "myservice.dev" -- todo
local prodWebhook = "mysevice.prod" -- todo
function getFile(file_name)
local f = assert(io.open(file_name, 'r'))
local string = f:read("*all")
f:close()
return string
end
ngx.req.read_body()
local data = ngx.req.get_body_data()
if nil == data then
local file_name = ngx.req.get_body_file()
if file_name then
data = getFile(file_name)
end
end
local json = require("cjson.safe")
local t = json.decode(data)
if not (type(t) == "table" and t["msg"]) then
ngx.exit(400)
end
local msg = ngx.unescape_uri(t["msg"])
if not (type(msg) == "string" and #msg > 0) then
ngx.exit(400)
end
if string.find(msg, '"title":".*test product.*"') then
ngx.var.proxy = devWebhook
else
ngx.var.proxy = prodWebhook
end
}
proxy_pass http://$proxy$uri;
}
}