• OpenResty使用漏桶算法实现限流


    前言

    其它项目组需要调用接口,添加接口限流,防止项目被狂掉宕机。生产用了openresty,所以在openresty上添加按接口限流,同时,需按照不同接口有不同的限流规则,使用openresty中内置的漏桶算法方式限流。

    桶算法

    漏桶算法思路简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率

    通俗解释。

    比如设置rate为100,burst为50,即允许1s放进来100个请求,桶大小为50。

    那么1s内:

    第1-100个请求会访问成功。

    第101-150个请求会进入burst。

    第150个请求之后的会直接失败返回。

    openresty的说明文档:https://github.com/openresty/lua-resty-limit-traffic/blob/master/lib/resty/limit/req.md

    限流配置

    使用OpenResty的漏桶算法进行限流配置,不同接口配置不同的标准,所以测试了两个接口test1和test2

    主要分两步:

    1. 添加限流使用的lua脚本
    2. 在反向代理中配置限流的lua脚本

    ps: 因为大多数使用情况还是会反向代理,所以直接在反向代理中配置lua

    添加lua脚本01和02的区别,仅限于漏桶的参数配置不同

    添加lua脚本01

    lualib\utils 路径下创建lua脚本

    lua脚本内容:

    -- utils/limit_req_leaky_bucket.lua
    local limit_req = require "resty.limit.req"
    
    -- rate:  5/s即为每秒3个请求,增加桶容量为1/s,超过5/s不到(5+1)/s的delay,排队等候
    local lim, err = limit_req.new("my_limit_req_store1", 5, 1)
    if not lim then
        ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
        return ngx.exit(500)
    end
    
    local _M = {}
    
    
    function _M.incoming()
        local key = ngx.var.binary_remote_addr
        local delay, err = lim:incoming(key, true)
        if not delay then
            if err == "rejected" then
                return ngx.exit(503) -- 超过的请求直接返回503
            end
            ngx.log(ngx.ERR, "failed to limit req: ", err)
            return ngx.exit(500)
        end
        
        -- 此方法返回,当前请求需要delay秒后才会被处理,和他前面对请求数
        -- 所以此处对桶中请求进行延时处理,让其排队等待,就是应用了漏桶算法
        -- 此处也是与令牌桶的主要区别
        if delay >= 0.001 then
            ngx.sleep(delay)
        end
    end
    
    return _M
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    添加lua脚本02

    lualib\utils 路径下创建lua脚本

    lua脚本内容:

    -- utils/limit_req_leaky_bucket.lua
    local limit_req = require "resty.limit.req"
    
    -- rate:  3/s即为每秒3个请求,增加桶容量为1/s,超过3/s不到(3+1)/s的delay,排队等候
    local lim, err = limit_req.new("my_limit_req_store2", 3, 1)
    if not lim then
        ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
        return ngx.exit(500)
    end
    
    local _M = {}
    
    
    function _M.incoming()
        local key = ngx.var.binary_remote_addr
        local delay, err = lim:incoming(key, true)
        if not delay then
            if err == "rejected" then
                return ngx.exit(503) -- 超过的请求直接返回503
            end
            ngx.log(ngx.ERR, "failed to limit req: ", err)
            return ngx.exit(500)
        end
        
        -- 此方法返回,当前请求需要delay秒后才会被处理,和他前面对请求数
        -- 所以此处对桶中请求进行延时处理,让其排队等待,就是应用了漏桶算法
        -- 此处也是与令牌桶的主要区别
        if delay >= 0.001 then
            ngx.sleep(delay)
        end
    end
    
    return _M
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在nginx.conf中添加配置文件

    # --- 限流 ---
    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    # ------------
    
    
    http {
        # --- 反向代理 ---
        include       /etc/nginx/conf.d/*.conf;
        # -----------------------
    
    
        include       mime.types;
        default_type  application/octet-stream;
    
        sendfile        on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        # --- 限流 ---
        lua_code_cache on;
       
        # 共享内存
        lua_shared_dict my_limit_req_store1 100M;
        lua_shared_dict my_limit_req_store2 100M;
        # -----------
    
        # --- 反向代理 ---
        upstream backend_server {
            server 127.0.0.1:8080;
        }
        # -----------------------
    
        server {
            listen       80;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                root   html;
                index  index.html index.htm;
            }
    
    
    
            location /test1 {
                
                # --- 限流 ---
                access_by_lua_block {
                    local limit_count = require "utils.limit_req_leaky_bucket1"
                    -- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。
                    if ngx.req.is_internal() then
                        return
                    end
                    limit_count.incoming()
                }
                # ------------
    
                # --- 反向代理 ---
                # 如果内容源是反向代理
                proxy_pass http://backend_server;
                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 /test2 {
                
                # --- 限流 ---
                access_by_lua_block {
                    local limit_count = require "utils.limit_req_leaky_bucket2"
                    -- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。
                    if ngx.req.is_internal() then
                        return
                    end
                    limit_count.incoming()
                }
                # ------------
    
                # --- 反向代理 ---
                # 如果内容源是反向代理
                proxy_pass http://backend_server;
                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;
                # -----------------------
    
            }
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    参考

    http://www.guanshanw.com/post/67951.html

  • 相关阅读:
    C语言练习百题之将一个数组逆序输出
    卷S人的166页精品Java面试手册,17大java面试系列专题让你全方位暴击大厂Java面试官!
    理解实现搜索二叉树
    UDP编程
    问题解决:python接入支付宝沙箱问题处理
    【Linux】Linux 基础开发工具(yum、vim、gcc/g++、gdb、make/makefile、git)
    查找总价格为目标值的两个商品 ---- 双指针
    基于C++实现表达式转换
    智能文件改名:高效复制并删除冗余,简化文件管理“
    ASO优化之关于Google Play中的搜索引擎优化
  • 原文地址:https://blog.csdn.net/qq1515312832/article/details/133070059