“擼啊”不止能秒殺!| 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在線工具


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章