日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

redis实践的一点思路,关于支付回调

發布時間:2023/12/10 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis实践的一点思路,关于支付回调 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,我是烤鴨:

如果作為第三方支付平臺,需要通知調用方付款成功。但是出現通知失敗的情況,怎么處理。
?? ?支付寶的異步通知,每個訂單的異步通知實行分頻率發送:15s 3m 10m 30m 30m 1h 2h 6h 15h。
?? ?如果沒有收到success,就會一直按上邊的進行通知。

?? ?就上述的情景說一下想到的解決方案,并不一定是有效的,只是一些想法:

?

?? ?1. 定時任務

?? ?最開始想到的是用定時任務來做。通知后,如果沒有收到結果,就會一直掃表。
?? ?掃描狀態是未通知的,下次通知的時間小于當前時間的,如果再通知再未送達到的話,
?? ?更新下次通知時間和通知次數。
?? ?這個做法有一些弊端,如果訂單到達一定數量,一直掃表會對數據庫壓力。
?? ?而且如果按照上面的時間間隔的話,在大量訂單的情況下很難保證精度。

?

?? ?2. 定時任務 + redis實現

?? ?為了避免數據庫的壓力,想到的是用redis來代替。
?? ?當第一次通知失敗的時候,將失敗的訂單標識(+訂單號)存到redis中。
?? ?其中redis中存放的是兩種數據結構,一種是Set集合,訂單號集合。
?? ?另一種String.key-value,key是前綴+訂單號,value是已通知次數。
?? ?還有一種是key是前綴+訂單號,value是下次通知時間。

?? ?簡易代碼如下:
?? ?第一次通知失敗:
??

?//通知失敗if (IDBConstant.RESULT_ERROR.equals(status)) {logger.info("返回結果為error 將訂單id存到redis中 ?orderId===" + orderId);//將訂單號放到redis中String key = IDBConstant.SCYD_NOTIFY_PREFIX_PRE + orderId;//集合的通用key,根據這個key能獲取到需要通知的訂單集合redisClient.hset(IDBConstant.SCYD_NOTIFY_AGAIN_PRE, key, key);//通知次數redisClient.set(IDBConstant.SCYD_NOTIFY_NUM_PRE + orderId, "2");//下一次通知時間,應該跟次數有關,可以寫個枚舉類,將次數和下次的加長時間對應redisClient.set(IDBConstant.SCYD_NOTIFY_TIME_PRE + orderId, System.currentTimeMillis() + 10 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "");}

?定時任務每隔一分鐘獲取redis數據

? // 獲取訂單號Set<String> list = redisClient.hkeys(IDBConstant.SCYD_NOTIFY_AGAIN_PAY);?? ?if (!list.isEmpty()) {list.forEach(item -> {String itemStr = (String) item;String[] strs = itemStr.split("_");String orderId = strs[1];String applyNum = redisClient.hget(IDBConstant.SCYD_NOTIFY_AGAIN_PAY, itemStr);System.out.println(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId);// 訂單發送的時間毫秒值String notifyTime = redisClient.get(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId);//被鎖不等待if (redisClient.tryLock(item, 0L, TimeUnit.SECONDS)) {// 如果通知時間 < 當前時間,發送通知if (Long.valueOf(notifyTime) < System.currentTimeMillis()) {// 通知次數String num = redisClient.get(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId);switch (num) {case "2":// 通知時間 + 10minnotifyTime = Long.valueOf(notifyTime)+ 20 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;case "3":// 通知時間 + 10minnotifyTime = Long.valueOf(notifyTime)+ 20 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;case "4":// 通知時間 + 15minnotifyTime = Long.valueOf(notifyTime)+ 30 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;case "5":// 通知時間 + 1h* DateConstant.SIXTY_MINUTESnotifyTime = Long.valueOf(notifyTime)+ 60 * DateConstant.ONE_THOUSAND * DateConstant.SIXTY_SECONDS + "";doSCYDPayNotifyAgainHandler(item, notifyTime, num ,applyNum);break;default:break;}}}});}

?? ?上面方法中doSCYDPayNotifyAgainHandler()?就是對當前的訂單再次通知。
?? ?如果通知失敗,更新次數和下次通知時間。如果成功就移除。最后別忘記釋放鎖。
?? ?方法如下:

public void doSCYDPayNotifyAgainHandler(String item, String notifyTime, String num,String applyNum) {taskAsyncPool.execute(new Runnable() {@Overridepublic void run() {String[] strs = item.split("_");String orderId = strs[1];logger.info("[通知]" + orderId + ":第" + num + "次任務啟動");//根據orderId ?獲取訂單信息//訂單信息假裝已經獲取到了try {//發送請求//過程1//過程2//獲取結果,成功的話if (IDBConstant.RESULT_SUCCESS.equals(status)) {//清空緩存數據redisClient.delKey(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId);redisClient.delKey(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId);//移除操作成功的redisClient.hdel(IDBConstant.SCYD_NOTIFY_AGAIN_PAY, item);} else {int count = Integer.parseInt(num);if(count==5) {//回調第五次還是失敗,直接返回return;}//如果還是沒有回調,更新回調時間redisClient.set(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId, notifyTime);count += 1;//更新回調次數redisClient.set(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId, count + "");}} catch (Exception e) {logger.error("[回調通知]" + item + ":{}第" + num + "次任務異常:method{}" ,e);redisClient.set(IDBConstant.SCYD_NOTIFY_TIME_PAY + orderId, notifyTime);int count = Integer.parseInt(num);count += 1;//更新回調次數redisClient.set(IDBConstant.SCYD_NOTIFY_NUM_PAY + orderId, count + "");} finally {//解鎖redisClient.unLock(item);}}});}?? ?

? ? 這樣多條線程執行,主線程從redis中獲取待通知訂單集合,另起線程做通知操作。
? ? 每通知一單就是一條線程,延遲性也得到了比較好的解決,上面從數據庫取的結果也可以多線程。
? ? 線程池也是有上限的,無限獲取很可能將內存和cpu耗盡。

? ? 推薦第三種方式。redis+隊列

?

?? ?3. redis+隊列


?? ?將已經獲取到的訂單扔到隊列中,在隊列里執行 doSCYDPayNotifyAgainHandler(String item, String notifyTime, String num,String applyNum)?
?? ?這個方法,延時和cpu問題就能比較好的解決了。
?? ?至于丟失問題,暫時沒考慮過。就目前來說,第二種方式夠用。其他的只是有一些想法,歡迎交流。

總結

以上是生活随笔為你收集整理的redis实践的一点思路,关于支付回调的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。