Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenResty 转发请求超时问题分析报告 #45

Open
jinhailang opened this issue Feb 21, 2019 · 0 comments
Open

OpenResty 转发请求超时问题分析报告 #45

jinhailang opened this issue Feb 21, 2019 · 0 comments

Comments

@jinhailang
Copy link
Owner

jinhailang commented Feb 21, 2019

问题描述

某产品对接最新版 waf proxy(支持 http2.0)后,上传图片超时,其他请求正常。

问题分析

从 Nginx 的 error 和 access 日志发现,请求转发到 WAF 超时(超过 60 秒),导致客户端主动断开连接(499 状态)。
为了兼容 http2(构建子请求时,将http2 改写成 http1.1),我们使用开源库 resty.http 代替了原来 Openresty 原生的 API ngx.location.capture,性能下降很大,因为 ngx.location.capture 是 Nginx 子请求,本质上不是 http 请求,直接运行在 C 语言级别,效率非常高。当 POST body 数据较大时,这个库转发
的速度会非常慢,从而引起超时。

经过多次对比测试发现,跟 client_body_buffer_size 设置的大小有关,当请求 body 大于 client_body_buffer_size 设置的值时,请求就会变得很慢,甚至超时;反之则正常。
client_body_buffer_size 用来指定 Nginx 读取请求 body 的 buffer 大小值,默认是 8k(32位操作系统)当请求的 body 大于这个值,则会将剩余的字节写入本地文件(磁盘)。所以,根本原因是 resty.http 库转发请求读写磁盘文件效率太低。

解决方案

可以从两方面进行优化解决:

  • 减少 resty.http 转发的超时时间(httpc:set_timeout(300) 不超过 300 毫秒),因为是转发 WAF 是 旁路 逻辑,可以避免转发 WAF 过慢影响客户正常业务请求。影响就是较大 body(大于 client_body_buffer_size 设置大小) 不会经过 WAF 检测,其实,考虑到性能等问题,WAF 也基本不会处理较大 body (> 1M ) 。
  • 建议优化 Nginx 配置项 client_body_buffer_size,提高请求 body 读取效率。建议设置 client_body_buffer_size 64k

更多

  • 分析 resty.http 库源码,看是否能对读写进行优化;
  • 预计很快 Openresty 的 API 就会支持 http2.0,到时候再替换回来;

问题相关代码片段

access_by_lua_block {
            local http = require "resty.http"

            local httpc = http.new()
            httpc:set_timeout(300)
            httpc:set_proxy_options({http_proxy = "http://127.0.0.1:9107"})

            ngx.req.read_body()

            local req_method = ngx.req.get_method()
            local req_body = ngx.req.get_body_data()
            local req_headers = ngx.req.get_headers()
            local req_url = "http://" .. ngx.var.host .. "/__to_waf__" .. ngx.var.uri
            if ngx.var.is_args == "?" then
                req_url = req_url .. "?" .. ngx.var.query_string
            end

            local res, err = httpc:request_uri(req_url, {
                version = 1.1,
                method = req_method,
                body = req_body,
                headers = req_headers,

                keepalive_timeout = 60,
                keepalive_pool = 16,
            })

            if not res then
                ngx.log(ngx.ERR, "failed to request: ", err, ". url: ", req_url)
                return ngx.exit(ngx.OK)
            end

            local status = res.status or 0
            local body = res.body

            if status == 200 then
                if not body or body == "" then
                    return ngx.exit(ngx.HTTP_CLOSE)
                end
            elseif status == 403 or status == 400 then
                ngx.req.set_method(ngx.HTTP_GET)
                return ngx.exec("/__waf_page__/" .. status .. ".html")
            end
        }
@jinhailang jinhailang changed the title openResty 转发请求超时问题分析报告 OpenResty 转发请求超时问题分析报告 Mar 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant