最近的工作变动比较大,所以很长时间没有写关于nginx+lua的文章了,现在总算是有时间重新开始研究nginx+lua这个使nginx变的更加灵活的技术了。在学习过程中的经验和遇到的问题记录下来。安装过程可以结合之前的文章“nginx服务治理安装篇”,目前采用的是在nginx基础上安装一些模块,进行学习而没有选择使用openresty,是因为基于公司网络架构及部署架构限制。
之前一直在负责一个新的项目,由于发生了比较悲催的服务器宕机的情况,其主因是由于业务量激增导致服务器压力无法承受住导致服务器宕机业务中断,所以工作调整开始研究服务治理相关的技术以支持这种情况,保证业务量暴增的情况下服务器不被压死。
采用的是openresty的lua-resty-limit-traffic模块,这个模块不需要随着nginx安装时进行添加,在使用时直接引入即可,还是比较方便的。
/** Created by Erick.
** DateTime: 2019-1-4 16:59
**/
local _M = {}
function _M.split(str,delim)
if type(delim) ~= "string" or string.len(delim) <= 0 then
return
end
local start = 1
local tab = {}
while true do
local pos = string.find(str,delim,start,true)
if not pos then
break
end
table.insert(tab,string.sub (str,start,pos-1))
start = pos+string.len(delim)
end
table.insert(tab,string.sub(str,start))
return tab
end
return _M
/** Created by Erick.
** DateTime: 2019-1-9 16:52
** operate shared_dic
**/
local _M = {}
/** get value in dic
** dic : nginx shared dic name
** key : the nginx shared dic's key
**/
function _M.shared_dic_get(dic , key)
local value = dic:get(key)
return value
end
/** set value in dic
** dic : defined nginx shared dic name
** key : the nginx shared dic's key
** value : reset value
** exptime : the key and value exp time
**/
function _M.shared_dic_set(dic , key , value , exptime)
if not exptime then
exptime = 0
end
local succ , err = dic:set(key , value , exptime)
return succ
end
return _M
/** Created by Erick.
** DateTime: 2019-1-4 16:35
** 初始化文件限流数据到table中
**/
local limit_table = ngx.shared.my_limit_store
local splitutil = require("splitutil")
file = io.open("../lua/syslimitrate.txt","r")
if nil == file then
ngx.log(ngx.INFO, "文件读取失败,将采用默认值进行赋值")
local suc,err = limit_table:set("default" , "15-15")
else
for line in file:lines() do
local splitTable = splitutil.split(line , "-")
local sysFlag = splitTable[1]
local limitRate = splitTable[2]
local bursts = splitTable[3]
local tableVal = string.format("%s-%s" , limitRate , bursts)
ngx.log(ngx.INFO, "sysFlag = " , sysFlag , "限流阀值:流速-桶容量:" , tableVal)
limit_table:set(sysFlag , tableVal)
end
end
file:close()
初始化数据主要是从文件中读取然后放到nginx的定义的内存空间ngx.shared.my_limit_store中,若是读取文件失败则设置一个默认值。
/**格式为:渠道-每秒处理并发量-缓存容量**/
taobao-10-200
baidu-50-200
/** Created by Erick.
** DateTime: 2019-1-8 16:14
**/
// 获取请求参数
local splitutil = require("splitutil")
ngx.req.read_body()
local args, err = ngx.req.get_post_args()
local sysFlag = args["sys"]
local limit_table = ngx.shared.my_limit_store
local limitRate = limit_table:get(sysFlag)
if not limitRate then
ngx.log(ngx.INFO, "sysFlag can not found so set defalut value")
limitRate = limit_table:get("default")
end
// 获取到的值进行拆分并限流
local limitValue = splitutil.split(limitRate,"-")
local rate = tonumber(limitValue[1])
local burst = tonumber(limitValue[2])
local limit_req = require "resty.limit.req"
ngx.say("rate====" , rate , "---burst=====",burst)
// 根据配置项创建一个限流的table。
local lim, err = limit_req.new("my_limit_req_store", rate, burst)
if not lim then
ngx.log(ngx.ERR,
"failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
// 根据渠道标识进行限流
local delay, err = lim:incoming(sysFlag, true)
if not delay then
if err == "rejected" then
ngx.say("rejected access service..............")
ngx.log(ngx.INFO,"rejected access service..............",err)
return ngx.exit(503)
end
ngx.log(ngx.INFO, "failed to limit req: ", err)
return ngx.exit(500)
end
ngx.log(ngx.INFO,"access received..............")
ngx.say("access received..............")
/** Created by Erick.
** DateTime: 2019-1-9 14:27
** this lua script can reset limitrate value
**/
local splitutil = require("splitutil")
local sharedoperutil = require("sharedoperutil")
// get request param
local args, err = ngx.req.get_uri_args()
local limitvalue = args["limitvalue"]
ngx.say("-----------limitvalue-----------" , limitvalue)
local limitParam = splitutil.split(limitvalue , "-")
local sysFlag = limitParam[1]
ngx.say(sharedoperutil.shared_dic_get(ngx.shared.my_limit_store,sysFlag))
local rateBurst = string.format("%s-%s" , limitParam[2] , limitParam[3])
ngx.say("-------------rateBurst:" , rateBurst)
sharedoperutil.shared_dic_set(ngx.shared.my_limit_store , sysFlag , rateBurst , 0)
ngx.say("---------------" , sharedoperutil.shared_dic_get(ngx.shared.my_limit_store,sysFlag))
以上是所有关于Lua脚本的内容,下面与nginx进行整合。
以下是nginx.conf的配置。
worker_processes 2;
pid logs/nginx.pid;
events {
worker_connections 10240;
}
error_log logs/access.log info;
http {
// 定义以及引入限流模块
lua_shared_dict my_limit_store 10m;
lua_shared_dict my_limit_req_store 100m;
lua_package_path "nginx-1.12.1/lua-resty-limit-traffic-0.05/lib/?.lua;nginx-1.12.1/nginx-1.12.1/lua/?.lua;;";
include mime.types;
log_format main '$remote_addr - $time_local - $request_method - $request_uri - $status - $body_bytes_sent - $request_time';
access_log logs/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 10;
keepalive_requests 500;
client_header_timeout 60;
client_body_timeout 60;
send_timeout 60;
open_file_cache max=1024 inactive=120s;
open_file_cache_valid 30;
open_file_cache_min_uses 5;
gzip on;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript application/json;
gzip_disable msie6;
client_body_in_file_only off;
client_body_temp_path http_body_dir 1 2;
client_body_in_single_buffer on;
client_max_body_size 10m;
client_body_buffer_size 128k;
client_header_buffer_size 4k;
large_client_header_buffers 4 16k;
// ---------初始化限流信息start-------------
init_by_lua_file lua/limitdata_init.lua;
// ---------初始化限流信息end-------------
// 这部分用到一个自动上线下的模块,在文章最后简单说下
upstream myupstream {
zone zone_for_myupstream 1m;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
listen 8890;
server_name localhost;
location /dynamic {
#allow 127.0.0.1;
#deny all;
dynamic_upstream;
}
location /limit{
#allow 127.0.0.1;
#deny all;
// --------重置限流阀值start--------
content_by_lua_file lua/limitvaluereset_content.lua;
//-------重置限流阀值end----------
}
location / {
//--------访问控制进行限流start---------
access_by_lua_file lua/limitrate_access.lua;
//---------访问控制进行限流end-----------
proxy_pass http://myupstream;
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
引入lua脚本的地方增加了注释说明,至此限流也就说完了,上边有个地方是自动上下线服务在这里说明下。
调用api接口可以实现例如服务器的上线,下线,配置权重,增加/删除机器,调整后不用修改比较方便,不做详细说明了,感兴趣的可以去github上查看,点击这里学习。