前言
说起支付平台,支付宝量级的支付平台和一个小型公司的支付不可同日耳语。一个初创或刚创业一两年的公司,一没人力,二没财力的情况下,如果也想对接支付那怎么办呢?
感谢支付宝和微信支付,两大行业巨头提供了简单易用的方案,简化了对接流程,又能支持大部分银行。今天我们就来根据不同业务规模,设计一个能经受业务考验的支付平台。
第一阶段
举个例子,阿力空闲时间,接了个外包的分销系统。业务模型如:成为会员,可以自动带二维码的分销海报,扫描你二维码的人成为会员后,你获得提成。
这个例子有几个核心步骤:
申请会员,支付成为会员,自动生成海报,
计算分销提成。
有点小挑战的自动生成海报。这个可以参考微信参数二维码接口和GDI+绘制图片来搞定,利用html5的canvas也能搞定。
最核心的部分,当然是支付。
先来一张订单表流程图压压场面。
订单模型
前些天看领域驱动提到了核心域和子域,那么整个交易流程是是这个模型的核心域,订单表是交易流程的子域。
我大概说下这些字段,业务类型和业务id以及业务处理url实现了各个业务的解藕,各个业务线都有自己的限界上下文。它可以根据取消日期和取消地址完成订单的取消动作,可以根据支付平台交易id和支付平台查询对账。
业务通知状态是用来综述通知业务处理是否成功。说完了订单,让我们来看下整体交易流程。
交易流程
订单有三个主流程,提交订单是用户主动触发,支付回调是属于支付平台触发,定时取消是后台任务根据设定的取消时间自动运行,小业务可以不考虑订单取消问题。
这样来说,第一版支付中心就完成了。由于刚上线,流量每天很少,平稳了运行一段时间后,也许会出现支付平台支付,但搭建的支付中心却未支付,只能手动修改数据库了,并触发业务回调了,这在最终一致性里,可以成为人工补偿。
后来不厌其烦,加了个支付日志,记录任何与支付平台交互的信息,然后每隔一段时间扫描最近变更的日志表,并和订单表对比,发现不匹配的,修复为已支付,完美的解决了这个问题,这在最终一致性里,可概括为定时补偿。
交易日志表
老板缺少人手,业务量又上升,又对阿力解决问题的能力很欣赏,就直接把阿力工资翻倍从原公司挖了过来。(故事纯属虚构)
第二阶段
刚过来新公司不久,就接到了一笔融资,然后新公司扩招了很多同事,市场销售人一多,产品线更多,线上支付流量也加快起来。阿力信心满满,觉得很有干劲。
得意不久,就遇到了服务线反馈的问题:有客户重复支付,需要退款。于是改订单,清理数据,财务退款,临时解决了问题。后来次数多了,手工处理及易出错,就查询支付宝和微信的自动退款接口,然后依赖日志表记录过支付成功对比判定重复支付,发起退款,引入了自动退款流程。
交易流程补充自动退款流程
然后又接到了一个线上客户需要抢购的需求,每月有一天集中一起抢,类似小米秒杀那样。然后到了激动的那天,系统撑过了三分钟,华丽丽的挂了!熬了二十分钟才恢复正常。
痛定思通,支付中心进入重构优化阶段。由于公司人员扩张,有时间和精力和能力去重购优化更健康的业务架构。
一,引入消息队列Rabbitmq支撑流量削峰。如支付回调先进消息队列,由消息队列去通知业务。大幅度缩短单次请求处理时间,提升兵法能力。
二,全面引入Redis缓存,减小数据库访问压力,部分关键业务表启用HttpRuntime缓存,性能指数级提升。
三,引入专业调度工具quartz.net或hangfire。可以用来处理定时查询订单交易问题,及退费问题。
四,购买商业.net监控平台,如听云。检测程序性能。
阿力跟随新公司技术体系,也对支付中心实现了升级。
支付平台回调通知后,先转发到消息队列,由消息队列来通知业务处理,如失败后延时转发到消息队列继续执行,最高重试5次,然后发短信或邮件通知责任人。
针对之前线上支付平台和自建平台不一致问题,利用hangfire调度机制定时每天晚上拉取一周数据和支付平台核对。确保了两个异构系统的一致性。
为防止支付平台同时通知,造成两条支付日志,先更新订单成功后,在队列里,用redis的incr和decr原子性操作,来确保只能同时操作一个订单,另一个通知延迟处理。
数据库开启读写分离,部署集群。
经过阿力和同事们两个月的协力合作与加班加点,新系统终于在那个客户第三次线上抢购前一段时间上线。经过线上抢购验证,新的系统轻轻松松的抗过了抢购,大家一片欢声笑语。阿力看到十几分钟XX百万的交易额惊呆了!,这真是金牌客户啊!
到了年底,微信红包火热起来,许多客户申请开通微信红包,有家客户粉丝有二十多万,发的钱也特别多。当时一到点,十万人齐刷刷摇手机抢红包。
最后,重启了几遍应用程序池也不顶用。针对如此的流量,我们应该怎么办呢?每秒万级的请求暂时就不是小公司处理的来的,况且这流量就过年才有,像级了春运。人有那么多,抢到红包的人是有限的。
百分之九十五的人都是无效流量。那就取巧吧,随机抽取一部分人的数据进入服务器,其他的人就本地留存吧,通过这种思路减少了一大部分流量。
只考虑第一,第二阶段的话,上面关于支付中心的思考架构是完全可以满足交易量的。况且又有多少公司能迈向独角兽之路呢?
念天地之悠悠,独怆然而泪下!
第三阶段
上面那种方式虽然取巧,针对特定业务,本来就是抢红包,大部分人都是无效的,能说的过去,假如是主业务流程有万级每秒甚至百万千万级每秒的请求量应该怎么办呢?阿力陷入了迷茫。
听说过docker,kuberneters为代表的容器编排,听说过CI/CD自动部署,听说过微服务的强大,听说过负载均衡,仿佛都是方向。
大海跨不过陆地,台风却能轻易穿梭,大化为小,繁化为简,聚简成面,规模化微服务也许才是解决巨量请求之道!(故事纯属虚构,不要代号入座)
附录:最终一致性
说完了解决中小型流量的问题,我们来了解下一致性问题。
1、关系型数据库事务追求ACID:
A: Atomicity,原子性
C: Consistency,一致性
I: Isolation,隔离性
D: Durability,持久性
2、CAP(帽子理论)
C:Consistency,一致性, 数据一致更新,所有数据变动都是同步的
A:Availability,可用性, 好的响应性能,完全的可用性指的是在任何故障模型下,服务都会在有限的时间处理响应
P:Partition tolerance,分区容错性,可靠性
帽子理论证明,任何分布式系统只可同时满足二点,没法三者兼顾
3、Base模型:
BA:Basically Available,基本可用
S:Soft State,软状态,状态可以有一段时间不同步
E:Eventually Consistent,最终一致,最终数据是一致的就可以了,而不是时时保持强一致
利用查询模式,补偿模式,异步确保模式,定时校对模式等可实现分布式系统最终一致性。
最终一致性更详细用法参考李艳鹏老师关于分布式一致性的讲解。
https://www.jianshu.com/p/1156151e20c8?from=singlemessage&isappinstalled=0
后言
阿力解决了支付中心的稳定问题后,就买了许多书,看到了上面关于最终一致性的陈述时,心里想到,这些都是我已经实现了的,原来还有这么多头头道道??
阿力又看到了领域驱动设计等书,感慨:支付领域模型真是学习领域驱动设计的最好实践。它具有独立的限界上下文,通过回调url和其他业务限界上下文沟通。
最后再用交易流程在做个总结吧!
交易流程
关键点:
1.回调部分,有消息队列通知,并支持失败重试。
2.每天晚上定时拉取支付平台对象记录核账,保证最终一致性。
3.支付平台回调时,根据支付日志判定是否重复支付,重复支付的发起自动退款。
源码
计划用.NET Core 按领域驱动的方式,完成以上设计。日期未定。