从无到有,支付路由系统升级打怪之路|原创
支付路由系统
提到路由,不免会想到网络通信过程中起到数据包转发的路由器。
而我们今天讲到支付路由系统,也是起到类似的作用。路由系统本身并不处理具体业务,它的作用就是将支付请求转发底层支付通道。
如上图所示,支付系统接入层,接收到支付请求之后,经过内部运算,最后将会通过路由系统转发给具体底层的支付通道。
另外,除了路由转发功能以外,路由器一般还会有一些额外的功能,比如防火墙等。
那我们其实也可以在支付路由系统加入额外功能,比如实时计算底层支付渠道的成功率,若低于一定的阈值,进行报警并且将该渠道下线。
这里需要说明一点,这里的路由系统可以是一个应用中子模块,也可以是一个单独子系统。
为什么需要路由系统
看到这里,可能会有一些小伙伴会思考,一定需要这个路由系统吗?直接将请求发给支付通道不好吗?
答案当然是可以的,如果当前只对接「一两个支付渠道」,这么做没问题,并且也推荐这么做。
这个阶段由于业务量不大,支付系统可能只是一个单体应用,或者也可能是其他应用内一个子模块而已。
那这个时候,业务很简单,系统也很简单,那我们不需要额外的路由系统,增加系统复杂度。
不过这样的系统弊端也很明显,如果后期再新增一个支付通道,我们需要再开发对接。
另外由于所有实现都在一个系统,假设系统应用发生问题,那么就是大家一起「死」,这其实也是单体系统最大弊端。
所以如果底层对接支付通道很多,像一般第三支付公司的支付系统,同一家银行的可能会对接很多支付通道,比对银联无跳转支付,网联支付,也有可能是 XX 行自己提供的接口。
那么这种情况就非常需要单独维护一个路由系统。
PS:630 政策之前,支付公司对接支付通道那真是一个多,同一家银行,可能会对接四五个通道。
但是 630政策之后, 不允许支付机构直接对接银行。
所以现在支付机构对接通道可能会比之前少很多。
实现方式
路由系统实现方式有很多,下面主要分享一下我所经历过实现方案。
我们的路由系统经历过三个阶段的迭代,才有了现在的实现方案。
第一个阶段-混沌初开
这个阶段就跟上面讲的场景一样,业务需求较简单,仅仅只需要对接一两个支付通道。
为了快速上线,设计方案就简单粗暴,所有业务都处于一个应用。
支付子模块对内暴露暴露支付服务接口,由业务系统发起直接调用。
系统设计图如下:
这个阶段由于只有一个支付渠道,所以也不需要有路由系统,直接由业务系统调用支付服务接口发起支付。
不过随着业务量增大之后,这个设计方案就暴露很多问题:
业务系统与支付系统位于同一个系统,系统任何一次变更都会影响整个系统。
扩展性问题。每接入一个新的支付通道如微信,就需要增加一个新的实现类。另外,业务系统的代码同时也需要改动,需要调用新的实现类。
针对以上问题,我们进行第一版的改造。
首先我们将整个支付模块从原来应用中拆分出来,成为一个独立的子系统,专门运行支付业务。
这个系统对外提供一组支付接口,业务系统只需要调用这个接口,传入必要的参数,无需关心支付系统到底是如何实现的。
如果业务系统想指定某个支付通道,比如支付宝,那么可以在接口传入这个渠道标识,支付系统将会根据这个渠道标识调用相应的支付通道。
其次梳理渠道接口文档,抽象出共性接口,每个支付通道实现都需要继承这个接口。
这组通用渠道接口,其中 「channelName」 方法,代表这个实现类具体代表哪个通道。比如说这个方法返回 「aliPay」,那么就代表这个实现类将会调用支付宝通道。
支付系统子应用中将会维护一个类似路由表,这里简单使用 Map
存储映射关系,「key」 为上文提到的渠道唯一应用标识,而 「value」 为具体的实现类。
应用初始化之后,将会调用 「Spring ApplicationContext getBeansOfType」 方法,获取同一个接口的所有实现类 ,最后将其放入 Map 缓存中。
每次支付调用都会根据渠道唯一标识从路由表获取具体实现类,然后由具体的子类实现支付逻辑。
学过设计模式的同学,这里应该不会陌生,这其实是使用设计模式中的策略模式。
这个阶段,由于业务还不是很复杂,系统还是挺简单,路由系统还只是系统中的一个子模块。
第二个阶段-神功初成
经历过一段时间,公司的业务量变的越来越大,这个阶段我们开始追求系统的稳定性。
但是在第一阶段设计方案中,支付系统所有模块位于同一工程。有些模块可能需要频繁发布,这样一旦发布就会影响所有系统功能。
第二点,系统功能全都耦合在一起,团队开发也变的困难,分支冲突,代码丢失也是经常的事。
第三点,一旦某些改动发布发生问题,整个系统都受到影响,真的是「要死一起死」。
所以这个阶段针对以上的问题,我们进行了相应改造,开始将支付系统进行拆分。
首先按照功能,将支付系统拆分几个独立的子系统,路由系统,渠道系统,成为独立系统,独立部署维护。
每个支付通道单独维护部署,成为一个单独的子应用。
系统之间的调用关系,就从同一进程内调用,变成使用 「RPC」 进行跨进程调用。
这个时候就会有个问题,渠道系统可能会因为发布而下线/上线,这时路由系统必须动态维护这种关系,在渠道系统某一节点下线时,自动删除调用关系,而当应用上线时,新增调用关系。
「说白了,路由系统需要实现渠道服务动态发现。」
看到这里不要怕,其实 Dubbo 框架已经自带这个功能,我们没必要自己再去实现了。
Dubbo 配置中有一个属性-「group」,这个属性可以用于服务分组。
当同一个接口有多个实现,我们就可以根据这个来区分不同渠道系统的实现。
因为渠道系统实现同一组接口之后,提供出 Dubbo 服务需要加上相应的 group 属性,值为相应的渠道唯一标识。
如下所示:
路由系统只要引入这个 Dubbo 服务,设置相应的 「group」 属性 ,路由系统引用渠道系统的服务:
此时路由系统就跟第一阶段一样,内部维护一个路由表就好了。
这里采用了 XML 配置存储渠道标识与 Dubbo 引用服务的映射关系,如下所示:
服务启动之后解析这个 XML 文件,然后将其维护在 Map 中。每次支付调用都会根据渠道唯一标识从路由表获取服务名,然后借助 「Spring ApplicationContext#getBean」 获取具体的 Dubbo 引用服务。
后续如果再新增渠道系统,路由系统不需要再修改任何代码,只要在配置文件中新增 Dubbo 服务引用以及增加路由表引用关系即可。
第三阶段-登峰造极
第二个阶段路由系统基本上已经满足现有阶段业务实用,不过还是存在个问题,渠道应用新增时,还需要新增配置「重启应用」。
之前有一次新增渠道,忘记了在路由系统新增配置,从而导致新的渠道应用无法被调用,找了很久的问题,才发现是这个问题。
所以第三阶段,主要是优化路由系统,去掉上述配置文件,到达新增渠道应用,而不用重启路由系统。
这个阶段的改造,我们不再使用 XML 配置引用服务,而是借助 「Dubbo API」 ,动态引用 Dubbo 服务。
查看 Dubbo 文档 ,可以直接使用 ReferenceConfig 直接查找服务提供者。
官方文档建议:
ReferenceConfig 实例很重,封装了与注册中心的连接以及与提供者的连接,需要缓存。否则重复生成 ReferenceConfig 可能造成性能问题并且会有内存和连接泄漏。在 API 方式编程时,容易忽略此问题。
这里使用ReferenceConfigCache,用于缓存 「ReferenceConfig」 实例。
改造之后,去除之前所有引用服务配置文件以及缓存注册代码,不用再使用 Map
存储路由的映射关系。改造如下:
总结
回顾上文,可以看到初期没有路由系统,整个系统可以运行下去。
但是随着业务量不断变大变复杂,最开始的系统架构已经不能适应当前的环境,所以我们才开始系统拆分,进行微服务改造,一步步改进系统。
改进的过程中,不断发现方案不足处,然后一步步迭代演进。这个过程中,要善于利用现有框架的功能,加速功能的开发。
最后,本文给出了几种不同阶段路由系统实现方式,适合不同阶段、不同类型的系统。
如果各位同学刚好也有类似需要,可以根据自己系统的情况借鉴参考。
特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:
长按订阅更多精彩▼如有收获,点个在看,诚挚感谢
从无到有,支付路由系统升级打怪之路|原创相关推荐
- 支付路由技术概述以及简单的建设说明
文章目录 一.支付路由的架构设计 1-1.设计目的 1-2.系统架构 二.支付路由的计算因子 三.支付路由的限制规则 3-1.银行与渠道黑暗期 3-2.余额不足次数阈值 3-3.单笔鉴权验证 3-4. ...
- 【支付系统学习笔记】-二支付系统设计(支付路由设计)
前言: 本文属于学习笔记,首先感谢原作者:凤凰牌老熊,博客链接:http://blog.lixf.cn/ 作者上来回顾了支付流程, 一 设计目标 支付路由在支付系统中的核心作用,除了本职工作路由外 ...
- 智能路由心酸调优路——看推广代码简洁之道的重要性
智能路由心酸调优路--看推广代码简洁之道的重要性 甜橙金融作为一个日均订单量过千万的面向C端的互联网金融科技公司,525爆点活动订单量更是平时的好几倍,随着公司业务的高速发展,原有的系统架构,对于业务 ...
- gitlab 迁移、升级打怪之路:8.8.5-- 8.10.8 -- 8.17.8 -- 9.5.9 -- 10.1.4 -- 10.2.5
gitlab 迁移.升级打怪之路:8.8.5--> 8.10.8 --> 8.17.8 --> 9.5.9 --> 10.1.4 --> 10.2.5 gitlab 数据 ...
- 支付系统 — 支付路由
本文主要介绍下支付中路由系统的主要流程. 支付路由的作用 降低成本:越便宜越好: 提高用户体验:用户支付的越爽越好:越快越好:成功率越高越好. 确保有可用通道:多个选择,确保能完成支付. 初期 在自由 ...
- 沪江计算机考研老师,[回报沪江]从六级370到考研74,我的08研英之路[原创].doc
[回报沪江]从六级370到考研74,我的08研英之路[原创] 08年的考研终于结束了,政治78 英语74 数一110 专业课(计算机)137 总分399.回忆这一年的考研之路,觉得最幸运的就是认识了沪 ...
- 【路径规划】基于Djisktra算法实现路由无线传感器网络模拟随机路点运动模型含Matlab源码
1 简介 基于Djisktra算法实现路由无线传感器网络模拟随机路点运动模型 2 部分代码 clc;clear all;close all;global EexL X Y Xb Yb N node ...
- 支付路由系统设计二:核心流程
技术栈:Java+Groovy+Lua+Springboot+Mysql+Redis+Drools+Velocity+RabbitMQ+Spring Data Jpa 目录 一.背景 二.分析 1.命 ...
- 老王开店和支付路由管理
老王开店和支付路由管理(王晓憨) 在上大学学金融的第一堂课上,老师说"金融是什么?金融就是放在冰箱里的一块肉,从冰箱拿出来,再放回去,你没做什么,但是手上已经沾满了油."后来阅历再 ...
最新文章
- [原创]SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT 'OpenRowset/OpenDatasource' 的访问...
- spring aop中的propagation的7种配置的意思
- 【Linux】一步一步学Linux——dpkg-deb命令(270)
- 浙江中医药大学第十一届程序设计竞赛题解
- Python解析xml文件,此实例将xml设置为模版(from lxml import etree)
- bom更改编号 sap_【BOM精讲】BOM 入门基本常识
- CodeForces - 786B Legacy (线段树+DIjkstra+思维)
- c语言数字类型转字符类型,C语言字符类型和数字类型互相转换
- 用友U8软件功能集合
- Peephole LSTM、GRU 实战
- python计算两点间距离_用python计算图像中两点之间的距离
- ardruino控制继电器_arduino控制继电器
- 【网络安全工程师面试合集】—社会工程学到底是什么?
- Java随笔记录第二章:输入输出流程控制
- cad剖切线的快捷键_Auto CAD2016剖面线快捷键是什么呢??
- vue3+ts+setup语法糖
- 安装和使用Linux花生壳(公网版)
- Linux df命令怎么使用
- GNN、GCN、GAT关系
- 字符串匹配问题(信息学奥赛一本通 - T1355)
热门文章
- 惠斯通电桥信号调理芯片_用惠斯通电桥测电阻
- wp10 android,WP10能在Android机正常用不?
- intellij gradle java_Java 使用 IntelliJ IDEA Gradle创建新项目报错问题解决方法
- 使用C++实现Socket编程传输文件
- 关于学习Python的一点学习总结(7->相关字符串操作)
- matlab入门笔记3
- 文件转换html文件失败,pandoc文档转换失败,出现错误67
- 积性函数、狄利克雷卷积、莫比乌斯反演
- 贵州高二会考计算机考试题,20167月贵州普通高中学业水平考试会考试题
- 桌面的html文件怎么打开方式,html文件怎么打开