nginx openresty lua-resty-http 使用的一些问题记录

需求背景

需求是使用 nginx 做一个 https 服务的代理

nginx 收到 http 请求后,需要修改 body 中的某些参数值,然后将修改后的数据发送到目标服务器(https)

本来以为很简单的需求,结果中间出现了不少岔子,这里记录一下

知识点汇总

  1. location = xxx {} 是精确匹配,location xxx {} 是前缀匹配。
  2. 用 lua 脚本处理请求时,如果脚本找不到,客户端看到的是 404 not found,和路由配置错了一个样。
  3. lua-resty-http 模块管理员权限直接安装就行(不需要和 nginx 一起编译)。
  4. httpc:request_uri 请求时,必要的配置是 resolver(做 dns 解析);https 请求时,加上 ssl_verify = false 可以避免 https 证书带来的问题(python requests 包也有这个参数,但是不推荐使用,不安全)。
  5. 像这种靠近底层的工具如果修改了 http 请求的 body 时,一定要关注是否已经设置过 Content-Length 这个 header 了,可以去掉或者重新设置。否则可能造成的问题是 数据异常截断 或者 莫名奇妙的等待

详细问题

问题1:配置了 location 但是显示 404

代码如下

复制代码
location = /aigw {
    content_by_lua_file conf/lua/aigw.lua;
}

这个代码先后改了两次,实际有两个原因客户端都会显示 404

  1. location = 是精确匹配,请求有后缀就找不到了
  2. 如果 conf/lua/aigw.lua 文件找不到,也不会提示内部错误信息,客户端看到的也是 404 问题

问题 2:module 'resty.http' not found:

使用如下命令安装,注意必须是 sudo 权限下,否则不行

复制代码
sudo apt-get update
sudo apt-get install -y luarocks
sudo luarocks install lua-resty-http

问题 3:no resolver defined to resolve "xxxxx"

问题代码如下,这个错误是下面 err 的值

复制代码
local res, err = httpc:request_uri(forwardUrl, {
...
})

解决方案:这个错误表明 NGINX 没有配置 DNS 解析器来解析 xxxxx 域名。你需要在 NGINX 配置文件中添加一个 resolver 指令来指定 DNS 服务器。

在 nginx.conf 文件的 http 或 server 块中添加以下内容:

复制代码
http {
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # 其他配置...
}

8.8.8.8 和 8.8.4.4 是 Google 的公共 DNS 服务器,你也可以使用其他 DNS 服务器。

添加后,重新加载 NGINX 配置:

复制代码
sudo nginx -s reload

问题 4:unable to get local issuer certificate

这也是发送请求时的错误信息。

这个错误表明 NGINX 无法验证目标 HTTPS 服务器的 SSL 证书。下面是两种解决方案,我才用了第一种

  1. 忽略 SSL 证书验证(不推荐用于生产环境): 在 httpc:request_uri 调用中添加 ssl_verify = false 参数。

    lua 复制代码
    local res, err = httpc:request_uri(forwardUrl, {
    	method = ngx.req.get_method(),
        body = body_data,
    	headers = ngx.req.get_headers(),
        ssl_verify = false  -- 这里是新增加的参数
    })
  2. 指定 CA 证书: 下载并指定一个可信的 CA 证书文件。(我没试过)

    首先,下载 CA 证书文件(例如 ca-certificates.crt),然后在 httpc:request_uri 调用中指定 ssl_ca_cert 参数。

    lua 复制代码
    local res, err = httpc:request_uri(forwardUrl, {
        method = ngx.req.get_method(),
        body = body_data,
        headers = ngx.req.get_headers(),
        ssl_ca_cert = "/path/to/ca-certificates.crt"
    })

问题 5:修改请求 body 值之后,请求就卡住了

代码依旧是这个代码

lua 复制代码
local res, err = httpc:request_uri(forwardUrl, {
   	method = ngx.req.get_method(),
    body = body_data,
   	headers = ngx.req.get_headers(),
    ssl_verify = false
})

当我把 body_data 转为 json,修改其中的值,又转为字符串之后,这个网络请求就莫名奇妙的卡住了。

废了很大的力气查了很多资料也没有结果。自己调试打印了两组数据,对比了数据类型,还是没找到原因。

最后我打印了 ngx.req.get_method() 发现其中有一个头信息是

复制代码
content-length: 216

猛然想到:因为我把 body 改小了,但是 content-length 值很大,这个 client 就会一直等着剩余的数据传输,所以就会一直卡着不动了。

修改方案是:

lua 复制代码
local new_headers = ngx.req.get_headers()
-- 删除 Content-Length 头,否则会导致 http client 卡住
new_headers["Content-Length"] = nil
new_headers["content-length"] = nil

local res, err = httpc:request_uri(forwardUrl, {
    method = ngx.req.get_method(),
    body = new_body,
    headers = new_headers,
    ssl_verify = false
})

问题 6:最终返回给客户端的数据是截断的

问题代码如下

lua 复制代码
-- res 是 httpc:request_uri 的返回值
ngx.status = res.status
for k, v in pairs(res.headers) do
    if k ~= "Transfer-Encoding" then
        ngx.header[k] = v
    end
end

-- 将 body 中的 model 替换回去
local ok, res_body_json = pcall(cjson.decode, res.body)
if not ok then
    ngx.log(ngx.ERR, "Failed to decode JSON: ", res.body)
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

res_body_json.model = model_prefix .. model_suffix
local new_res_body = cjson.encode(res_body_json)

ngx.print(new_res_body)
ngx.exit(ngx.HTTP_OK)

上面的代码返回的 json 数据,一直只有一部分。

这个答案也很简单,和之前卡住的问题一样,因为我修改了返回的 body 值,而 ngx 在最开始逐一设置了 resheader 值,显然又把 Content-Length 带进去了。加上这么两句以后,问题解决:

lua 复制代码
ngx.header["Content-Length"] = #new_res_body
ngx.header["content-length"] = #new_res_body
相关推荐
椎49513 小时前
苍穹外卖前端nginx错误之一解决
运维·前端·nginx
2301_7873284919 小时前
25.负载均衡-Nginx、HAProxy、LVS 全解析
nginx·负载均衡·lvs
siriuuus19 小时前
Nginx 负载均衡调度算法
运维·nginx·负载均衡
tjsoft1 天前
设置 windows nginx.exe 每天 重启
运维·windows·nginx
舰长1151 天前
nginx 负载均衡配置
运维·nginx·负载均衡
foundbug9992 天前
查看nginx日志文件
linux·nginx·github
一只程序烽.2 天前
java项目使用宝塔面板部署服务器nginx不能反向代理找到图片资源
java·服务器·nginx
福大大架构师每日一题2 天前
nginx 1.29.2 发布:AWS-LC 支持、QUIC及SSL优化等重要更新
nginx·ssl·aws
金色天际线-2 天前
nginx + spring cloud + redis + mysql + ELFK 部署
redis·nginx·spring cloud
帅帅梓3 天前
nginx访问控制 用户认证 https
运维·nginx·https