在限量抢购或者限时秒杀类场景中,除了要有效应对秒杀前后的流量高峰,还需要防止发生接受的下单量超过商品限购数量的问题,云数据库Redis企业版性能增强型实例的TairString结构支持简洁高效的限流器,可以很好地解决订单超量问题。本文介绍的方案也适用于其它需要限速或者限流的场景。

抢购限流器

TairString是Redis企业版性能增强型实例集成了阿里巴巴Tair后新增的数据结构,比原生Redis String功能更加强大,除了比特位(bit)操作外能够覆盖原生Redis String的所有功能。

TairString的EXINCRBY/EXINCRBYFLOAT命令与原生Redis String的INCRBY/INCRBYFLOAT命令功能类似,都可对value进行递增或递减运算,但EXINCRBY/EXINCRBYFLOAT支持更多选项,例如EXNXVERMINMAX等,详细说明请参见TairString命令。下文介绍的方案涉及MINMAX两个选项:

选项 说明
MIN 设置TairString value的最小值。
MAX 设置TairString value的最大值。

使用原生Redis String实现抢购,代码逻辑复杂,一旦管理不当,容易出现漏网订单,即明明商品已经抢完,却还有用户收到抢购成功的提示,造成不良影响,而使用TairString,只需要非常简单的代码即可实现严谨的订单数量限制,伪代码如下:

if(EXINCRBY(key_iphone, -1, MIN:0) == "would overflow")
    run_out();

限流计数器

抢购限流器类似,使用EXINCRBY命令的MAX选项可以实现限流计数器,伪代码如下:

if(EXINCRBY(rate_limitor, 1, MAX:1000) == "would overflow")
    traffic_control();

限流计数器的应用场景很多,例如并发限流、访问频率限制、密码修改次数限制等等。以并发限流为例,在请求的并发量突然超过系统的性能限制时,为了防止服务彻底崩溃引发更大的问题,采用限速器限制并发量,保证系统处理能力内的请求得到及时回应,是一种较合理的临时解决方案。使用TairStringEXINCRBY命令,您可以通过简单的代码设置一个并发限流器:

public boolean tryAcquire(Jedis jedis,String rateLimitor,int limiter){
    try {
        jedis.getClient().sendCommand(TairCommand.EXINCRBY,rateLimitor,"1","EX","1","MAX",String.valueOf(limiter));    // 设置限流器
        jedis.getClient().getIntegerReply();
        return true;
    }catch (Exception e){
        if(e.getMessage().contains("increment or decrement would overflow")){    // 检查返回结果中是否包含错误信息
            return false;
        }
        throw e;
    }
}