对于同一笔业务操做,无论调用多少次,获得的结果都是同样的。javascript
咱们以对接支付宝充值为例,来分析支付回调接口如何设计?java
若是咱们系统中对接过支付宝充值功能的,咱们须要给支付宝提供一个回调接口,支付宝回调信息中会携带(out_trade_no【商户订单号】,trade_no【支付宝交易号】),trade_no在支付宝中是惟一的,out_trade_no在商户系统中是惟一的。mysql
回调接口实现有如下实现方式。sql
过程以下:数据库
1.接收到支付宝支付成功请求安全
上面的过程,对于同一笔订单,若是支付宝同时通知屡次,会出现什么问题?当屡次通知同时到达第2步时候,查询订单都是未处理的,会继续向下执行,最终本地会给用户加两次钱。多线程
此方式适用于单机其,通知按顺序执行的状况,只能用于本身写着玩玩。并发
方式1中因为并发出现了问题,此时咱们使用java中的Lock加锁,来防止并发操做,过程以下:app
1.接收到支付宝支付成功请求框架
分析问题:
使用数据库中悲观锁实现。悲观锁相似于方式二中的Lock,只不过是依靠数据库来实现的。数据中悲观锁使用for update来实现,过程以下:
1.接收到支付宝支付成功请求
select * from t_order where order_id = trade_no for update;
4.判断订单是已处理
重点在于for update,对for update,作一下说明:
方式3能够正常实现咱们须要的效果,能保证接口的幂等性,不过存在一些缺点:
依靠数据库中的乐观锁来实现。
1.接收到支付宝支付成功请求
select * from t_order where order_id = trade_no;
3.判断订单是已处理
update t_order set status = 1 where order_id = trade_no where status = 0; //上面的update操做会返回影响的行数num if(num==1){ //表示更新成功 提交事务; }else{ //表示更新失败 回滚事务; }
注意:
依赖数据库中惟一约束来实现。
咱们能够建立一个表:
CREATE TABLE `t_uq_dipose` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `ref_type` varchar(32) NOT NULL DEFAULT '' COMMENT '关联对象类型', `ref_id` varchar(64) NOT NULL DEFAULT '' COMMENT '关联对象id', PRIMARY KEY (`id`), UNIQUE KEY `uq_1` (`ref_type`,`ref_id`) COMMENT '保证业务惟一性' );
对于任何一个业务,有一个业务类型(ref_type),业务有一个全局惟一的订单号,业务来的时候,先查询t_uq_dipose表中是否存在相关记录,若不存在,继续放行。
过程以下:
1.接收到支付宝支付成功请求
select * from t_uq_dipose where ref_type = '充值订单' and ref_id = trade_no;
3.判断订单是已处理
try{ insert into t_uq_dipose (ref_type,ref_id) values ('充值订单',trade_no); //提交本地事务: }catch(Exception e){ //回滚本地事务; }
说明:
关于消息服务中,消费者如何保证消息处理的幂等性?
1.实现幂等性常见的方式有:悲观锁(for update)、乐观锁、惟一约束
若是你喜欢本文,
请长按二维码,关注 javacode2018
转发至朋友圈,是对我最大的支持。