写点什么

从 0 到 1 实现一套聚合支付系统

2020 年 5 月 29 日

从0到1实现一套聚合支付系统

大家好,我是来自盒子科技研发部支付线刘恒,目前主要是负责公司的一个聚合支付系统的研发工作。今天主要是讲一下我们聚合支付系统从 2016 年年初到现在技术演变。


首先我会从那三个方向,第一是聚合支付的介绍,聚合支付在我们公司它承担一个什么样的地位,第二是在我们公司有什么样的使用场景。第三是从公司开始做聚合支付最开始的版本是什么样子的,在这个版本之上,我们做了什么样的优化,然后到了现在发展成什么样的架构。


01 聚合支付的介绍

首先从第一个主题讲什么是聚合支付,聚合支付主要是就是一个将所有的第三方支付,通过借助形式融合在一起,相当于对接一个支付接口,就可以使用各种支付的场景,就比如说各位有可能去那种超市便利店去买东西,贴着一个码,码上有什么微信支付,支付宝支付,还有一个京东 QQ 各种支付。然后我们公司也有一个,就是一个好哒,相当于这个用户就是图上两个男生,然后扫橙色的那个二维码,所以我们公司做了一个好哒立牌。


它主要是针对一个微小商户进行一个收款工具,让商家他那边会有一个好哒商户通,第一个可以实时的收听语音报告,当前用户付款多少钱,第二个就是他可以去实时查看账单,了解当天的营业额。



还有一个产品就是我们公司的一个 pos 机。这个主要是一款生态 pos,它里面不仅继承了我们一个我们这个具备支付系统提供的服务,就比如微信支付宝,它们还集成了一个刷卡的功能,就是磁条卡芯片卡,还有各种支付方式。


这次我们讲的聚合支付,只是涉及到交易流,不涉及到资金流,资金流是其他项目组负责。


02 1.0 系统

好,进入项目背景。


第一个就是工期短,基本上所有的项目都会遇到,天天都在赶工程。



我们是从 2016 年过年前一周,然后忽然被拉入一个群,说是有个项目要做一做,当时老大让一周上线,


第二是业务不熟。不知道聚合支付到底做什么事情,它的支付流程是什么样的?虽然说之前做过支付相关业务,但是每个公司支付业务是完全不一样的。当时做微信支付宝,微信 APP ID 是什么东西还不知道,所以说就在这种情况下开始着手,还有就是一个当时的交易量,当时的交易量是只有前端的一两个产品在使用,每天的交易笔数也很小,就几千笔。


第三个就是人员的缺乏,因为当时就做系统研发,只有我和另外一个新同事。


就在这种背景下,我们就搭建了第一套系统架构,即虚线圈住的,我负责交易前置,同事负责交易网关,当时就直接操作 DB 没有做任何的其他的优化。



当时就做这样一个简单的架构,第一个开发比较快,直接拿需求进行改代码,方便测试以及上线。当时是 2016 年 3 月份 4 月份就上线了,也很快。后来就是在经过了三四个月交易量比较猛增的情况下,就发现这个系统感觉各种瓶颈就出来了。



  • 渠道的隔离,因为当时对接了几个渠道,特别渠道不稳定的话,比如资源不可用、网络问题,导致超时,这样就会把所有渠道交易全部影响,造成级联反应,导致整个服务交易链路不可用,影响比较严重。周末别人都可以在家好好休息,但是我们支付研发不行,每天都是随时关注手机,因为说不定哪个渠道就出问题了,立马要处理。而且系统哪边挂了之后立马要赶紧联系。所以说这个渠道隔离放在第一位首要的。

  • 接口膨胀,特别涉及到某些相似的业务,就比如说那个消费、撤销、退款接口,就每个业务类型都有这几个接口,随着业务的发展,也不好维护,开发每次来个需求都要去考虑,到底是改哪个接口,要不要都改。

  • 动态扩容。因为聚合支付很多交易都是异步的,用户下单时,我们会立即返回就下单成功,或者下单失败,但是这个交易有没有消费成功,我们需要设置定时的任务去查询最终付款结果。

  • 定时调度,它需要定时、定点、定量的去拉取一批订单进行处理,如果拉取的数据太多,内存直接爆了,拉取太少,有很多交易得不到执行。在分布式环境如何充分提升并发的前提下充分使用机器的资源变得越来越紧迫。

  • 配置分散,传统方式是将配置文件存放在每一个节点,每次升级都需要运维手动改。风险较高而且相当不好维护。


03 2.0 系统

在这个前提下,我们开始着手设计 2.0。当初有几个大的方向:


  • 稳定:支付系统的根基

  • 支付体验:用户使用支付功能时感知零延迟

  • 低耦合:模块之间减少依赖,需求变动风险控制在最小范围


在这个过程中也是试了很多种方案,要么程序复杂,你写完的话可能只有自己懂,后续不好维护;要么性能跟不上去。所以我们也尝试了各种方案,最后演变为如下系统架构。



首先将服务划分为三条线,上面绿色的,和中间那个红色的和最下面一条橙色的。绿色的就是我们把交易核心、交易网关独立出来。任务作业和那个查询网关独立部署。这两条业务线通过 MQ 进行解耦,然后我们再独立查询服务,对前端业务仅仅提供一个流水查询功能而已。


业务流程 如下:


业务发起一笔消费,首先进入支付核心初始化流水、风控风险识别、渠道路由、渠道网关报文组装、上送、渠道应答。异步交易发送消息至 MQ 集群,任务作业监听消息,put 缓存,定时任务拉取进行状态查询,业务方通过查询服务查看该笔交易支付状态



前置优化水平方向


  • 接入层:将共性的接口统一。比如说下单,所有的业务,不管微信支付,还有其他的全部归为下单,具体的业务,通过一个 serviceId 参数进行识别

  • 服务层:共性逻辑,也就是核心逻辑全部抽离出来,然后进行统一下沉,作为底层服务,上层业务全部通过 serviceId 配置化实现,这样的话尽量去少改动核心业务逻辑。

  • 缓存层:随着交易量的增长,特别是在第一代的时候,里面很多业务查询都是直接操作 DB 了,对 DB 有很大的性能影响。所以我们就是在 DB 之上将所有消费交易信息全部缓存,后续所有的操作查询和更新全部操作缓存层主要为了提升了服务的性能。



前置优化垂直拆分:


  • 核心交易:负责交易的核心链路,用户感知最明显。比如支付失败,用户立马能知道,立马就会投诉或者打电话给客服,该模块也包含退款等业务。

  • 任务作业:将处理中的交易进行状态同步,和核心交易通过消息解耦

  • 查询服务:仅仅是对公司内部提供一个交易状态的查询功能



任务作业内部查询策略设计为两个队列、一个批处理


  • 内存队列:基于 DelayQueue 设计的延迟队列,通过制定算法策略,就是比如说延迟十秒、间隔五秒,或者是很多银行使用 2 的 N 次方进行查询。

  • 该队列主要是针对单笔交易执行快速状态同步,提升用户体验。

  • 缓存队列:基于我们公司 Codis 缓存集群,结合分布式调度框架 Elastic-Job 设计。主要是针对状态延迟的订单,进行批量状态同步

  • DB 批处理:也是结合 Elastic-Job 设计,主要是提供人工干预的入口,当渠道延迟比较长、或者渠道异常的情况下,执行批量状态同步



分片策略:


  • 任务分片:目的在于把一个任务分散到不同的机器上运行,既可以解决单机计算能力上限的问题,也能降低部分任务失败对整体系统的影响。elastic-job 并不直接提供数据处理的功能,框架只会将分片项分配各个运行中的作业服务器(其实是 Job 实例,部署在一台机器上的多个 Job 实例也能分片),

  • PS:开发者需要自行处理分片项与真实数据的对应关系。框架也预置了一些分片策略:平均分配算法策略,作业名哈希值奇偶数算法策略,轮转分片策略。同时也提供了自定义分片策略的接口。

  • 数据分片:订单号取模存储(zset)


数据结构:


  • 有序集合(zset):按照分片逻辑,将订单号取模,存放至对应的队列中

  • string:交易明细序列化存储


设计思路:


  1. MQ 消费者(作业节点),接收到消息后,将数据存放在缓存

  2. 作业节点根据分片项、score 范围,定时从对应的缓存队列中获取指定数量的订单号

  3. 业务循环处理,根据订单号再去缓存中获取对应的详细信息

  4. 执行查询逻辑


注意事项:


zset 元素数据过期,需要业务自己处理,可以单独建立检测机制,也可以每次执行业务时执行判断,过期则移除,不然集合会越来越大。



  • 渠道隔离:在高并发访问下,系统所依赖的渠道稳定性对系统的影响非常大,依赖有很多不可控的因素,比如网络连接变慢,资源突然繁忙,暂时不可用,我们选在知名的容错开源框架 Hystrix,隔离方案选择 thread。

  • 查询网关:在交易系统中,查询业务量一般时支付业务的 6 倍,甚至更高,这样对查询服务性能就会有更高的要求。减少对核心交易影响,提升稳定性。


通道商户缓存:通道信息(机构号、商户号、密钥等)属于静态信息,在初次使用时存放至分布式缓存系统中(设置有效期,防止僵尸数据),同时增加手动修改的入口,方便人工干预。



  • 千里之堤毁于蚁穴:我们用容错的方式就是让这种蚁穴不要变大,在依赖的服务不可用时,服务调用方应该通过一些技术手段,向上提供有损服务,保证业务柔性可用。

  • 线程池资源隔离:Java 的 Servlet 容器,无论是 Tomcat 还是 Jetty 都是多线程模型,都用 Worker 线程来处理请求。当业务请求打满 Worker 线程的最大值之后,剩余请求会被放到等待队列(或者拒绝掉),如果等待队列也塞满之后,那这台 Web Server 就会拒绝服务。


如果你的服务是 QPS 较高的服务,那基本上这种场景下,你的服务也会跟着被拖垮。假如上游服务也没有设置合理的超时时间,故障就会扩散。这种故障逐级放大的过程,就是服务雪崩效应。


我们采用容错框架 Hystrix 来解决此问题。 通过 Hystrix 命令模式,将每个类型的业务请求封装成对应的命令请求。每个命令请求对应一个线程池,创建好的线程池是被放入到 ConcurrentHashMap 中。


注意: 尽管线程池提供了线程隔离,也必须要有超时设置,不能无限制的阻塞以致于线程池一直饱和。



Hystrix 线程监控


实时展示当前各个业务线程池资源使用情况,研发人员可以以此为参考,确定资源是否够用、是否需要升级机器资源等。



2.0 之后我们是全面对接我们公司监控平台,主要从以下几点进行监控:


  • 节点耗时监控:如说哪个时间点、哪个节点耗时比较慢,通过百分比的形式,可以比较直观的看出问题。

  • 成功率的监控:折线图定时刷新数据,将各个时间点的交易记录数、成功笔数、失败笔数进行汇总计算,渠道接口异常时可以第一时间发出告警

  • 应答码监控:应答码 TOP 排行榜,方便研发分析数据,提前将问题通知给渠道,减少后续可能出现更大的问题;部分应答码重点监控,通过设定告警阀值,超过阀值短信及电话告警,研发第一时间接入处理,减少可能造成的损失。

  • 邮件巡检报告:用于第二天研发进行数据分析。


以上就是盒子科技聚合支付系统演变的大致过程,在 2017 年的到现在没有出现任何技术故障和业务故障,没有造成一笔长款、短款的出现,系统具备良好的伸缩性,能够保证公司近两年业务的快速发展。


04 下一步需要做什么

那么在系统稳定的基础之上,下一步我们还需要做哪些事情呢?


  • 全链路的监控:我们现在链路监控只是从前端到后端有一个请求的跟踪号,但是这个都分散在我们业务日志里面的。所以说我们下一步就准备做一个全链路的监控,就相当于把每一个每笔交易,它具体在哪个时间点在哪个机器上,然后在哪个渠道,然后它状态做的什么变更,做一个完整的记录,通过一个可视化的界面提供出来,方便客服、运营等其他协作部门使用。



  • 智能路由:遇到渠道异常、临时停用渠道等等情况下,需要将用户切换至其他渠道,目前都是人工通过拉取数据手工操作的,所以下一步我们就想如何让我们的路由更加智能。

  • 动态分片:主要包括数据分片、任务分片,业务量持续倍数增长的情况,各个环节的分片策略如何做到自动化实现,充分使用各个机器的性能。(本文完)


2020 年 5 月 29 日 15:281269

评论

发布
暂无评论
发现更多内容

有的时候,到达目的地,还不如在旅途中。

金龟换酒

心理学 哲学 活在当下

【基础架构】不同场景下的数据存储技术,你用对了吗?

嘉为蓝鲸

网络 存储 系统 raid 磁盘挂载

产业互联网成区块链与数字货币的分水岭

CECBC区块链专委会

区块链 数字货币 产业互联网

使用amoeba实现mysql读写分离

小Q

Java MySQL 编程 程序员

太牛了,这份神仙级面试笔记把所有Java知识面试题都详解出来了

Java成神之路

Java redis 编程 程序员 面试

大数据管理:构建数据自己的“独门独院”

华为云开发者社区

大数据 数据湖

内存型数据库Redis,是如何实现持久化的?

Zhongger

redis

浅析LR.Net工作流引擎

Philips

敏捷开发 工作流 软件开发流程 开发工具

LeetCode题解:225. 用队列实现栈,两个队列, 压入 - O(n), 弹出 - O(1),JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

深兰科技的征途,AI的赛场与战场

脑极体

同事跳槽阿里,临走甩给一份上千页的Linux源码笔记,真香

周老师

Java 编程 程序员 架构 面试

Docker私有化部署gitlab gitlab-runner

Leon

gitlab 持续集成 runner

快来看看!AQS 和 CountDownLatch 有怎么样的关系?

程序员小航

Java AQS 源码阅读 CountDownLatch JUC

正在走进现实的“飞行汽车”,能否颠覆地面交通?

脑极体

架构师课作业 - 第十二周

Tulane

抽象可能从未停止过

架构师修行之路

系统设计 抽象 抽象思维

DB-Engines 9月数据库排名:ClickHouse一路猛冲,Redis坐稳第七

华章IT

MySQL 数据库 redis Clickhouse

鼓舞人心!主席支持数字经济!央行数字货币研究所为世界制定区块链相关国际标准

CECBC区块链专委会

区块链 金融

又踩Maven的两个坑

xiaoboey

maven Unknown lifecycle phase settings.xml 无效 PowerShell

XSKY全新一代SDS一体机五大场景之存储+灾备

XSKY融合存储

CPU中的程序是怎么运行起来的

良知犹存

cpu

太赞了!华为工程师终于总结出了Linux归纳笔记,提供开放下载

小Q

一个银行客户经理的“变形记”

华为云开发者社区

人工智能 金融科技

云图说 | 一分钟带你扫盲云容器黑话

华为云开发者社区

容器 节点 集群

或许是史上最好的AQS源码分析了,你确定要错过?!

InfoQ_d2212957090d

北京城市副中心将试点法定数字货币

CECBC区块链专委会

数字货币 货币

喷一喷坑爹的面向UI编程

架构师修行之路

Redis 数据同步机制--主从模式

是老郭啊

redis 主从配置 主从同步 redis主从 主从复制

为什么企业自主开发软件时,都会使用统一的模块化框架式开发平台?

Learun

敏捷开发 程序设计 开发工具 软件设计 技术方案

深入Spring Security魔幻山谷-获取认证机制核心原理讲解

朱季谦

spring security

深入浅出java虚拟机

AI乔治

Java 架构 性能优化 JVM JVM原理

从0到1实现一套聚合支付系统-InfoQ