可用性高达5个9!支付系统高可用架构设计实战

1、背景

对于互联网应用和企业大型应用而言,多数都尽量地要求作到7*24小时不间断运行,而要作到彻底不间断运行能够说“难于上青天”。为此,对应用可用性程度的衡量标准通常有3个9到5个9。php

可用性指标 计算方式 不可用时间(分钟)
99.9% 0.1%*365*24*60 525.6
99.99% 0.01%*365*24*60 52.56
99.999% 0.001%*365*24*60 5.256

对于一个功能和数据量不断增长的应用,要保持比较高的可用性并不是易事。为了实现高可用,宜信支付系统从避免单点故障、保证应用自身的高可用、解决交易量增加等方面作了许多探索和实践。java

在不考虑外部依赖系统突发故障,如网络问题、三方支付和银行的大面积不可用等状况下,宜信支付系统的服务能力能够达到99.999%。python

本文重点讨论如何提升应用自身的可用性,关于如何避免单点故障和解决交易量增加问题会在其余系列讨论。linux

为了提升应用的可用性,首先要作的就是尽量避免应用出现故障,但要彻底作到不出故障是不可能的。互联网是个容易产生“蝴蝶效应”的地方,任何一个看似很小的、发生几率为0的事故均可能出现,而后被无限放大。redis

你们都知道RabbitMQ自己是很是稳定可靠的,宜信支付系统最开始也一直在使用单点RabbitMQ,而且从未出现运行故障,因此你们在心理上都认为这个东西不太可能出问题。数据库

直到某天,这台节点所在的物理主机硬件由于年久失修坏掉了,当时这台RabbitMQ就没法提供服务,致使系统服务瞬间不可用。缓存

故障发生了也不可怕,最重要的是及时发现并解决故障。宜信支付系统对自身系统的要求是,秒级发现故障,快速诊断和解决故障,从而下降故障带来的负面影响。安全

2、问题

以史为鉴。首先咱们简单的回顾一下,宜信支付系统曾经碰到的一些问题:服务器

(1) 新来的开发同事在处理新接入的三方通道时,因为经验不足忽视了设置超时时间的重要性。就是这样一个小小的细节,致使这个三方队列所在的交易所有堵塞,同时影响到其余通道的交易;微信

(2) 宜信支付系统是分布式部署的,而且支持灰度发布,因此环境和部署模块很是多并且复杂。某次增长了一个新模块,因为存在多个环境,且每一个环境都是双节点,新模块上线后致使数据库的链接数不够用,从而影响其余模块功能;

(3) 一样是超时问题,一个三方的超时,致使耗尽了当前所配置的全部worker threads, 以致于其余交易没有可处理的线程;

(4) A三方同时提供鉴权,支付等接口,其中一个接口由于宜信支付系统交易量突增,从而触发A三方在网络运营商那边的DDoS限制。一般机房的出口IP都是固定的,从而被网络运营商误认为是来自这个出口IP的交易是流量攻击,最终致使A三方鉴权和支付接口同时不可用。

(5) 再说一个数据库的问题,一样是由于宜信支付系统交易量突增引起的。创建序列的同事给某个序列的上限是999,999,999,但数据库存的这个字段长度是32位,当交易量小的时候,系统产生的值和字段32位是匹配的,序列不会升位。但是随着交易量的增长,序列不知不觉的升位数了,结果致使32位就不够存放。

相似这样的问题对于互联网系统很是常见,而且具备隐蔽性,因此如何避免就显得很是重要了。

3、解决方案

下面咱们从三个方面来看宜信支付系统所作的改变。

3.1 尽量避免故障

3.1.1 设计可容错的系统

好比重路由,对于用户支付来讲,用户并不关心本身的钱具体是从哪一个通道支付出去的,用户只关心成功与否。宜信支付系统链接30多个通道,有可能A通道支付不成功,这个时候就须要动态重路由到B或者C通道,这样就能够经过系统重路由避免用户支付失败,实现支付容错。

还有针对OOM作容错,像Tomcat同样。系统内存总有发生用尽的状况,若是一开始就对应用自己预留一些内存,当系统发生OOM的时候,就能够catch住这个异常,从而避免此次OOM。

3.1.2 某些环节快速失败“fail fast原则”

Fail fast原则是当主流程的任何一步出现问题的时候,应该快速合理地结束整个流程,而不是等到出现负面影响才处理。

举个几个例子:

(1)支付系统启动的时候须要加载一些队列信息和配置信息到缓存,若是加载失败或者队列配置不正确,会形成请求处理过程的失败,对此最佳的处理方式是加载数据失败,JVM直接退出,避免后续启动不可用;

(2)支付系统的实时类交易处理响应时间最长是40s,若是超过40s前置系统就再也不等待,释放线程,告知商户正在处理中,后续有处理结果会以通知的方式或者业务线主动查询的方式获得结果;

(3)宜信支付系统使用了redis作缓存数据库,用到的地方有实时报警埋点和验重等功能。若是链接redis超过50ms,那么这笔redis操做会自动放弃,在最坏的状况下这个操做带给支付的影响也就是50ms,控制在系统容许的范围内。

3.1.3 设计具有自我保护能力的系统

系统通常都有第三方依赖,好比数据库,三方接口等。系统开发的时候,须要对第三方保持怀疑,避免第三方出现问题时候的连锁反应,致使宕机。

(1)拆分消息队列

宜信支付系统提供各类各样的支付接口给商户,经常使用的就有快捷,我的网银,企业网银,退款,撤销,批量代付,批量代扣,单笔代付,单笔代扣,语音支付,余额查询,身份证鉴权,银行卡鉴权,卡密鉴权等。与其对应的支付通道有微信支付,ApplePay,支付宝等30多家支付通道,而且接入了几百家商户。在这三个维度下,如何确保不一样业务、三方、商户、以及支付类型互不影响,宜信支付系统所作的就是拆分消息队列。下图是部分业务消息队列拆分图:

(2)限制资源的使用

对于资源使用的限制设计是高可用系统最重要的一点,也是容易被忽略的一点,资源相对有限,用的过多了,天然会致使应用宕机。为此宜信支付系统作了如下功课:

  • 限制链接数

随着分布式的横向扩展,须要考虑数据库链接数,而不是无休止的最大化。数据库的链接数是有限制的,须要全局考量全部的模块,特别是横向扩展带来的增长。

  • 限制内存的使用

内存使用过大,会致使频繁的GC和OOM,内存的使用主要来自如下两个方面:

A:集合容量过大;

B:未释放已经再也不引用的对象,好比放入ThreadLocal的对象一直会等到线程退出的时候回收。

  • 限制线程建立

线程的无限制建立,最终致使其不可控,特别是隐藏在代码中的建立线程方法。

当系统的SY值太高时,表示linux须要花费更多的时间进行线程切换。Java形成这种现象的主要缘由是建立的线程比较多,且这些线程都处于不断的阻塞(锁等待,IO等待)和执行状态的变化过程当中,这就产生了大量的上下文切换。

除此以外,Java应用在建立线程时会操做JVM堆外的物理内存,太多的线程也会使用过多的物理内存。

对于线程的建立,最好经过线程池来实现,避免线程过多产生上下文切换。

  • 限制并发

作过支付系统的应该清楚,部分三方支付公司是对商户的并发有要求的。三方给开放几个并发是根据实际交易量来评估的,因此若是不控制并发,全部的交易都发给三方,那么三方只会回复“请下降提交频率”。

因此在系统设计阶段和代码review阶段都须要特别注意,将并发限制在三方容许的范围内。

咱们讲到宜信支付系统为了实现系统的可用性作了三点改变,其一是尽量避免故障,接下来说后面两点。

3.2 及时发现故障

故障就像鬼子进村,来的猝不及防。当预防的防线被冲破,如何及时拉起第二道防线,发现故障保证可用性,这时候报警监控系统的开始发挥做用了。一辆没有仪表盘的汽车,是没法知道车速和油量,转向灯是否亮,就算“老司机”水平再高也是至关危险的。一样,系统也是须要监控的,最好是出现危险的时候提早报警,这样能够在故障真正引起风险前解决。

3.2.1 实时报警系统

若是没有实时报警,系统运行状态的不肯定性会形成没法量化的灾难。宜信支付系统的监控系统指标以下:

  • 实时性-实现秒级监控;

  • 全面性-覆盖全部系统业务,确保无死角覆盖;

  • 实用性-预警分为多个级别,监控人员能够方便实用地根据预警严重程度作出精确的决策;

  • 多样性-预警方式提供推拉模式,包括短信,邮件,可视化界面,方便监控人员及时发现问题。

报警主要分为单机报警和集群报警,而宜信支付系统属于集群部署。实时预警主要依靠各个业务系统实时埋点数据统计分析实现,所以难度主要在数据埋点和分析系统上。

3.2.2 埋点数据

要作到实时分析,又不影响交易系统的响应时间,宜信支付系统在各个模块中经过redis实时作数据埋点,而后将埋点数据汇总到分析系统,分析系统根据规则进行分析报警。

3.2.3 分析系统

分析系统最难作的是业务报警点,例如哪些报警只要一出来就必须出警,哪些报警一出来只须要关注。下面咱们对分析系统作一个详细介绍:

(1)系统运行架构

(2)系统运行流程

(3)系统业务监控点

宜信支付系统的业务监控点都是在平常运行过程当中一点一滴总结出来的,分为出警类和关注类两大块。

A:出警类

  • 网络异常预警;

  • 单笔订单超时未完成预警;

  • 实时交易成功率预警;

  • 异常状态预警;

  • 未回盘预警;

  • 失败通知预警;

  • 异常失败预警;

  • 响应码频发预警;

  • 核对不一致预警;

  • 特殊状态预警;

B:关注类

  • 交易量异常预警;

  • 交易额超过500W预警;

  • 短信回填超时预警;

  • 非法IP预警;

3.2.4 非业务监控点

非业务监控点主要是指从运维角度的监控,包括网络,主机,存储,日志等。具体以下:

(1)服务可用性监控

使用JVM采集Young GC/Full GC次数及时间、堆内存、耗时Top 10线程堆栈等信息,包括缓存buffer的长度。

(2)流量监控

经过Agent监控代理部署在各个服务器上,实时采集流量状况。

(3)外部系统监控

经过间隙性探测来观察三方或者网络是否稳定。

(4)中间件监控

  • 针对MQ消费队列,经过RabbitMQ脚本探测,实时分析队列深度;

  • 针对数据库部分,经过安装插件xdb,实时监控数据库性能.

(5)实时日志监控

经过rsyslog完成分布式日志的归集,而后经过系统分析处理,完成日志实时监控和分析。最后,经过开发可视化页面展现给使用者。

(6)系统资源监控

经过Zabbix监控主机的CPU负载、内存使用率、各网卡的上下行流量、各磁盘读写速率、各磁盘读写次数(IOPS)、各磁盘空间使用率等。

以上就是宜信支付系统的实时监控系统所作的,主要分为业务点监控和运维监控两方面,虽然系统是分布式部署,可是每一个预警点都是秒级响应。除此以外,业务系统的报警点也有一个难点,那就是有些报警是少许报出来不必定有问题,大量报警就会有问题,也就是所谓的量变引发质变。

举一个例子,拿网络异常来讲,发生一笔多是网络抖动,可是多笔发生就须要重视网络是否真的有问题,针对网络异常宜信支付系统的报警样例以下:

  • 单通道网络异常预警:1分钟内A通道网络异常连续发生了12笔,触发了预警阀值;

  • 多通道网络异常预警1: 10分钟内,连续每分钟内网络异常发生了3笔,涉及3个通道,触发了预警阀值;

  • 多通道网络异常预警2: 10分钟内,总共发生网络异常25笔,涉及3个通道, 触发了预警阀值.

3.2.5 日志记录和分析系统

对于一个大型系统而言,天天记录大量的日志和分析日志是有必定的难度的。宜信支付系统天天平均有200W笔订单量,一笔交易通过十几个模块流转,假设一笔订单记录30条日志,可想而知天天会有多么巨大的日志量。

宜信支付系统日志的分析有两个做用,一个是实时日志异常预警,另一个是提供订单轨迹给运营人员使用。

(1)实时日志预警

实时日志预警是针对全部实时交易日志,实时抓取带有Exception或者Error的关键字而后报警。这样的好处是,若是代码中有任何运行异常,都会第一时间发现。宜信支付系统针对实时日志预警的处理方式是,首先采用rsyslog完成日志归集,而后经过分析系统实时抓取,再作实时预警。

(2)订单轨迹

对于交易系统,很是有必要实时了解一笔订单的状态流转。宜信支付系统最初的作法是经过数据库来记录订单轨迹,可是运行一段时间后,发现订单量剧增致使数据库表过大不利于维护。

宜信支付系统如今的作法是,每一个模块经过打印日志轨迹,日志轨迹打印的格式按照数据库表结构的方式打印,打印好全部日志后,rsyslog来完成日志归集,分析系统会实时抓取打印的规范日志,进行解析而后按天存放到数据库中,并展现给运营人员可视化界面。

日志打印规范以下:

2016-07-22 18:15:00.512||pool-73-thread-4||通道适配器||通道适配器-发三方后||CEX16XXXXXXX5751||16201XXXX337||||||04||9000||【结算平台消息】处理中||0000105||98XX543210||GHT||03||11||2016-07-22 18:15:00.512||张张||||01||tunnelQuery||true||||Pending||||10.100.140.101||8cff785d-0d01-4ed4-b771-cb0b1faa7f95||10.999.140.101||O001||||0.01||||||||http://10.100.444.59:8080/regression/notice||||240||2016-07-20 19:06:13.000xxxxxxx

||2016-07-22 18:15:00.170||2016-07-22 18:15:00.496xxxxxxxxxxxxxxxxxxxx

||2016-07-2019:06:13.000||||||||01||0103||111xxxxxxxxxxxxxxxxxxxxxxxxx

||8fb64154bbea060afec5cd2bb0c36a752be734f3e9424ba7xxxxxxxxxxxxxxxxxxxx

||622xxxxxxxxxxxxxxxx||9bc195a59dd35a47||f2ba5254f9e22914824881c242d211

||||||||||||||||||||6xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx010||||||||||

简要日志可视化轨迹以下:

日志记录和分析系统除了以上两点,也提供了交易和响应报文的下载和查看。

3.2.6 7*24小时监控室

宜信支付系统以上的报警项目给操做人员提供推拉两种方式,一种是短信和邮件推送,一种是报表展现。除此以外,因为支付系统相比互联网其余系统自己的重要性,宜信支付系统采用7*24小时的监控室保证系统的安全稳定。

3.3 及时处理故障

在故障发生以后,特别是生产环境,第一时间要作的不是寻找故障发生的缘由,而是以最快速度处理故障,保障系统的可用性。宜信支付系统常见的故障和处理措施以下:

3.3.1 自动修复

针对自动修复部分,宜信支付系统常见的故障都是三方不稳定形成的,针对这种状况,就是上面说的系统会自动进行重路由。

3.3.2 服务降级

服务降级指在出现故障的状况下又没法快速修复的状况下,把某些功能关闭,以保证核心功能的使用。宜信支付系统针对商户促销的时候,若是某个商户交易量过大,会实时的调整这个商户的流量,使此商户服务降级,从而不会影响到其余商户,相似这样的场景还有不少,具体的服务降级功能会在后续系列介绍。

4、Q&A

Q1: 能讲讲当年那台RabbitMQ宕掉的具体细节和处理方案吗?

A1: RabbitMQ宕机时间引起了对系统可用性的思考,当时咱们的RabbitMQ自己并无宕机(RabbitMQ仍是很稳定的),宕机的是RabbitMQ所在的硬件机器,可是问题就出在当时RabbiMQ的部署是单点部署,而且你们惯性思惟认为RabbitMQ不会宕机,从而忽略了它所在的容器,因此这个问题的产生对于咱们的思考就是全部的业务不能够有单点,包括应用服务器、中间件、网络设备等。单点不只仅须要从单点自己考虑,好比整个服务作双份,而后AB测试,固然也有双机房的。

Q2: 贵公司的开发运维是在一块儿的吗?

A2: 咱们开发运维是分开的,今天的分享主要是站在整个系统可用性层面来考虑的,开发偏多,有一部分运维的东西。这些宜信支付系统的走过的路,是我一路见证过的。

Q3: 大家的后台所有使用的Java吗?有没有考虑其余语言?

A3: 咱们目前系统多数是java,有少数的python、php、C++,这个取决于业务类型,目前java这个阶段最适合咱们,可能随着业务的扩展,会考虑其余语言。

Q4: 对第三方依赖保持怀疑,可否举个具体的例子说明下怎么样作?万一第三方彻底不了用了怎么办

A4: 系统通常都有第三方依赖,好比数据库,三方接口等。系统开发的时候,须要对第三方保持怀疑,避免第三方出现问题时候的连锁反应,致使宕机。你们都知道系统一旦发生问题都是滚雪球的,愈来愈大。好比说咱们扫码通道,若是只有一家扫码通道,当这家扫码通道发生问题的时候是没有任何办法的,因此一开始就对它表示怀疑,经过接入多家通道,若是一旦发生异常,实时监控系统触发报警后就自动进行路由通道切换,保证服务的可用性;其二,针对不一样的支付类型、商户、交易类型作异步消息拆分,确保若是一旦有一种类型的交易发生不可预估的异常后,从而不会影响到其余通道,这个就比如高速公路多车道同样,快车和慢车道互不影响。其实整体思路就是容错+拆分+隔离,这个具体问题具体对待。

Q5: 支付超时后,会出现网络问题,会不会存在钱已付,订单丢失,如何作容灾及数据一致性,又有没重放日志,修过数据?

A5:作支付最重要的就是安全,因此针对订单状态咱们都是保守处理策略,所以对于网络异常的订单咱们都是设置处理中状态,而后最终经过主动查询或者被动接受通知来完成和银行或者三方的最终一致性。支付系统中,除了订单状态还有响应码问题,你们都知道银行或者三方都是经过响应码来响应的,响应码和订单状态的翻译也是必定要保守策略,确保不会出现资金多付少付等问题。总之这个点的整体思路是,资金安全第一,全部的策略都是白名单原则。

Q6: 刚才提到过,若某支付通道超时,路由策略会分发至另外一通道,根据那个通道图可看出,都是不一样的支付方式,好比支付宝或微信支付,那若是我只想经过微信支付,为啥不是重试,而要换到另外一通道呢?仍是通道自己意思是请求节点?

A6:首先针对超时不能够作重路由,由于socket timeout是不能肯定这笔交易是否发送到了三方,是否已经成功或者失败,若是是成功了,再重试一遍若是成功,针对付款就是多付,这种状况的资金损失对公司来讲不能够的; 其次,针对路由功能,须要分业务类型,若是是单笔代收付交易,用户是不关心钱是哪一个通道出去的,是能够路由的,若是是扫码通道,用户若是用微信扫码,确定最终是走微信,可是咱们有好多中间渠道,微信是经过中间渠道出去的,这里咱们能够路由不一样的中间渠道,这样最终对于用户来讲仍是微信支付。

Q7: 可否举例说下自动修复的过程?如何发现不稳定到重路由的细节?

A7: 自动修复也就是经过重路由作容错处理,这个问题很是好,若是发现不稳定而后去决策重路由。重路由必定是明确当前被重路由的交易没有成功才能够路由,不然就会形成多付多收的资金问题。咱们系统目前重路由主要是经过过后和事中两种方式来决策的,针对过后好比5分钟以内经过实时预警系统发现某个通道不稳定,那么就会把当期以后的交易路由到别的通道;针对事中的,主要是经过分析每笔订单返回的失败响应码,响应码作状态梳理,明确能够重发的才作重路由。这里我指列举这两点,其余的业务点还很是多,鉴于篇幅缘由,不作详述,可是整体思路是必须有一个内存实时分析系统,秒级决策,这个系统必须快,而后结合实时分析和离线分析作决策支撑,咱们的实时秒级预警系统就作这个事情。

Q8: 商户促销有规律吗?促销时峰值与平时相比会有多少差异?有技术演练么?降级的优先级是怎样的?

A8: 商户促销通常咱们会事先常常和商户保持沟通,事先了解促销的时间点和促销量,而后针对性作一些事情;促销峰值和平时差距很是大,促销通常都是2个小时以内的比较多,好比有的卖理财产品,促销也就集中在1个小时以内,因此峰值很是高;技术演练是咱们在了解商户的促销量,而后预估系统的处理能力,而后提早作演练;降级的优先级主要是针对商户的,因为接入咱们的商户支付场景比较多的,有理财,有代收付,有快捷,有扫码等等,因此咱们总体原则就是不一样的商户之间必定不能够相互影响,由于不能由于你家作促销影响了其余商家。

Q9:rsyslog归集日志怎么存储的?

A9: 这个是好问题,刚开始咱们的日志也就是订单轨迹log是记录在数据库表中的,结果发现一笔订单流转须要好多模块,这样一笔订单的日志轨迹就是10笔左右,若是一天400w笔交易的话,这张数据库表就有问题了,就算拆分也是会影响数据库性能的,而且这个属于辅助业务,不该该这样作。而后,咱们发现写日志比写数据库好,因此把实时日志打印成表格的形式,打印到硬盘上,这块因为只是实时日志因此日志量不大,就是在日志服务器的一个固定目录下。因为日志都是在分布式机器上,而后经过归集日志到一个集中的地方,这块是经过挂载存储的,而后有专门运维团队写的程序去实时解析这些表格形式的日志,最终经过可视化页面展现到运营操做页面,这样运营人员看到的订单轨迹几乎是实时的,您关心的怎么存储实际上不是啥问题,由于咱们分了实时日志和离线日志,而后超过必定时间的离线日志会切割,最终被删除。

Q10: 系统监控和性能监控如何配合的?

A10:我理解的系统监控包括了系统性能监控,系统性能监控是系统总体监控的一部分,不存在配合问题,系统性能监控有多个维度,好比应用层面,中间件,容器等。系统的非业务监控能够查看文章分享。

做者:冯忠旗

来源:宜信技术学院

相关文章
相关标签/搜索