秒杀小案例,源码拿走不谢
背景
双11,618等各大电商节越来越多,最吸引人的莫不是秒杀商品的活动。那秒杀活动的背后,又是用什么技术实现的呢?是不是很好奇?另外不懂秒杀的,会被面试官虐吗?不要好奇,不要担心,小编给你介绍一个小案例,开拓下思维。
环境
jdk1.8+spring bootredis思路
模拟用户发起秒杀操作,首先判断库存是否足够,如果库存为0,则秒杀结束2.如果库存足够,则判断用户是否拿到该商品的redis分布式锁,如果拿到锁,则进入下单业务逻辑,如果没拿到锁,则30秒后再次进入秒杀逻辑 3. 进入下单逻辑后,还需要再次判断下库存,库存为0,则秒杀结束,反之生成订单。(这里是为了进一步防止超卖发生)
搭建
生成springboot项目,pom引入相关依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>application.yml添加redis相关配置
各位可以根据实际情况来配置
server: port: 6004 spring: application: name: spring-boot-ms # Redis配置 redis: host: 你自己的redis的ip地址 # Redis服务器连接端口 port: 你自己的redis的端口 # Redis服务器连接密码(默认为空) password: liyajie@2021 timeout: 30000 # 连接池最大连接数(使用负值表示没有限制) maxTotal: 50 # 连接池中的最大空闲连接 maxIdle: 10 numTestsPerEvictionRun: 1024 timeBetweenEvictionRunsMillis: 30000 minEvictableIdleTimeMillis: 1800000 softMinEvictableIdleTimeMillis: 10000 # 连接池最大阻塞等待时间(使用负值表示没有限制) maxWaitMillis: 1500 testOnBorrow: true testWhileIdle: true blockWhenExhausted: false JmxEnabled: true定义redis配置类
@Configuration @PropertySource(“classpath:application.yml”) public class RedisConfig { @Value(“${redis.host}“) private String host; @Value(“${redis.port}“) private int port; @Value(“${redis.timeout}“) private int timeout; @Value(“${redis.password}“) private String password; @Value(“${redis.maxIdle}“) private int maxIdle; @Value(“${redis.maxTotal}“) private int maxTotal; @Value(“${redis.maxWaitMillis}“) private int maxWaitMillis; @Value(“${redis.blockWhenExhausted}“) private Boolean blockWhenExhausted; @Value(“${redis.JmxEnabled}“) private Boolean JmxEnabled; @Bean public JedisPool jedisPoolFactory() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMaxTotal(maxTotal); jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); // 连接耗尽时是否阻塞, false报异常,true阻塞直到超时, 默认true jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted); // 是否启用pool的jmx管理功能, 默认true jedisPoolConfig.setJmxEnabled(JmxEnabled); jedisPoolConfig.setTestOnBorrow(true); JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password); return jedisPool; } }定义测试controller类
@RestController @Slf4j public class MsController { @Autowired private RedisUtils redisUtils; @Autowired MsService msService; //总库存 private long nKuCuen = 0; //商品key名字 private String shangpingKey = “computer_key”; //获取锁的超时时间 秒 private int timeout = 30 * 1000; /** * 设置分布式锁 * @param * @return boolean * @author liyajie * @createTime 2021/12/16 14:52 **/ @GetMapping(“/setnx/{key}/{val}”) public boolean setnx(@PathVariable String key, @PathVariable String val) { return redisUtils.setnx(key, val); } /** * 删除分布式锁 * @param * @return int * @author liyajie * @createTime 2021/12/16 14:52 **/ @GetMapping(“/delnx/{key}/{val}”) public int delnx(@PathVariable String key, @PathVariable String val) { return redisUtils.delnx(key, val); } /** * 抢单 * @param * @return List<String> * @author liyajie * @createTime 2021/12/16 14:54 **/ @GetMapping(“/qiangdan”) public List<String> qiangdan() { //抢到商品的用户 List<String> shopUsers = new ArrayList<>(); //构造很多用户 List<String> users = Collections.synchronizedList(new ArrayList<String>()); IntStream.range(0, 100000).parallel().forEach(b -> { users.add(“神器-“ + b); }); //初始化库存 nKuCuen = 10; //模拟开抢 users.parallelStream().forEach(b -> { String shopUser = this.qiang(b); if (!StringUtils.isEmpty(shopUser)) { shopUsers.add(shopUser); } }); return shopUsers; } private String qiang(String b) { //用户开抢时间 long startTime = System.currentTimeMillis(); //未抢到的情况下,30秒内继续获取锁 while ((startTime + timeout) >= System.currentTimeMillis()) { //商品是否剩余 if (nKuCuen <= 0) { break; } if (redisUtils.setnx(shangpingKey, b)) { //用户b拿到锁 log.info(“用户{}拿到锁…”, b); try { //商品是否剩余 if (nKuCuen <= 0) { break; } //模拟生成订单耗时操作,方便查看:神器-50 多次获取锁记录 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //抢购成功,商品递减,记录用户 nKuCuen -= 1; //抢单成功跳出 log.info(“用户{}抢单成功跳出…所剩库存:{}”, b, nKuCuen); return b + “抢单成功,所剩库存:” + nKuCuen; } finally { log.info(“用户{}释放锁…”, b); //释放锁 redisUtils.delnx(shangpingKey, b); } } else { log.info(“用户{}等待获取锁…”, b); // 用户b没拿到锁,在超时范围内继续请求锁,不需要处理 /*if (b.equals(“神器-50”) || b.equals(“神器-69”)) { log.info(“用户{}等待获取锁…”, b); }*/ } } return “”; } }测试
总结
这个秒杀小案例主要用到的知识点就是redis的分布式锁的唯一性,希望大家温故知新。
如需获取源码,请关注后,私信小编,非常感谢
发表回复