网站怎么注销主体,网页制作培训教程,淘客手机网站源码,discuz!网站模板大家好#xff0c;我是烤鸭:
如果作为第三方支付平台#xff0c;需要通知调用方付款成功。但是出现通知失败的情况#xff0c;怎么处理。 支付宝的异步通知#xff0c;每个订单的异步通知实行分频率发送:15s 3m 10m 30m 30m 1h 2h 6h 15h。 如果没有收到success我是烤鸭:
如果作为第三方支付平台需要通知调用方付款成功。但是出现通知失败的情况怎么处理。 支付宝的异步通知每个订单的异步通知实行分频率发送: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数据 // 获取订单号SetString 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(count5) {//回调第五次还是失败直接返回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问题就能比较好的解决了。 至于丢失问题暂时没考虑过。就目前来说第二种方式够用。其他的只是有一些想法欢迎交流。