玖叶教程网

前端编程开发入门

Nginx结合Lua实现限流(nginx加lua脚本限流)


背景

在实际工作中总是会遇到由于业务量激增服务器压力无法承受,导致服务器宕机业务中断,这样对公司会造成很大的影响,所以开始研究服务治理相关的技术以支持这种情况,保证业务量暴增的情况下服务器不被压死。

限流模块

采用的是openresty的lua-resty-limit-traffic模块,这个模块不需要随着nginx安装时进行添加,在使用时直接引入即可,还是比较方便的。

设计思路

  • 初始化限流策略信息,例如按照渠道或者ip地址。每个的处理速度也就是处理的并发量,还有就是桶容量大小(超过每秒处理的量后还看可以缓存多少)
  • 获取请求参数
  • 根据参数在策略中查找并获取相应的阀值,若不存在则采用默认值。
  • 使用lua-resty-limit-traffic进行限流。

代码实现

  • 先说两个工具类,一个是字符串分割,一个nginx缓存的设置和获取。

字符串分割 splitutil.lua

/** Created by Erick.

**  DateTime: 2020-1-14

**/

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

缓存的get和set,sharedoperutil.lua

/** Created by Erick.

**  DateTime: 2020-1-14

**  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
  • 初始化限流策略信息 limitdata_init.lua 初始化限流策略信息 limitdata_init.lua
/** Created by Erick.

**  DateTime: 2020-1-14

**  初始化文件限流数据到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中,若是读取文件失败则设置一个默认值。

syslimitrate.txt文件内容

/**格式为:渠道-每秒处理并发量-缓存容量**/

taobao-10-200

baidu-50-200
  • 获取请求参数并查询对应的限流阀值进行限流limitrate_access.lua
/** Created by Erick.

**  DateTime: 2020-1-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..............")
  • 动态调整阀值信息limitvaluereset_content.lua
/** Created by Erick.

**  DateTime: 2020-1-14

**  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脚本的地方增加了注释说明,至此限流也就说完了,上边有个地方是自动上下线服务在这里说明下。

ngx_dynamic_upstream

调用api接口可以实现例如服务器的上线,下线,配置权重,增加/删除机器,调整后不用修改比较方便,不做详细说明了,感兴趣的可以去github上查看。

在接下来的一段时间内会将lua语音的基本用法简单整理。



发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言