1、引入依赖
<!-- Jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
2、编写配置文件
#Redis spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.database=3 spring.redis.password= spring.redis.timeout=2000 #Jedis #最大空闲数 spring.redis.jedis.pool.max-idle=6 #最大连接数 spring.redis.jedis.pool.max-active=10 #最小空闲数 spring.redis.jedis.pool.min-idle=2 #连接超时 spring.redis.jedis.pool.timeout=2000
3、编写配置类
package com.zero4j.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; @Configuration public class JedisConfig { private Logger logger = LoggerFactory.getLogger(JedisConfig.class); @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.min-idle}") private int minIdle; @Bean public JedisPool jedisPool(){ JedisPoolConfig jedisPoolConfig=new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMinIdle(minIdle); jedisPoolConfig.setMaxTotal(maxActive); JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout,password); logger.info("JedisPool连接成功:"+host+"\t"+port); return jedisPool; } }
4、实现业务逻辑
@Autowired private JedisPool jedisPool;
Jedis jedis = null; try { jedis = jedisPool.getResource(); String poolId = "pool1"; String poolLuaRedisKey = "cardLottery." + poolId + "-lua-shaKey"; String poolLuaRedisValue = jedis.get(poolLuaRedisKey); byte[] shaKey = null; if(poolLuaRedisValue!=null){ shaKey = poolLuaRedisValue.getBytes(); } if (shaKey == null) { ClassPathResource cps = new ClassPathResource("lua/test.lua"); InputStream in = cps.getInputStream(); byte[] data = new byte[in.available()]; in.read(data); shaKey = jedis.scriptLoad(data); jedis.set("cardLottery." + poolId + "-lua-shaKey", new String(shaKey)); } System.out.println("本次执行的lua脚本的sha为:" + new String(shaKey)); List<String> keys = new ArrayList<>(); keys.add(poolId); keys.add("100"); keys.add("0.5"); List<String> vals = new ArrayList<>(); for (int i = 0; i < 10; i++) { Date _startTime = new Date(); Object result = jedis.evalsha(new String(shaKey), keys, vals); Date _endTime = new Date(); long _timeCross = _endTime.getTime() - _startTime.getTime(); System.out.println("lua的第" + (i + 1) + "次执行花了" + _timeCross + "毫秒,执行结果:" + result); } }catch(Exception e){ e.printStackTrace(); }finally { if(jedis!=null){ jedis.close(); } }
5、Lua脚本
local key = 'cardPool.'..KEYS[1]..'-bucket' -- redis中令牌桶对象的key local initTokens = tonumber(KEYS[2]) -- {初始化时的令牌数} local probability = tonumber(KEYS[3]) -- {中奖概率:小于0的数字(百分比概念)} print(probability) local bucket = redis.call('hgetall', key) -- 当前 key 的令牌桶对象 local currentTokens -- 当前token数量 local runCount -- 累计执行次数统计 -- 若当前桶未初始化,先初始化令牌桶 if table.maxn(bucket) == 0 then -- 初始桶内令牌 currentTokens = initTokens runCount = 0 redis.call('hset', key, 'stock', currentTokens) redis.call('hset', key, 'probability', probability) redis.call('hset', key, 'runCount', 0) elseif table.maxn(bucket) == 6 then currentTokens = tonumber(bucket[2]) probability = tonumber(bucket[4]) runCount = tonumber(bucket[6]) end -- 如果当前桶内令牌小于 0,抛出异常 assert(currentTokens >= 0) redis.call('hset', key, 'runCount', runCount + 1) if currentTokens == 0 then -- 如果当前令牌 == 0, 返回false return -1 else -- 如果当前令牌 大于 0, 则执行抽奖逻辑 if probability<> nil or probability >= 1 or math.random() <= probability then -- 如果中奖概率大于或等于1,生成随机的小数如果在probability内,则中奖 -- 更新当前桶内的令牌 -1, 返回true redis.call('hset', key, 'stock', currentTokens - 1) return 1 else return 0 end end
注意:如果Lua脚本返回是true和false的类型,则jedis实际获得的值为1和null。