鄭州七彩網(wǎng)站建設(shè)公司怎么樣常熟網(wǎng)絡(luò)推廣
SpringCloud 大型系列課程正在制作中,歡迎大家關(guān)注與提意見。
程序員每天的CV 與 板磚,也要知其所以然,本系列課程可以幫助初學(xué)者學(xué)習(xí) SpringBooot 項目開發(fā) 與 SpringCloud 微服務(wù)系列項目開發(fā)
1 項目準(zhǔn)備
- SpringBoot 整合 RabbitMQ 消息隊列【SpringBoot系列11】本文章 基于這個項目來開發(fā)
本文章是系列文章 ,每節(jié)文章都有對應(yīng)的代碼,每節(jié)的源碼都是在上一節(jié)的基礎(chǔ)上配置而來,對應(yīng)的視頻講解課程正在火速錄制中。
訂單系統(tǒng),用戶下單,即要保存即時性,也要保證流暢性,同時還要防止超賣,本文章是基于 RabbitMQ 消息隊列 + Redis 實現(xiàn)的下單,當(dāng)然后續(xù)還會的秒殺系統(tǒng)設(shè)計 以及后續(xù)的微服務(wù)以及熔斷控制等等
如這里 我的商品 庫存有 10 個
然后我使用 apache-jmeter-5.5 壓測,200個用戶1秒內(nèi)請求完成,每個用戶請求2次,也就是1秒有400次下單請求
測試完成后,商品庫存為0,然后訂單生成10個,完美解決并發(fā)問題
這是實現(xiàn)的普通訂單,基本實現(xiàn)邏輯是
1、redis 校驗庫存,預(yù)下單
2、消息隊列減庫存 生成 訂單 (數(shù)據(jù)庫、redis、es)
3、用戶查詢到訂單成功,發(fā)起支付
4、支付回調(diào) 修改訂單數(shù)據(jù) (數(shù)據(jù)庫、redis 、es)
1 預(yù)下單接口
@Api(tags="訂單模塊")
@RestController()
@RequestMapping("/orders")
@Slf4j
public class OrderController {@Autowiredprivate OrderService orderService;/*** 下單* @param goodsId 商品ID* @param userId* @return*/@GetMapping("/create/{id}")public R createOrder(@PathVariable("id") Long goodsId,@RequestHeader Long userId) {return orderService.createPreOrder(goodsId,userId);}
}
@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate OrderMQSender mqSender;@Overridepublic R createPreOrder(Long goodsId, Long userId) {log.info("預(yù)下單處理 userId:{} goodsId:{} ",userId,goodsId);//獲取redis中的商品庫存 先判斷商品是否有庫存Boolean aBoolean = redisTemplate.hasKey("goodStock:" + goodsId);if(Boolean.FALSE.equals(aBoolean)){return R.error("下單失敗 商品庫存不足");}//獲取商品庫存int goodsStock = Integer.valueOf(redisTemplate.opsForValue().get("goodStock:" +goodsId).toString());if(goodsStock==0){return R.error("下單失敗 商品庫存不足");}//發(fā)送下單消息SecKillMessage message = new SecKillMessage(userId, goodsId);mqSender.sendCommonOrderMessage(JsonUtils.toJson(message));return R.okData("預(yù)下單成功");}
redisTemplate 的 hasKey 可以直接判斷key是否存在,在這里如果商品的key不存在,則商品無庫存,redis 的商品庫存是在服務(wù)啟動后,自動同步進(jìn)入的
@Service
@Slf4j
public class OrderServiceImpl implements OrderService , InitializingBean {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate GoodsService goodsService;/*** 初始化秒殺商品數(shù)量到 redis 中** @return*/@Overridepublic R startSeckillInit() {List<SeckillGoods> goods = secKillGoodsService.findAllSecKillGoods();if (CollectionUtils.isEmpty(goods)) {return R.error("無秒殺商品");}goods.forEach(g -> {log.info("初始化秒殺商品 goodsId:{} stock: {}", g.getGoodsId(), g.getStockCount());redisTemplate.opsForValue().set("goodStock:" + g.getGoodsId(), g.getStockCount());});return R.ok("初始化完成");}@Overridepublic void afterPropertiesSet() throws Exception {this.startSeckillInit();}
InitializingBean 當(dāng)一個類實現(xiàn)這個接口之后,Spring啟動后,初始化Bean時,若該Bean實現(xiàn)InitialzingBean接口,會自動調(diào)用afterPropertiesSet()方法,完成一些用戶自定義的初始化操作。
2 消息隊列的定義
在這里單獨定義普通下單使用的隊列與交換機(jī)
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class OrderRabbitMQTopicConfig {private static final String commonOrderQueue = "commonOrderQueue";private static final String commonExchange = "commonOrderExchange";@Beanpublic Queue commonOrderQueue() {return new Queue(commonOrderQueue);}@Beanpublic TopicExchange commonExchange() {return new TopicExchange(commonExchange);}@Beanpublic Binding commonOrderBinding() {return BindingBuilder.bind(commonOrderQueue()).to(commonExchange()).with("commonOrder.#");}
}
然后就是訂單的發(fā)送者
@Service
@Slf4j
public class OrderMQSender {@Autowiredprivate RabbitTemplate rabbitTemplate;/*** 普通訂單走的隊列* @param msg*/public void sendCommonOrderMessage(String msg) {log.info("預(yù)下單發(fā)送消息:{}", msg);rabbitTemplate.convertAndSend("commonOrderExchange", "commonOrder.message", msg);}
}
然后定義普通訂單的消息接收者
@Service
@Slf4j
public class OrderMQReceiver {@Autowiredprivate OrderService orderService;@RabbitListener(queues = "commonOrderQueue")public void receiveCommonOrderMessage(String message) {log.info("接收的秒殺訂單消息:{}", message);SecKillMessage secKillMessage = JsonUtils.toObj(message, SecKillMessage.class);Long userId = secKillMessage.getUserId();Long goodsId = secKillMessage.getGoodsId();//普通下單orderService.createOrder(goodsId, userId);}}
普通下單里,就是減庫存,生成訂單的過程
@Override@Transactionalpublic R createOrder(Long goodsId, Long userId) {log.info("下單處理 userId:{} goodsId:{} ",userId,goodsId);//查詢商品詳情Goods goods = goodsService.findGoods(goodsId);//商品的實際庫存if (goods.getGoodsStock() < 1) {// 設(shè)置該商品庫存為空redisTemplate.opsForValue().set("goodStock:" + goods.getId(), "0");log.info("庫存不足 下單失敗");return R.error("商品庫存不足");}//減庫存 int currentStock = goods.getGoodsStock() -1;//更新數(shù)據(jù)庫 庫存goods.setGoodsStock(currentStock);int update = goodsService.updateGoodsStock(goods);if(update<=0){log.info("更新庫存失敗 下單失敗");return R.error("商品庫存不足");}//更新redis 緩存redisTemplate.opsForValue().set("goodStock:" + goods.getId(), currentStock);// 下訂單Order order = new Order();order.setUserId(userId);order.setGoodsId(goodsId);order.setDeliveryAddrId(0L);order.setGoodsName(goods.getGoodsName());order.setGoodsCount(1);order.setGoodsPrice(goods.getGoodsPrice());order.setOrderChannel(1);order.setStatus(0); // 訂單創(chuàng)建中order.setCreateDate(new Date());orderMapper.insert(order);log.info("下單成功 userId:{} goodsId:{} orderId:{}",userId,goodsId,order.getId());//緩存普通訂單redisTemplate.opsForValue().set("order:" +userId + ":" + goodsId, order);//保存數(shù)據(jù)到ES中//后續(xù)實現(xiàn)return R.okData(order);}
本文章是系列文章 ,每節(jié)文章都有對應(yīng)的代碼,每節(jié)的源碼都是在上一節(jié)的基礎(chǔ)上配置而來,對應(yīng)的視頻講解課程正在火速錄制中。
本文章只有核心代碼,全部代碼請查看對應(yīng)源碼
項目源碼在這里 :https://gitee.com/android.long/spring-boot-study/tree/master/biglead-api-10-seckill
有興趣可以關(guān)注一下公眾號:biglead
- 創(chuàng)建SpringBoot基礎(chǔ)項目
- SpringBoot項目集成mybatis
- SpringBoot 集成 Druid 數(shù)據(jù)源【SpringBoot系列3】
- SpringBoot MyBatis 實現(xiàn)分頁查詢數(shù)據(jù)【SpringBoot系列4】
- SpringBoot MyBatis-Plus 集成 【SpringBoot系列5】
- SpringBoot mybatis-plus-generator 代碼生成器 【SpringBoot系列6】
- SpringBoot MyBatis-Plus 分頁查詢 【SpringBoot系列7】
- SpringBoot 集成Redis緩存 以及實現(xiàn)基本的數(shù)據(jù)緩存【SpringBoot系列8】
- SpringBoot 整合 Spring Security 實現(xiàn)安全認(rèn)證【SpringBoot系列9】
- SpringBoot Security認(rèn)證 Redis緩存用戶信息【SpringBoot系列10】
- SpringBoot 整合 RabbitMQ 消息隊列【SpringBoot系列11】