秒杀小案例,源码拿走不谢

秒杀小案例,源码拿走不谢

背景

双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的分布式锁的唯一性,希望大家温故知新。

如需获取源码,请关注后,私信小编,非常感谢

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Proudly powered by WordPress | Theme: HoneyWaves by SpiceThemes