阅读 745

Redis与RabbitMQ实现秒杀(redis消息队列和mq)

对redis进行配置

redis:
  host: 192.168.203.124
  port: 6379
  # redis数据库索引
  database: 0
  timeout: 10000ms
  lettuce:
    pool:
      max-active: 8

      max-wait: 10000ms

      max-idle: 200

      min-idle: 5复制代码

首先要先定义好自己的RedisTemplate

这里我们配置的是<String,Object>类型的<key,value>

@Configuration
public class RedisConfig {


    @Bean(value = "myRedisTemplate")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //key 序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //value 序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }
}复制代码

RabbitMQ的使用

这里我们选择的是使用Topic模式,首先我们要对RabbitMQ写自己的配置类

@Configuration
public class RabbitMQTopicConfig {
    private static final String QUEUE = "seckillQueue";
    private static final String EXCHANGE = "seckillExchange";

    /**
     * 消息队列
     * @return
     */
    @Bean
    public Queue queue(){
        return new Queue(QUEUE);
    }

    /**
     * 交换机
     * @return
     */
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange(EXCHANGE);
    }
    /**
     *topic模式下的绑定
     * @return
     */
    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(topicExchange()).with("seckill.#");
    }
}复制代码

RabbitMQ使用的是生产者消费者的模式,与之对应的就是发送者和接收者,我们需要建立自己的发送者和接收者。 发送者如下:

public class MQSender {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendKillMessage(String message){
        log.info("发送消息:" + message);
        rabbitTemplate.convertAndSend("seckillExchange","seckill.message",message);
    }
}复制代码

发送者仅仅是简单的通过消息队列发送消息,而接收者在收到消息以后便可以进行相应的业务操作,这便实现了异步的操作。

public class MQReciver {
    @Autowired
    private IGoodsService iGoodsService;

    @Autowired
    @Qualifier("myRedisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderServiceImpl orderService;

    /**
     * 下订单操作
     */
    @RabbitListener(queues = "seckillQueue")
    public void recive(String message){
        seckillMessage seckillMessage1 = new seckillMessage();
        log.info("接收到的消息为: " + message);
        String s = JacksonUtil.toJson(message);
        seckillMessage seckillMessage = JacksonUtil.fromJson(message, seckillMessage1.getClass());
        Long goodsId = seckillMessage.getGoodsId();
        User user = seckillMessage.getUser();

        GoodsVo goodsVo = iGoodsService.listGoods(goodsId);
        if(goodsVo.getStockCount() < 1){
            return;
        }
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
        if(seckillOrder != null){
            return ;
        }
        orderService.sekill(user, goodsVo);
    }
}复制代码

在秒杀系统中下订单的具体应用

秒杀成功的重要标志就是在数据库中生成订单,因此我们要保证订单数据的正确性。

  • [1 ] 在我们系统初始化的时候,我们应该把商品库存数量提前加载到redis当中

/**
 * 系统初始化,把商品库存数量加载到redis
 * @throws Exception
 */
@Override
public void afterPropertiesSet() throws Exception {
    List<GoodsVo> goods = iGoodsService.getGoods();
    if(CollectionUtils.isEmpty(goods)){
        return;
    }
    goods.forEach(goodsVo -> {
        redisTemplate.opsForValue().set("seckillGoods" + goodsVo.getId(), goodsVo.getGoodsStock());
        EmptyStockMap.put(goodsVo.getId(),false);
    }
    );
}复制代码
  • [2 ] 在秒杀开始时,大批量的用户即将访问我们提前加载到redis中的数据,为了缓解redis的压力,我们可以采用内存标记的方法,减少redis的压力。

//用于标记某商品的库存是否已经为0
//再对redis库存进行访问之前,可以先对该Map进行访问
private Map<Long,Boolean> EmptyStockMap = new HashMap<>();复制代码
  • [3] 用户对redis进行访问以后,我们先不用立刻对数据库生成相应的订单,而是应该对redis进行预减库存的操作

Long stock = valueOperations.decrement("seckillGoods" + goodsId);
if(stock < 0){
    //库存不足直接标记为true,就不用再次来访问了
    EmptyStockMap.put(goodsId,true);
    valueOperations.increment("seckillGoods"+goodsId);
    return "not enough";
}复制代码
  • [4]在整个流程完毕以后,我们最后调用RabbitMQ对数据库进行相应的操作,这样就达到了异步的操作

seckillMessage seckillMessage = new seckillMessage(user, goodsId);
mqSender.sendKillMessage(JacksonUtil.toJson(seckillMessage));


作者:SillyWg
链接:https://juejin.cn/post/7039290815369183268


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐