服务级高并发优化
- rabbitmq以及rabbitmq4种交换机模式
- 接口优化
- rabbitmq以及rabbitmq4种交换机模式
- 接口优化
- 思路
- 步骤
- 代码实现
- 1、redis预加载库存
- 2、 开始秒杀,预减库存
- 3.加入消息队列中
- 4.消息发送
- 5.消息出队
- 秒杀方法
- getMiaoshaResult方法
- 注意事项(转自评论区)
rabbitmq以及rabbitmq4种交换机模式
接口优化
rabbitmq以及rabbitmq4种交换机模式
接口优化
思路
主要时间少我们对数据库的访问
步骤
1.我们在系统初始化的时候先把秒杀商品的信息预存储到我们的redis中
2.开始秒杀时,先判断redis缓存中的库存,如果没有,直接返回秒杀失败
3.如果有库存,请求入队,返回排队中
4.请求出队,生成订单,减少库存
5。客户端轮询,检测是否秒杀成功。
代码实现
1、redis预加载库存
通过实现InitialzingBean接口,重写其中afterProperties方法
/**
* 系统初始化
* */
public void afterPropertiesSet() throws Exception {
List<GoodsVo> goodsList = goodsService.listGoodsVo();
if(goodsList == null) {
return;
}
for(GoodsVo goods : goodsList) {
redisService.set(GoodsKey.getMiaoshaGoodsStock, ""+goods.getId(), goods.getStockCount());
localOverMap.put(goods.getId(), false);
}
}
这里使用了一个内存标记,即如果库存没有了,就标记false,不再去访问redis。
2、 开始秒杀,预减库存
//预减库存
long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);//10
if(stock < 0) {
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
//判断是否已经秒杀到了
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order != null) {
return Result.error(CodeMsg.REPEATE_MIAOSHA);
}
3.加入消息队列中
//入队
MiaoshaMessage mm = new MiaoshaMessage();
mm.setUser(user);
mm.setGoodsId(goodsId);
sender.sendMiaoshaMessage(mm);
return Result.success(0);//排队中
4.消息发送
@Autowired
AmqpTemplate amqpTemplate;
public void sendMiaoshaMessage(MiaoshaMessage miaoshaMessage){
String msg = RedisService.beanToString(miaoshaMessage);
log.info("miaosha send msg:" + msg);
amqpTemplate.convertAndSend(MQConfig.MIAOSHA_QUEUE,msg);
}
5.消息出队
@RabbitListener(queues = MQConfig.MIAOSHA_QUEUE)
public void receiveMiaoshaMsg(String miaoshaMessage){
log.info("miaosha receive msg:" + miaoshaMessage);
MiaoshaMessage msg = RedisService.stringToBean(miaoshaMessage, MiaoshaMessage.class);
long goodsId = msg.getGoodsId();
MiaoShaUser miaoShaUser = msg.getMiaoShaUser();
GoodsVo goodsVo = goodsService.getGoodsVoByGoodsId(goodsId);
//判断库存
int stock = goodsVo.getStockCount();
if(stock < 0)
return;
//有库存而且没秒杀过,开始秒杀
miaoshaService.miaosha(miaoShaUser,goodsVo);
}
秒杀方法
@Transactional
public OrderInfo miaosha(MiaoShaUser user, GoodsVo goods) {
//库存减一
boolean success = goodsService.reduceStock(goods);
if(success)
//下订单
return orderService.createOrder(user,goods);
else{
setGoodsOver(goods.getId());
return null;
}
}
getMiaoshaResult方法
public long getMiaoshaResult(long userId, long goodsId) {
MiaoshaOrder order = orderService.selectMiaoshaOrderByUserIdGoodsId(userId, goodsId);
if(order != null){
//秒杀成功
return order.getOrderId();
}else {
boolean isOver = getGoodsOver(goodsId);
if(isOver)
return -1;
else
//继续轮询
return 0;
}
}
用户在秒杀该商品的过程中,在得到秒杀结果之前,会一直进行轮询,直到返回orderId或者-1来告知秒杀成功与失败
该方法中,从数据库中看看能不能查询到秒杀订单信息,有说明秒杀成功,返回订单号;失败了则获取redis中的是否秒杀完的标志,跟前边setGoodsOver()相对应,这里的getGoodsOver()便是对set的值进行获取,如果没有库存了则说明秒杀失败了,否则要继续轮询了(已经秒杀到,但是订单还没有创建完成)
注意事项
预减库存前面那个hashmap是标记内存中当前商品秒杀完了(内存中的库存莫得了),目的是减少redis内存访问。我说的情况是这样:当前一个用户进来,首先他秒杀到了一个,然后他退回去,疯狂点秒杀。。。。。这是业务逻辑处理是:预减库存====》判断是秒杀到了,返回“不能重复秒杀”(这时内存中的库存还是被预减到了小于0,map标记)。 后面用户进来,因为内存标记,返回库存莫得了。 最后秒杀订单只有最先开始那个用户的,而且秒杀商品表实际只减少了一个,但是redis存储的内存库存小于0,其他用户全都无法秒杀到。至始至终这个秒杀商品只减少了一个