Redis+activeMQ实现秒杀demo

Redis+activeMQ实现秒杀demo

GitEE地址:https://gitee.com/jminzhou/study-project

1. 思路

  1. 先将要被秒杀的商品存到redis中,可以使用aspectJ,可以编写一个service去实现,使用商户管理去调用借口,也可以使用监听器的方式,反正最主要的思路就是把信息存到redis中。
  2. 将当前登录的用户信息也存到redis中,因为商品是直接下单成功的(不会判断用户的金额是否足够,所以只需要一个用户的唯一标识即可)。
  3. 将订单信息存到mq中,然后使用mq去转存redis中,判断库存是否足够。

2. 实现(只有一少部分核心demo代码)

2.1 aop

将商品信息存到redis中

/**
 * @date 2020/10/28 14:27
 */
@Aspect
@Component
public class MyOrderAspectJ {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ProductService productService;


    /**
     * 设置商品getOne切点
     */
    @Pointcut("execution(* com.redis.seckill.controller.ProductController.get One(..))")
    public void productInfo(){}

    /**
     * 开启秒杀之前将商品信息存到redis中。
     */
    @AfterReturning(pointcut = "productInfo()")
    public void saveProduct(JoinPoint joinPoint){
//        获取拦截方法的参数列表,这里的参数只有一个商品id
        Object[] args = joinPoint.getArgs();

//        得到商品id
        Integer productId = (Integer) args[0];

//        判断是否有该商品的key
        if (!redisUtil.hasKey("productInfo"+productId)){
//        将商品信息存到redis中。
            redisUtil.set("productInfo"+productId,
                    JSON.toJSON(productService.getOne(new QueryWrapper<Product>().eq("product_id",productId))));
        }

    }
}

2.2 MQ

发送者

/**
 * 发送者
 * @date 2020/10/29 9:08
 */
@Service
@Log4j2
public class Sender {


    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    /**
     * 如果用户抢到商品就发送到队列中去
     * 信息包含用户id和商品id
     * @param userId
     * @param productId
     */
    public void sendDirectQueue(Integer userId,Integer productId,Integer quantity){
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>秒杀请求发送,商品id:"+productId+"用户id"+userId);
        try {
            Map<String,Integer> map = new HashMap<>();
            map.put("userId",userId);
            map.put("productId",productId);
            map.put("quantity",quantity);
            jmsMessagingTemplate.convertAndSend(ActiveMQConfig.QUEUE_NAME,map);
        }catch (Exception e){
            e.printStackTrace();
            log.error("【队列发送异常】{}",e.getMessage());
        }
    }
}

接收者

我这里再进行秒杀完毕之后直接存到数据库了。我也不知道对不对😭。

我觉得也可以先存到redis中,等到服务器的峰值没有那么大的时候,再转存到数据库中。

/**
 * 消息接收者
 * @date 2020/10/29 9:15
 */
@Log4j2
@Service
public class Receiver {


    @Autowired
    private RedisUtil redisUtil;


    @Autowired
    private MyOrderService myOrderService;

    @Autowired
    private ProductService productService;


    @JmsListener(destination = ActiveMQConfig.QUEUE_NAME)
    public void receive(MapMessage mapMessage){
        try {
//            根据接收的map中的productId得到商品的id
            String productId = mapMessage.getString("productId");
            String userId = mapMessage.getString("userId");
            String quantityStr = mapMessage.getString("quantity");
//            从redis中读取该商品的信息
            String jsonProduct = JSONObject.toJSONString(redisUtil.get("productInfo" + productId));
            Product product = JSONObject.parseObject(jsonProduct, Product.class);

//            判断想要购买的数量是否大于商品剩余的数量
            Integer quantity = Integer.valueOf(quantityStr);
            int num = product.getProductResidue()-quantity;
            if (num < 0){
                log.error("【商品库存不足】");
                throw new RuntimeException("【商品库存不足】");
            }

//            剩下肯定就是数量小于库存了

//            更新redis中商品信息
            product.setProductResidue(product.getProductResidue()-quantity);
            redisUtil.set("productInfo" + productId, JSON.toJSON(product));

//            也可以先写入到redis中,等服务器峰值过去再写入到数据库中。

//           生成订单存到数据库中
            MyOrder myOrder = new MyOrder();
            myOrder.setProductOrder(Integer.valueOf(productId));
            myOrder.setUserOrder(Integer.valueOf(userId));
            myOrder.setProductQuantity(quantity);
            myOrder.setOrderCount(product.getProductPrice().multiply(new BigDecimal(quantity)));
            myOrderService.save(myOrder);

//            更新商品表
            productService.updateById(product);


        }catch (Exception e){
            log.error("【队列接收异常】{}",e.getMessage());
        }
    }
}

2.3 秒杀service

@Transactional
@Service
@Log4j2
public class MyOrderServiceImpl extends ServiceImpl<MyOrderMapper, MyOrder> implements MyOrderService {


    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private Sender sender;

    @Override
    public boolean seckill(Integer userId, Integer productId, Integer quantity) {

        boolean result = true;
//        获取redis中商品的库存容量
        String jsonProduct = JSONObject.toJSONString(redisUtil.get("productInfo" + productId));
        Product product = JSONObject.parseObject(jsonProduct, Product.class);

        if (product.getProductResidue()<=0){
            result = false;
            log.error("【秒杀下单service】,库存不足");
        }
        sender.sendDirectQueue(userId,productId,quantity);
        return result;
    }
}

3. 食用方法

  1. 先访问http://localhost:8080/product/getOne/{商品id}端口,将想进行秒杀的商品存到redis中。
  2. 虚假的登录(其实就是将自己的用户信息存到redis中):http://localhost:8080/account/login ,这里直接没传参数,在AccountController内部直接给定了用户id的值为1。
  3. 执行秒杀:http://localhost:8080/order/create ,这里也是没传参数,MyOrderController,给定了值。

强烈建议先clone在食用。

gitEE:https://gitee.com/jminzhou/study-project

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值