openststry(二)
access_by_lua_block
[root@centos7 conf]# ls /usr/local/openresty/lualib/ cjson.so librestysignal.so ngx redis resty tablepool.lua [root@centos7 conf]# mkdir -p /usr/local/openresty/lualib/untils [root@centos7 conf]#
[root@centos7 conf]# cat /usr/local/openresty/lualib/utils/limit_conn.lua
[root@centos7 conf]# cat /usr/local/openresty/lualib/utils/limit_conn.lua -- utils/limit_conn.lua local limit_conn = require("resty.limit.conn") -- new 的第四个参数用于估算每个请求会维持多长时间,以便于应用漏桶算法 local limit, limit_err = limit_conn.new("limit_conn_store", 10, 2, 0.05) if not limit then error("failed to instantiate a resty.limit.conn object: ", limit_err) end local _M = {} function _M.incoming() local key = ngx.var.binary_remote_addr local delay, err = limit:incoming(key, true) if not delay then if err == "rejected" then return ngx.exit(503) end ngx.log(ngx.ERR, "failed to limit req: ", err) return ngx.exit(500) end if limit:is_committed() then local ctx = ngx.ctx ctx.limit_conn_key = key ctx.limit_conn_delay = delay end if delay >= 0.001 then ngx.log(ngx.WARN, "delaying conn, excess ", delay, "s per binary_remote_addr by limit_conn_store") ngx.sleep(delay) end end function _M.leaving() local ctx = ngx.ctx local key = ctx.limit_conn_key if key then local latency = tonumber(ngx.var.request_time) - ctx.limit_conn_delay local conn, err = limit:leaving(key, latency) if not conn then ngx.log(ngx.ERR, "failed to record the connection leaving ", "request: ", err) end end end return _M
重点在于这句话local limit, limit_err = limit_conn.new("limit_conn_store", 8, 2, 0.05)
,允许的最大并发为常规的8个,突发的2个,一共8+2=10个并发,详情参考https://github.com/openresty/lua-resty-limit-traffic/blob/master/lib/resty/limit/conn.md#new
被拒绝的请求直接返回503
if err == "rejected" then
return ngx.exit(503) -- 超过的请求直接返回503
end
重点在于这句话,模拟每个请求0.5秒处理完成
content_by_lua_block {
ngx.sleep(0.5)
}
注意在限制连接的代码里面,我们用 ngx.ctx
来存储 limit_conn_key
。这里有一个坑。内部重定向(比如调用了 ngx.exec
)会销毁 ngx.ctx
,导致 limit_conn:leaving()
无法正确调用。 如果需要限连业务里有用到 ngx.exec
,可以考虑改用 ngx.var
而不是 ngx.ctx
,或者另外设计一套存储方式。只要能保证请求结束时能及时调用 limit:leaving()
即可。
重新加载配置文件
worker_processes 1; daemon off; events { worker_connections 1024; } error_log logs/error_lua.log info; http { default_type application/octet-stream; sendfile on; keepalive_timeout 65; lua_code_cache on; # 注意 limit_conn_store 的大小需要足够放置限流所需的键值。 # 每个 $binary_remote_addr 大小不会超过 16 字节(IPv6 情况下),算上 lua_shared_dict 的节点大小,总共不到 64 字节。 # 100M 可以放 1.6M 个键值对 lua_shared_dict limit_conn_store 100M; server { listen 80; location / { access_by_lua_block { local limit_conn = require "utils.limit_conn" -- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。 if ngx.req.is_internal() then ngx.log(ngx.INFO,">> lua redirect ") return end limit_conn.incoming() ngx.log(ngx.INFO,">>> lua reqeust comes!") } content_by_lua_block { -- 模拟请求处理时间,很重要,不加可能测试不出效果 -- 生产中没有请求是只返回一个静态的index.html的! ngx.sleep(0.5) } log_by_lua_block { local limit_conn = require "utils.limit_conn" limit_conn.leaving() ngx.log(ngx.INFO,">>> lua reqeust leave!") } } } }
openresty -s reload
安装测试工具
yum -y install httpd-tools
[root@centos7 work]# ab -V This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ [root@centos7 work]#
测试
上面的配置是每个请求处理0.5秒,并发是10
- 10个请求,并发为1
ab -n 10 -c 1 127.0.0.1/
# 请求全部成功,用时5s左右
Concurrency Level: 1
Time taken for tests: 5.012 seconds
Complete requests: 10
Failed requests: 0
[root@centos7 work]# ab -n 10 -c 1 127.0.0.1/
return _M[root@centos7 conf]# ab -n 10 -c 1 127.0.0.1/ This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: openresty/1.19.3.2 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: / Document Length: 0 bytes Concurrency Level: 1 Time taken for tests: 5.010 seconds Complete requests: 10 Failed requests: 0 Write errors: 0 Total transferred: 1620 bytes HTML transferred: 0 bytes Requests per second: 2.00 [#/sec] (mean) Time per request: 501.049 [ms] (mean) Time per request: 501.049 [ms] (mean, across all concurrent requests) Transfer rate: 0.32 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 501 501 0.1 501 501 Waiting: 501 501 0.1 501 501 Total: 501 501 0.1 501 501 Percentage of the requests served within a certain time (ms) 50% 501 66% 501 75% 501 80% 501 90% 501 95% 501 98% 501 99% 501 100% 501 (longest request) [root@centos7 conf]#
- 10个请求,并发为10
ab -n 10 -c 10 127.0.0.1/
# 请求全部成功,用时1.5s左右
Concurrency Level: 10
Time taken for tests: 1.505 seconds
Complete requests: 10
Failed requests: 0
[root@centos7 conf]# ab -n 10 -c 10 127.0.0.1/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient).....done
Server Software: openresty/1.19.3.2
Server Hostname: 127.0.0.1
Server Port: 80
Document Path: /
Document Length: 0 bytes
Concurrency Level: 10
Time taken for tests: 1.004 seconds
Complete requests: 10
Failed requests: 0
Write errors: 0
Total transferred: 1620 bytes
HTML transferred: 0 bytes
Requests per second: 9.96 [#/sec] (mean)
Time per request: 1004.494 [ms] (mean)
Time per request: 100.449 [ms] (mean, across all concurrent requests)
Transfer rate: 1.57 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.2 1 1
Processing: 501 502 0.2 502 502
Waiting: 501 502 0.3 502 502
Total: 502 502 0.2 502 503
Percentage of the requests served within a certain time (ms)
50% 502
66% 502
75% 502
80% 503
90% 503
95% 503
98% 503
99% 503
100% 503 (longest request)
[root@centos7 conf]#
- 20个请求,并发为10,并发为10并不会触发限制条件,所以能成功!注意和下面并发11的区别!
ab -n 20 -c 10 127.0.0.1/
# 请求全部成功,用时2s左右
Concurrency Level: 10
Time taken for tests: 2.005 seconds
Complete requests: 20
Failed requests: 0
- 22个请求,并发为11 重点解释一下:
- 并发不是qps,并发11不是说第一秒发11个请求,然后第二秒再发送11个请求,而是发完第一波紧接着发第二波,每一波的间隔时间不一定是1秒,下面的1.506 seconds就能看出来,按理应该是2s但是并不是
- 第一波11个请求发送过去了,但是只能处理10个,所以成功了10个,紧接着第二波11个请求发过去了,但是第一波大部分未处理完成所以第二波的都失败了,也有处理完成了的可以接着处理,所以至少会成功10个,下面显示的是11个
- 此处的大量失败应该是并发超过了10,触发了限制条件让nginx worker线程睡眠了,所以导致后面的请求大量失败
- -- 触发限制条件
if delay >= 0.001 then
ngx.sleep(delay) -- ngx worker睡眠
end
[root@centos7 work]# ab -n 20 -c 10 127.0.0.1/
[root@centos7 conf]# ab -n 20 -c 10 127.0.0.1/ This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: openresty/1.19.3.2 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: / Document Length: 0 bytes Concurrency Level: 10 Time taken for tests: 1.507 seconds Complete requests: 20 Failed requests: 0 Write errors: 0 Total transferred: 3240 bytes HTML transferred: 0 bytes Requests per second: 13.27 [#/sec] (mean) Time per request: 753.406 [ms] (mean) Time per request: 75.341 [ms] (mean, across all concurrent requests) Transfer rate: 2.10 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.2 0 1 Processing: 501 502 0.3 502 502 Waiting: 501 502 0.3 502 502 Total: 502 502 0.2 502 503 Percentage of the requests served within a certain time (ms) 50% 502 66% 502 75% 503 80% 503 90% 503 95% 503 98% 503 99% 503 100% 503 (longest request)
ab -n 22 -c 11 127.0.0.1/
# 11个成功,11个失败
Concurrency Level: 11
Time taken for tests: 1.506 seconds
Complete requests: 22
Failed requests: 11
Non-2xx responses: 11 # HTTP状态非2xx的有11个,说明限并发成功(只有有非2xx的返回才会显示这句话)
[root@centos7 work]# ab -n 22 -c 11 127.0.0.1/
[root@centos7 conf]# ab -n 22 -c 11 127.0.0.1/ This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: openresty/1.19.3.2 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: / Document Length: 0 bytes Concurrency Level: 11 Time taken for tests: 2.013 seconds Complete requests: 22 Failed requests: 0 Write errors: 0 Total transferred: 3564 bytes HTML transferred: 0 bytes Requests per second: 10.93 [#/sec] (mean) Time per request: 1006.415 [ms] (mean) Time per request: 91.492 [ms] (mean, across all concurrent requests) Transfer rate: 1.73 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.3 0 1 Processing: 502 548 148.1 502 1007 Waiting: 501 548 148.1 502 1007 Total: 502 549 148.0 503 1007 Percentage of the requests served within a certain time (ms) 50% 503 66% 503 75% 503 80% 503 90% 504 95% 1005 98% 1007 99% 1007 100% 1007 (longest request) [root@centos7 conf]#
log_by_lua_block
反向代理
上面测试的是content_by_lua
,也就是内容直接在lua中生成,但是实际中内容有可能是后端服务器生成的,所以可以设置反向代理或者负载均衡,如下为反向代理配置
location / { access_by_lua_block { local limit_conn = require "utils.limit_conn" -- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。 if ngx.req.is_internal() then return end limit_conn.incoming() } log_by_lua_block { local limit_conn = require "utils.limit_conn" limit_conn.leaving() } # 反向代理 proxy_pass http://172.17.0.3:8080; proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 60; proxy_read_timeout 600; proxy_send_timeout 600; }
内部重定向
location / {
access_by_lua_block {...}
content_by_lua_block {...}
log_by_lua_block {...}
}
openresty-best-practices/ngx_lua/lua-limit.md
https://github.com/moonbingbing/openresty-best-practices/blob/master/ngx_lua/lua-limit.md