参考http://www.lua.org/ftp/.教程,下载5.3.5_1版本,本地安装,如果你使用的是Mac,那建议用brew工具直接执行brew install lua就可以顺利安装,
有关brew工具的安装可以参考https://brew.sh/.网站,建议翻墙否则会很慢。
安装IDEA插件,在IDEA->Preferences面板,Plugins,里面Browse repositories,在里面搜索lua,然后就选择同名插件lua。安装好后重启IDEA
配置Lua SDK的位置: IDEA->File->Project Structure,选择添加Lua,路径指向Lua SDK的文件夹
加载lua脚本
@Configuration public class RedisConfiguration { @Bean(name = "redPacket") public DefaultRedisScript<Long> loadRedPackRedisScript() { DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setLocation(new ClassPathResource("redPacket.lua")); redisScript.setResultType(java.lang.Long.class); return redisScript; } }**UserRedPacketService **
public interface UserRedPacketService { /** * 通过Redis实现抢红包 * @param redPacketId 红包编号 * @param userId 用户编号 *@return 0-没有库存,失败 * 1-成功,且不是最后一个红包 * 2-成功,且是最后一个红包 */ Long grapRedPacketByRedis(Long redPacketId, Long userId); }**UserRedPacketServiceImpl **
@Slf4j @Service public class UserRedPacketServiceImpl implements UserRedPacketService { private final RedisScript<Long> ratePacket; private final StringRedisTemplate stringRedisTemplate; public UserRedPacketServiceImpl(@Qualifier("redPacket") RedisScript<Long> ratePacket, StringRedisTemplate stringRedisTemplate) { this.ratePacket = ratePacket; this.stringRedisTemplate = stringRedisTemplate; } @Override public Long grapRedPacketByRedis(Long redPacketId, Long userId) { // 当前抢红包用户和日期信息 String args = userId + "_" + System.currentTimeMillis(); Long result = stringRedisTemplate.execute(ratePacket, Lists.newArrayList(redPacketId + ""), args); log.info("返回结果:{}", result); return result; } }UserRedPacketController
@Autowired private UserRedPacketService userRedPacketService; @GetMapping("/grapRedPacketByRedis") public boolean grapRedPacketByRedis(Long redPacketId, Long userId) { Long result = userRedPacketService.grapRedPacketByRedis(redPacketId, userId); Map<String, Object> resultMap = Maps.newHashMap(); boolean flag = result > 0; return flag; }红包编号为1和红包数量数量为8个以及每个红包金额为5 hset red_packet_1 stock 8 hset red_packet_1 unit_amount 10
public class RedisPackThread { public static void main(String[] args) throws InterruptedException { ThreadPoolExecutor redPacketExecutor = new ThreadPoolExecutor(100, 1000, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), r -> { // t.setName("redPacket"); return new Thread(r); }, (r, executor) -> { System.out.println("async sender is error rejected, runnable: " + r + ", executor: {}" + executor); }); CountDownLatch cdl = new CountDownLatch(100); CyclicBarrier cyclicBarrier = new CyclicBarrier(100); for (int i = 0; i < 100; i++) { int finalI = i; redPacketExecutor.submit(() -> { try { cyclicBarrier.await(); RestTemplate restTemplate = new RestTemplate(); Boolean result = restTemplate.getForObject("http://localhost:8888/grapRedPacketByRedis?redPacketId=1&userId=" + finalI, Boolean.class); if(Objects.requireNonNull(result)) { System.out.println("我是线程:" + finalI + " 我抢到红包了"); } } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } finally { cdl.countDown(); } }); } cdl.await(); redPacketExecutor.shutdown(); } }运行结果
redis执行lua脚本的时候,会将它作为一个整体执行,要么全部执行成功,如果出现异常则执行结果不会更新到redis中,很好的解决了高并发的问题。