项目中多维度限流的实现

多维度限流的实现

限流(Rate Limiting)是保护服务稳定性的重要手段,通常在短时间内大量请求涌入时,通过丢弃部分请求来保证系统稳定。限流的实现有多种方式,本文将介绍两种主要的限流方式:单实例限流和分布式限流,并探讨如何在实际应用中选择适合的限流模式。

单实例限流

单实例限流是针对单个服务实例设置的限流规则,主要用于防止某个实例因过载而崩溃。每个实例分配限流额度,确保单个实例接收到的QPS和Connection在设定阈值内,从而保障实例的稳定性。

示例应用场景: 假设在短视频APP中遇到突发热点新闻「xxx事件」,导致视频信息服务请求量激增。为避免服务崩溃,可以对请求量进行限流,确保部分用户能够正常使用APP,并进行服务扩容以进一步降低损失。

限流过滤器

java
复制代码
public class RateLimiterFilter {
    public void filterRequest(GatewayContext gatewayContext) {
        // 示例代码,具体实现略
        int statusCode = gatewayContext.getResponse().getHttpResponseStatus().code();
        int responseBodySize = gatewayContext.getResponse().getFutureResponse().getResponseBodyAsBytes().length;
    }
}
分布式限流

分布式限流通过SDK和中心存储(如Redis或MySQL)结合实现精确的集群限流模式。每个实例运行SDK限流逻辑,通过令牌桶算法向中心存储申请令牌,实现集群整体的流量限制。

优点

  1. 能够精准控制集群整体的MAX QPS,即使限流值小于实例数。
  2. 实例的权重并不影响触发限流的概率,所有实例共享令牌桶。

缺点

  1. 引入SDK和中心存储,增加运维成本。
  2. 请求前需要从中心存储申请令牌,带来一定性能开销。
常见限流算法
  1. 令牌桶算法:适合处理突发流量,每秒生成固定数量的令牌,请求需要消耗令牌才能被处理。
  2. 漏桶算法:通过固定速率处理请求,适合稳定流量的限流需求。
限流模式选择
  1. 单实例限流:适用于保护服务自身不被打垮的场景,如计算型服务、缓存型服务、代理型服务等。
  2. 分布式限流:适用于对集群整体限流精度有要求的服务,主要保护其依赖的第三方服务(数据库、缓存、中台服务等)。
实现限流器工厂接口

可以定义一个限流器工厂接口,用于创建不同类型的限流器:

java
复制代码
public interface RateLimiterFactory {
    /**
     * 获取对应的限流器
     * @param serviceId
     * @param flowControlConfig
     * @return RateLimiter
     */
    RateLimiter getRateLimiter(String serviceId, Rule.FlowControlConfig flowControlConfig);
}
Guava限流器实现

使用Guava的RateLimiter进行限流,示例代码如下:

public class GuavaRateLimiterFactory implements RateLimiterFactory {
    private final Map<String, RateLimiter> resourceRateLimiterMap = new ConcurrentHashMap<>();

    @Override
    public RateLimiter getRateLimiter(String serviceId, Rule.FlowControlConfig flowControlConfig) {
        return resourceRateLimiterMap.computeIfAbsent(serviceId, id -> {
            if (flowControlConfig == null || flowControlConfig.getMaxPermits() <= 0) {
                return null;
            }
            return RateLimiter.create(flowControlConfig.getMaxPermits());
        });
    }
}
Redis Lua脚本实现分布式限流

使用Lua脚本在Redis中实现限流器,确保操作的原子性。以下是Lua脚本的关键代码:

lua
复制代码
local num = redis.call('incr', KEYS[1])
if tonumber(num) == 1 then
    redis.call('expire', KEYS[1], ARGV[1])
end
if tonumber(num) > tonumber(ARGV[2]) then
    return 0
else
    return 1
end

在Java代码中,使用Jedis库来执行Lua脚本,示例代码如下:

java
复制代码
public Boolean executeScript(String key, int expire, int limit) {
    // Lua脚本字符串
    String lua = buildLuaScript();

    // 创建Jedis实例
    Jedis jedis = new Jedis();

    // 将Lua脚本加载到Redis,获取脚本的SHA-1哈希值
    String scriptLoad = jedis.scriptLoad(lua);

    // 执行Lua脚本
    Object result = jedis.evalsha(scriptLoad, Arrays.asList(key), Arrays.asList(String.valueOf(expire), String.valueOf(limit)));

    // 返回结果,1表示通过,0表示被限流
    return result.equals(1L);
}

private String buildLuaScript() {
    return "local num = redis.call('incr', KEYS[1])\n" +
           "if tonumber(num) == 1 then\n" +
           "    redis.call('expire', KEYS[1], ARGV[1])\n" +
           "end\n" +
           "if tonumber(num) > tonumber(ARGV[2]) then\n" +
           "    return 0\n" +
           "else\n" +
           "    return 1\n" +
           "end";
}
结论

限流是保障服务稳定性的重要手段。在实际应用中,根据服务的不同特点和需求,可以选择合适的限流模式。单实例限流适用于保护服务自身,分布式限流则适用于对集群整体限流精度有要求的场景。通过合理配置限流规则,可以有效地提升服务的稳定性和抗压能力

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值