場景描述
在秒殺微服務中,筆者在需要各種校驗前端傳來的引數後,通過 Redis 加鎖限流(切面A)並返回,最後封裝訂單資料推送到 RabbitMQ 訊息佇列(切面B)做善後工作。
問題:如何將 切面 A 的資料傳遞 給切面B 處理呢?
/**
* 新增到秒殺流程
*
* @param killId 秒殺商品快取鍵 sessionId_skuId
* @param key 隨機碼 randomCode
* @param num 數量
* @return {@link R}
*/
@GetMapping("/kill")
public R addToSeckill(
@RequestParam("killId") String killId,
@RequestParam("key") String key,
@RequestParam("num") Integer num) {
// 實現類只是帶有兩個註解方法,返回 null(因為全部交給切面託管了)
String orderSn = seckillService.kill(killId, key, num);
if (StringUtils.isEmpty(orderSn)) {
return R.error();
}
return R.ok().setData(orderSn);
}
解決方案
通過引數傳遞資料,通過捕獲異常保證業務邏輯(離譜但有用) ?
// 強制修改引數,通過異常返回正常流程,而通過AOP訊息佇列處理收尾動作
try {
return pjp.proceed(new Object[]{orderTo, null, null});
} catch (Throwable e) {
return orderSn;
}
注意事項:
-
引數一致性:必須偽造和方法簽名的數量相等的引數 ⇒ 否則執行緒會丟擲異常 I 就返回了,無法執行
pjp.proceed
原始方法 ⇒ 無法跳轉第二個切面
java.lang.IllegalArgumentException: Expecting 3 arguments to proceed, but was passed 1 arguments
-
捕獲異常不丟擲,直接執行正常業務邏輯 ⇒ 否則執行緒將吞沒異常 II
cn.miozus.gulimall.common.to.mq.SeckillOrderTo cannot be cast to java.lang.String
3.雖然兩個切面都返回了 orderSn
,實際最終只有切面A傳遞到了控制層和前端, 切面B的返回值成了擺設。
跳轉過程
打斷點檢視兩個切面的跳轉過程。
切面A:準備跳轉第二個切面
切面B:傳送訊息完成
列印日誌,可見場景需求,已經滿足了。
2022-03-29 17:32:56.521 INFO 7904 --- [io-25000-exec-8] c.m.g.s.aspect.SeckillRabbitMqAspect : 快速建立訂單:傳送訊息建立完成: 202203291732444881508738921192005634
2022-03-29 17:33:01.526 INFO 7904 --- [io-25000-exec-8] c.m.g.s.controller.SeckillController : 秒殺建立訂單用時:28778
? seckill orderSn = 202203291732444881508738921192005634
2022-03-29 17:33:01.527 INFO 7904 --- [nectionFactory5] c.m.g.s.config.RabbitMqSeckillConfig : ? 訊息已傳送, params: correlationData:null,ack:true,cause:null
其他方案
最簡單的辦法,不切了,兩個切面耦合在一起。注入和呼叫方法。