redis+lua高同時商品秒殺を実現
7257 ワード
マルチredisサービスノードがこの方法を推奨しない場合.マルチノードはredLockで分散ロックを実現
2、redisツールのパッケージ
コントロールレイヤ呼び出し
redis+lua , , , lua , 。redis io , 。 。
redis+Lua
1、 , redis, ,
2、 ,redis lua , , ,
3、 , redis ,
。999 。 (redis+lua + )tomcat 3500 ( ),tomcat 400/s, , , , , 。 , 。 。 。
。 。 。
1、 lua
local productId = tostring(KEYS[1])
local uid = tostring(ARGV[1])
--
local function successFun(success, msg, data)
success = success or 1
msg = msg or ""
data = data or {}
return cjson.encode({success = success, msg = msg, data = data})
end
--
local function response(errno, msg, data)
errno = errno or 0
msg = msg or ""
data = data or {}
return cjson.encode({errno = errno, msg = msg, data = data})
end
--
local log_key = "LOG_{" .. productId .. "}"
-- return log_key
local has_fetched = redis.call("sIsMember", log_key, uid)
if (has_fetched ~= 0) then
return response(-1, " ")
end
local result = false
--
local quan_key = "QUAN_{" .. productId .. "}"
local param = productId.."@";
local product = redis.call("hgetall",param)
if product==nil then
return response(-1, " ")
end
local nums = redis.call("hget",param,"num");
local n = tonumber(nums);
if (n<=0)then
return response(-1, " ")
end
redis.call("sAdd", log_key, uid)
local num = n-1;
local json = {};
json["id"] = productId;
json["num"] = n;
result = {uid = uid, pid = productId, product = json}
-- redis
redis.call("rPush", "DB_QUEUE", cjson.encode(result))
---
redis.call("hset", param, "num",(num))
redis.call('rPush',"user",cjson.encode(result))
if (result == false) then
return response(-1, " ")
else
return successFun(1, " ", result)
end
2、redisツールのパッケージ
package com.bus.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
/**
* @author wwz
* @date 2020-03-06
* @descrption:
*/
@Component
public class RedisUtils {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final String expireTime = "50000";
@SuppressWarnings("rawtypes")
@Autowired
private StringRedisTemplate stringRedisTemplateDemo;
private DefaultRedisScript getLockRedisScript;
private DefaultRedisScript releaseLockRedisScript;
private DefaultRedisScript realRedisScript;
private StringRedisSerializer argsStringSerializer = new StringRedisSerializer();
private StringRedisSerializer resultStringSerializer = new StringRedisSerializer();
private StringRedisSerializer realStringSerializer = new StringRedisSerializer();
private final String EXEC_RESULT = "1";
@SuppressWarnings("unchecked")
@PostConstruct
private void init() {
getLockRedisScript = new DefaultRedisScript();
getLockRedisScript.setResultType(String.class);
releaseLockRedisScript = new DefaultRedisScript();
realRedisScript = new DefaultRedisScript();
releaseLockRedisScript.setResultType(String.class);
realRedisScript.setResultType(String.class);
// lua
getLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/getLock.lua")));
releaseLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua")));
realRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/real.lua")));
}
/**
*
* @param key
* @param requestId
* @param retryTimes
* @return
*/
public JSONObject set(String key, String requestId, int retryTimes) {
try {
int count = 0;
while (true) {
String result = stringRedisTemplateDemo.execute(realRedisScript, argsStringSerializer, realStringSerializer,
Collections.singletonList(key), requestId);
JSONObject object = JSON.parseObject(result);
log.debug("result:{},type:{}", result, result.getClass().getName());
return object;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public boolean get(String key, String requestId) {
String result = stringRedisTemplateDemo.execute(releaseLockRedisScript, argsStringSerializer, resultStringSerializer,
Collections.singletonList(key), requestId);
if (EXEC_RESULT.equals(result)) {
return true;
}
return false;
}
}
コントロールレイヤ呼び出し
@RequestMapping("buy")
@ResponseBody
public Object orderBy(String productId){
String requestId = UUID.randomUUID().toString();
try{
//Executors.newFixedThreadPool()
JSONObject object = redisUtils.set(productId,requestId,0);
if(object == null){
return JsonResult.Fail(" , !");
}
String success = object.getString("success");
if("1".equals(success)){
taskExecutor.execute(new Runnable() {
@Override
public void run() {
orderService.createOrder(object.getJSONObject("data"),requestId);
}
});
return JsonResult.OK(" ");
}
return JsonResult.Fail(object.getString("msg"));
}catch (Exception e){
e.printStackTrace();
return JsonResult.Fail(" , !");
}
}