欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

“撸啊”不止能秒杀!| lua+redis实现高并发抢令牌

程序员文章站 2024-01-20 13:01:40
...

“撸啊”不止能秒杀!| lua+redis实现高并发抢令牌

目录

1.什么是lua

2.为什么是lua

3.抢令牌的业务场景 + 代码实现

 


1.什么是lua

撸啊(Lua)是一门小巧的脚本语言,它的主要特点是轻量级、可扩展。主要应用场景是:游戏开发、独立应用脚本、Web 应用脚本。lua还有一个重要的特点是可以轻松嵌入到其他开发语言中。lua脚本语句比较简单,语句风格和C语言类型,可以通过Lua在线工具进行语言试验。 Redis 2.6.0 版本开始的,使用内置的 Lua 解释器,可以对 Lua 脚本进行求值。

2.为什么是lua

介绍了这么多究竟为什么要使用redis+lua实现秒杀场景呢?

因为在秒杀场景中需要有优惠券信息列表、已成功抢券用户表,采用redis存储的优势是读取快。而Lua操作是原子性的,只要将1. 验证用户是不是已经成功抢券 、2.券数量减一 、3.在已成功抢券用户表中添加当前用户 这三个逻辑放到lua脚本中便可以保证数据的准确性,能够合理的避免高并发下一券多发、库存错误等情况。所以redis+lua二者结合可以完美实现秒杀的业务需求。

3.抢令牌的业务场景 + 代码实现

redis+lua的配方不止适用于秒杀场景,本文实战的场景是一个抽象的抢令牌模型,实现的语言是java+redis+lua

完整的业务是这样滴:

乐淘商城有10个客服,后台需要保证每次前台请求都返回一个客服的id,且每个客服服务的客户数量要平均。

具体实现方案:

1.每天凌晨将客服id放到reids list中

2.请求时返回第一个id,同时将此id插入到list队尾,同时删除此id(维护一个循环队列)

代码实现:

lua脚本,将脚本保存到以.lua结尾的文件中,保存到系统能加载的路径下

--- Created by Administrator.
--- DateTime: 2020/04/20 19:46
--- 获取队尾第一个数据,并删除,将删除的数插入队头

local key = KEYS[1]
if (redis.call("EXISTS", key) == 0 ) then
     return "defaultErrorCode"
else
    local val = redis.call("RPOP", key)
    redis.call("LPUSH",key,val)
    return val
end

lua文件要在redisConfig中做为资源配置

@Configuration
public class RedisConfig {
    @Bean
    public DefaultRedisScript<Long> redisScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/employeeAgent.lua")));
        redisScript.setResultType(Long.class);
        return redisScript;
    }
}

java代码需要调用RedisTemplate和DefaultRedisScript两个对象,lua脚本是以资源文件的形式引入的。

@Autowired
private RedisTemplate redisTemplate;
@Resource
private DefaultRedisScript<Long> redisScript;

public void getEmployeeAgent() {
        String redisKey = "testKey";
        if(!redisTemplate.hasKey(redisKey)){
                List<String> employeeInfoList = Arrays.asList("1","2","3","4","5","6","7","8","9","10");
                if(EmptyUtil.isNotEmpty(employeeInfoList)){
                    redisTemplate.opsForList().leftPushAll(redisKey,employeeInfoList);
                    redisTemplate.expire(redisKey,Constant.FORTY_EIGHT, TimeUnit.HOURS);
                }
            }
        ArrayList<String> keys = Lists.newArrayList();
        keys.add(redisKey);
        String result = (String)redisTemplate.execute(redisScript, keys);
    }

经过测试后id是有序且平均分配。看看!用这么少的代码就可以实现那么有挑战性的需求,所以lua+redis可真是一个宝藏组合!

下次面试官再问你如何实现秒杀,大声的告诉他撸啊!

参考资料:

Lua 教程
Redis EVAL简介

Lua在线工具


“撸啊”不止能秒杀!| lua+redis实现高并发抢令牌