目录
最近读了大众点评的一篇网关系统优化的文章,特记录一些有用的知识点
核心思路是大系统做小,做简单
能用阶段
短平快,直接对接渠道,无过多可以描述的
可用阶段
存在的问题:
1、所有的业务逻辑都在同一个物理部署单元,不同业务之间相互影响(如避免退款业务出现问题拖垮支付业务)
2、随着业务流量增大,数据库压力增大,偶尔波动,对用户支付体验大
3、支付退款等状态的同步依赖第三方支付的异步通知,一旦第三方支付出现问题较被动
解决方案
问题1:拆服务
按照渠道拆分,不同的第三方支付渠道独立一个物理部署单元,例如微信一个,支付宝一个。。。
按照业务类型拆分,不同业务独立一个物理部署单元,例如退款一个,支付一个
最终选择了按照业务拆分,这才是正常的选择!包括查询服务等之类都应该进行拆分
问题2:
解决方案Master-Slave 使用了中间件Zebra来做负载均衡和灾备切换,其实数据库还是一个瓶颈的,而且真正短时间内数据库服务挂掉的话,是否还是不敢将从库切为主库?
问题3:
主动查询,后台补单,较正常的操作
柔性可用阶段
新的挑战:
1、新同学按照自己习惯接入,尤其第三方渠道对接http服务就多个。。。
2、故障服务,会使之RPC连接池占用,拖垮重启服务后,依旧会被故障渠道击垮
3、针对SDK业务,渠道网关无法获悉用户未支付是否因为某个第三方网关内部故障
4、共享DB,容易被其他业务影响
5、退款链路异常case未进行统一收集、整理并分类,缺乏清晰的退款链路监控。
解决方案:
问题1:
收集整理不同的应用场景,抽象出一套接入框架。定义请求组装、请求执行、响应解析和错误重试等一整套网关交互流程,屏蔽了底层的HTTP或Socket交互细节,并提供响应的扩展点。
针对银行接入存在前置机这种特殊应用场景,基于Netty抽象出连接池(Conn Pool)和简单的负载均衡机制(LB,提供Round Robin路由策略)。
不同渠道在接入时可插入自定义的组装策略(扩展已有的HttpReq、HttpsReq或NettyReq),执行策略[扩展已有(Http、Https或Netty)Sender/Receiver],解析策略(扩展已有的HttpResp、HttpsResp或NettyResp),并复用框架已提供的内容解析(binary/xml/json parser)、证书加载(keystore/truststore loader)和加解密签名(encrypt/decrypt/sign/verify sign)组件,从而在达到提高渠道接入效率的同时,尽可能减少新渠道接入带来的风险。
问题2:隔离
我们直接将渠道隔离的最小粒度定义到支付渠道 -> 支付方式 -> 银行。
在fail-fast事务执行过程中,级联有2个fail-fast断路开关:
静态开关,根据人工配置(on/off),断定某个支付请求是否需快速失败。
动态开关,根据历史统计信息,确定当前健康状态,进而断定是否快速失败当前支付请求。
动态断路开关抽象了3种健康状态(closed-放行所有请求;half_open-部分比例的请求放行;open-快速失败所有请求),并依据历史统计信息(总请求量/请求失败量/请求异常量/请求超时量),在其内部维护了一个健康状态变迁的状态机。我们在此基础上增加了自动切换,主备两个渠道,优先自动切换,如果切换后备渠道还是有问题,那么开始执行类似部分比例放行这种情况
状态机的每一次状态变迁都会产生一个健康状态事件,收银台服务可以监听这个健康状态事件,实现支付渠道的联动上下线切换。
每一笔支付请求结束后都会动态更新历史统计信息。
问题3:
成功率监控
问题4:
独享DB
问题5:
退款异常case收集
总结
在整个渠道网关系统一步步的完善过程中,踩过很多坑,吃过很多教训,几点小的收获:
坚持核心思想,拆分、解耦,大系统做小,做简单;
系统总会有出问题的时候,重要的是如何快速定位、恢复、解决问题,这是一个长期而又艰巨的任务;
高可用性的最大敌人不仅是技术,还是使用技术实现系统的人,如何在业务、系统快速迭代的过程中,保证自我驱动,不掉队;
高流量,大并发对每一个工程师既是挑战,更是机遇。
感谢!文章参考整理:序列化与反序列化