负载均衡流控

明白了上面的扩缩容,对于取用令牌桶的消费和漏桶的消费也是需要了解的。下面将对两种情况分别介绍

我们可以看到流控接入负载均衡逻辑是在负载核心逻辑中,依赖配置的限流器,选择对应的限流策略。

# 代码位置 : balance/tl_ops_balance_core.lua

-- 流控介入
if tl_ops_manage_env.balance.limiter then
    local depend = tl_ops_limit:tl_ops_limit_get_limiter(node.service, node_id)
    if depend then
        -- 令牌桶流控
        if depend == tl_ops_constant_limit.depend.token then
            local token_result = tl_ops_limit_fuse_token_bucket.tl_ops_limit_token( node.service, node_id)  
            if not token_result or token_result == false then
                balance_count_node.tl_ops_balance_count_incr_node_fail(node.service, node_id)
                tl_ops_err_content:err_content_rewrite_to_balance("", "t-limit", balance_mode, tl_ops_constant_balance.cache_key.token_limit, "")
                return
            end
        end

        -- 漏桶流控 
        if depend == tl_ops_constant_limit.depend.leak then
            local leak_result = tl_ops_limit_fuse_leak_bucket.tl_ops_limit_leak( node.service, node_id)
            if not leak_result or leak_result == false then
                balance_count_node.tl_ops_balance_count_incr_node_fail(node.service, node_id)
                tl_ops_err_content:err_content_rewrite_to_balance("", "l-limit", balance_mode, tl_ops_constant_balance.cache_key.leak_limit, "")
                return
            end
        end
    end
end

令牌桶流控

令牌桶的实现思路之前我额外写过一些文章来讲解,这里就不细说了 具体可查看我的这篇文章 :fa-hand-o-right: 令牌桶的实现思路

这里需要注意的是,实际令牌桶算法应该要在获取令牌时加锁,避免并发问题。tl-ops-manage 接入负载逻辑中的限流器,并未加锁,所以是允许少量请求并发获取的情况。如果对加锁能接受,可以自行补充锁即可。

# 代码位置 : limit/fuse/tl_ops_limit_fuse_token_bucket.lua

-- get token with lazy generate
-- block 取用令牌数量
local tl_ops_limit_token_bucket = function( block )

    ...

    -- 取出令牌
    if token_bucket > block then
        local ok, _ = shared:incr(self.keys.token_bucket, -block)
        if not ok then
            return false
        end
        return true
    end

    -- 距离上次填充时间差 * 生成速率 = 需要补充的令牌
    ngx.update_time()
    local cur_time = ngx.now()
    local duration_token_bucket = (cur_time - pre_time) * rate
    if duration_token_bucket <= 0 then
        return false
    end

    local new_token_bucket = math.min(token_bucket + duration_token_bucket, capacity)

    -- 令牌还是不够
    if new_token_bucket < block then
        local ok, _ = shared:set(self.keys.token_bucket, new_token_bucket)
        if not ok then
            return false
        end

        local ok, _ = shared:set(self.keys.pre_time, cur_time)
        if not ok then
            return false
        end

        return false
    end

    -- 移除一个令牌
    local ok, _ = shared:set(self.keys.token_bucket, new_token_bucket - block)
    if not ok then
        return false
    end

    local ok, _ = shared:set(self.keys.pre_time, cur_time)
    if not ok then
        return false
    end

    return true
end

漏桶流控

和令牌桶不同,漏桶的实现是依靠向外流出令牌的方式,他们的区别可以大致这么理解,令牌桶是从桶中拿令牌,拿到令牌后执行请求漏桶是将请求当成令牌,一个一个放入桶

同令牌桶一致,漏桶限流器也未加锁。需要可自行补充

# 代码位置 : limit/fuse/tl_ops_limit_fuse_leak_bucket.lua

-- get leak with lazy generate
-- block 漏桶流速单位
local tl_ops_limit_leak_bucket = function( block )

    ...

    -- 当前堆积量
    local leak_bucket, _ = shared:get(self.keys.leak_bucket)
    if not leak_bucket then
        leak_bucket = 0
    end

    -- 漏桶当前时间区间内的剩余请求量 = 当前堆积量 - (在此时间区间应该被漏出的请求量) 
    -- ==
    -- 漏桶当前时间区间内的剩余请求量 = 当前堆积量 - (距离上次时间差 * 生成速率)
    ngx.update_time()
    local cur_time = ngx.now()
    local lave_leak_bucket = leak_bucket - (cur_time - pre_time) * rate
    if lave_leak_bucket <= 0 then
        return false
    end

    -- 溢出
    if lave_leak_bucket + 1 > capacity then
        return false
    end

    local new_leak_bucket = math.max(capacity, lave_leak_bucket)
    local ok, _ = shared:set(self.keys.leak_bucket, new_leak_bucket)
    if not ok then
        return false
    end

    local ok, _ = shared:set(self.keys.pre_time, cur_time)
    if not ok then
        return false
    end

    return true
end