赞
踩
当秒杀开始的时候,会有大量的高并发,解决高并发是我们的第一个目标,其次就是高并发的时候,会有超卖的现象,解决超卖是我们的第二个目标。
1.引入pom中的依赖,application写配置信息。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
#rabbitmq
spring.rabbitmq.host=10.9.151.60
#redis
spring.redis.host=10.9.151.60
spring.redis.port=9000
3.radis中获取到需要秒杀商品的数量,后面消费者层会使用
2.写rabbitMQ的模板
启动类中写模板,并且用@bean声明,这样在系统启动的时候,就会生成bean对象让spring管理起来。
@SpringBootApplication @EnableEurekaClient @MapperScan("cn.tedu.seckill.mapper") public class StarterSeckill { public static void main(String[] args) { SpringApplication.run(StarterSeckill.class,args); } //模板声明 @Bean//声明一个队列 public Queue queue01(){ return new Queue("seckillQ",false,false,false,null); } @Bean//声明交换机 public DirectExchange ex01(){ return new DirectExchange("seckillEX"); } @Bean//绑定关系 public Binding bind01(){ return BindingBuilder.bind(queue01()).to(ex01()).with("seckill"); } }
3.controller层
首先需用方法1要把所有的秒杀商品获取到,方法2是用户点击商品详细信息的时候调用的,方法3作为生产者使用了RabbitTemplate把用的手机号和秒杀商品的id拼接起来,放入消息队列中,并且调用convertAndSend方法,只有当消费者拿到了消息队列中的数据,消息队列才会进行下一步。
@RestController public class SeckillController { @Autowired(required = false) private SeckillMapper seckillMapper; //查询所有秒杀商品 @RequestMapping("/seckill/manage/list") public List<Seckill> list(){ return seckillMapper.selectSeckills(); } //根据选择商品返回详情,返回单件商品 @RequestMapping("/seckill/manage/detail") public Seckill detail(Long seckillId){ return seckillMapper.selectOneById(seckillId); } //注入模板对象 @Autowired private RabbitTemplate rabbitTemplate; @RequestMapping("/seckill/manage/{seckillId}") public SysResult startSeckill(@PathVariable Long seckillId){ //模拟每次不同用户访问商品 //如果只允许一个用户秒杀一次 可以使用redis //随机生成一个电话号 String userPhone="1876678"+new Random().nextInt(9999); String msg=userPhone+"/"+seckillId; rabbitTemplate.convertAndSend ("seckillEX", "seckill",msg); //等待声明代码配置完毕再验证功能 return SysResult.ok(); } }
4.消费者:
消费者监听着消息队列,当消息队列中有消息的时候,就把消息队列中的信息获取出来,拆出用户号码和商品信息,因为redis是单进程单线程,所以这里就不会产生超卖的问题,在对数据库里的库存进行减少时,我们先去redis中操作,如果在redis中商品数量已经为-1,则说明商品卖完了,不允许在卖,这样就避免了超卖。
@Component public class SeckillConsumer { @Autowired(required = false) private SeckillMapper seckillMapper; //编写消费逻辑的方法 @Autowired private StringRedisTemplate redisTemplate; //监听的队列名字 @RabbitListener(queues="seckillQ") public void consume(String msg){ //msg=电话号码/seckillId /* 1 userPhone seckillId 解析 2 根据seckillId 执行更新库存 update seckill set number=number-1 where seckill_id=#{seckillId} and number>0 and now()>start_time and now()<end_time 返回值 成功1 失败0 3 0 打印谁,秒杀哪个商品失败了 4 1 收集信息,入库成功的数据success *///msg=18768728281/1 Long userPhone=Long.parseLong(msg.split("/")[0]); Long seckillId=Long.parseLong(msg.split("/")[1]); //increment("num_hw", -1),把redis中key为num_hw存储的值+(-1),再存入redis中 Long decr = redisTemplate.opsForValue().increment("num_hw", -1); if(decr<0){ //if进入,说明商品已经被其他的消费端秒杀完了 System.out.println("商品已经秒杀完了"); return; } int result= seckillMapper.decrNumberById(seckillId); if(result==0){ //条件不满足 秒杀失败的 System.out.println("用户:"+userPhone+";秒杀失败"); return; } //成功减库存,用户秒杀也就成功 //insert into success Success suc=new Success(); suc.setSeckillId(seckillId); suc.setUserPhone(userPhone); suc.setCreateTime(new Date()); suc.setState(0); seckillMapper.insertSuccess(suc); } }
5.mapper层:
public interface SeckillMapper {
List<Seckill> selectSeckills();
Seckill selectOneById(Long seckillId);
int decrNumberById(Long seckillId);
void insertSuccess(Success suc);
}
6.mappers映射文件:
<mapper namespace="cn.tedu.seckill.mapper.SeckillMapper"> <select id="selectSeckills" resultType="Seckill"> select * from seckill </select> <select id="selectOneById" resultType="Seckill"> select * from seckill where seckill_id=#{seckillId} </select> <insert id="insertSuccess"> insert into success (seckill_id,user_phone,state,create_time) values (#{seckillId},#{userPhone},#{state},#{createTime}) </insert> <!--减库存--> <update id="decrNumberById"> update seckill set number=number-1 where seckill_id=#{seckillId} and number > 0 and now() > start_time and now() < end_time </update> </mapper>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。