文档章节

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

vshcxl
 vshcxl
发布于 2016/12/12 21:57
字数 4376
阅读 34
收藏 1
点赞 0
评论 0

对于互联网应用和企业大型应用而言,多数都尽可能地要求做到7*24小时不间断运行,而要做到完全的不间断运行可以说“难于上青天”。

 

为此,对应用的可用性程度一般衡量标准有三个9到五个9。

 

 

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

 

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

 

本文重点讨论如何提高应用自身的可用性。

 

为了提高应用的可用性,首先要做的就是尽可能避免应用出现故障,但要完全做到不出故障是不可能的。互联网是个容易产生“蝴蝶效应”的地方,任何一个看似很小的、发生概率为0的事故都可能出现,然后被无限放大。

 

大家都知道RabbitMQ本身是非常稳定可靠的,付钱拉最开始也一直在使用单点RabbitMQ,并且从未出现运行故障,所以大家在心理上都认为这个东西不太可能出问题。

 

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

 

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

 

首先简单回顾一下我们曾经碰到的一些问题:

 

以史为鉴

 

  1. 新来的开发同事在处理新接入的三方通道时,由于经验不足忽视了设置超时时间的重要性。就是这样一个小小的细节,导致这个三方队列所在的交易全部堵塞,同时影响到其他通道的交易。

  2. 系统是分布式部署的,并且支持灰度发布,所以环境和部署模块非常多而且复杂。某次增加了一个新模块,由于存在多个环境,且每个环境都是双节点,新模块上线后导致数据库的连接数不够用,从而影响其他模块功能。

  3. 同样是超时问题,一个三方的超时,导致耗尽了当前所配置的所有worker threads,以至于其他交易没有可处理的线程。

  4. A三方同时提供鉴权,支付等接口,其中一个接口因为我们的交易量突增,从而触发A三方在网络运营商那边的DDoS限制。通常机房的出口IP都是固定的,从而被网络运营商误认为是来自这个出口IP的交易是流量攻击,最终导致A三方鉴权和支付接口同时不可用。

  5. 再说一个数据库的问题,同样是因为我们的交易量突增引发的。建立序列的同事给某个序列的上限是999,999,999,但数据库存的这个字段长度是32位,当交易量小的时候,系统产生的值和字段32位是匹配的,序列不会升位。可是随着交易量的增加,序列不知不觉的升位数了,结果导致32位就不够存放。

 

类似这样的问题对于互联网系统非常常见,并且具有隐蔽性,所以如何避免就显得非常重要了。

 

下面我们从三个方面来看我们所做的改变。

 

尽可能避免故障

 

>>>>

设计可容错的系统

 

比如重路由,对于用户支付来说,用户并不关心自己的钱具体是从哪个通道支付出去的,用户只关心成功与否。付钱拉连接30多个通道,有可能A通道支付不成功,这个时候就需要动态重路由到B或者C通道,这样就可以通过系统重路由避免用户支付失败,实现支付容错。

还有针对OOM做容错,像Tomcat一样。系统内存总有发生用尽的情况,如果一开始就对应用本身预留一些内存,当系统发生OOM的时候,就可以catch住这个异常,从而避免这次OOM。

 

>>>>

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

 

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

 

举个几个例子:

 

  • 付钱拉启动的时候需要加载一些队列信息和配置信息到缓存,如果加载失败或者队列配置不正确,会造成请求处理过程的失败,对此最佳的处理方式是加载数据失败,JVM直接退出,避免后续启动不可用;

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

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

 

>>>>

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

 

系统一般都有第三方依赖,比如数据库、三方接口等。系统开发的时候,需要对第三方保持怀疑,避免第三方出现问题时候的连锁反应,导致宕机。

 

(1)拆分消息队列

 

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

 

 

(2)限制资源的使用

 

对于资源使用的限制设计是高可用系统最重要的一点,也是容易被忽略的一点,资源相对有限,用的过多了,自然会导致应用宕机。为此我们做了以下功课:

 

  • 限制连接数

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

 

  • 限制内存的使用

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

  1. 集合容量过大; 

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

 

  • 限制线程创建

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

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

 

除此之外,Java应用在创建线程时会操作JVM堆外的物理内存,太多的线程也会使用过多的物理内存。对于线程的创建,最好通过线程池来实现,避免线程过多产生上下文切换。

 

  • 限制并发

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

 

所以在系统设计阶段和代码review阶段都需要特别注意,将并发限制在三方允许的范围内。

 

及时发现故障

 

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

 

>>>>

实时报警系统

 

如果没有实时报警,系统运行状态的不确定性会造成无法量化的灾难。我们的监控系统指标如下:

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

  • 全面性:覆盖所有系统业务,确保无死角覆盖;

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

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

 

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

 

>>>>

埋点数据

 

要做到实时分析,又不影响交易系统的响应时间,我们在系统各个模块中通过redis实时做数据埋点,然后将埋点数据汇总到分析系统,分析系统根据规则进行分析报警。

 

>>>>

分析系统

 

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

 

1、系统运行架构

 

 

2、系统运行流程

 

 

3、系统业务监控点

 

我们的业务监控点都是在日常运行过程中一点一滴总结出来的,分为出警类和关注类两大块。

 

出警类:

  • 网络异常预警;

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

  • 实时交易成功率预警;

  • 异常状态预警;

  • 未回盘预警;

  • 失败通知预警;

  • 异常失败预警;

  • 响应码频发预警;

  • 核对不一致预警;

  • 特殊状态预警;

 

关注类:

  • 交易量异常预警;

  • 交易额超过500W预警;

  • 短信回填超时预警;

  • 非法IP预警;

 

4、非业务监控点

 

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

 

  • 服务可用性监控:

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

 

  • 流量监控:

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

 

  • 外部系统监控:

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

 

  • 中间件监控:

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

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

 

  • 实时日志监控:

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

 

  • 系统资源监控:

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

 

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

 

举一个例子,拿网络异常来说,发生一笔可能是网络抖动,但是多笔发生就需要重视网络是否真的有问题,针对网络异常,我们的报警样例如下:

 

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

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

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

 

>>>>

日志记录和分析系统

 

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

 

我们日志的分析有两个作用,一个是实时日志异常预警,另外一个是提供订单轨迹给运营人员使用。

 

>>>>

实时日志预警

 

实时日志预警是针对所有实时交易日志,实时抓取带有Exception或者Error的关键字然后报警。这样的好处是,如果代码中有任何运行异常,都会第一时间发现。我们针对实时日志预警的处理方式是,首先采用rsyslog完成日志归集,然后通过分析系统实时抓取,再做实时预警。

 

>>>>

订单轨迹

 

对于交易系统,非常有必要实时了解一笔订单的状态流转。我们最初的做法是通过数据库来记录订单轨迹,但是运行一段时间后,发现订单量剧增导致数据库表过大不利于维护。

 

我们现在的做法是,每个模块通过打印日志轨迹,日志轨迹打印的格式按照数据库表结构的方式打印,打印好所有日志后,rsyslog来完成日志归集,分析系统会实时抓取打印的规范日志,进行解析然后按天存放到数据库中,并展示给运营人员可视化界面。

 

日志打印规范如下:

 

 

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

 

 

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

 

>>>>

7*24小时监控室

 

以上的报警项目给操作人员提供推拉两种方式,一种是短信和邮件推送,一种是报表展示。除此之外,由于支付系统相比互联网其他系统本身的重要性,我们采用7*24小时的监控室保证系统的安全稳定。

 

及时处理故障

 

在故障发生之后,特别是生产环境,第一时间要做的不是寻找故障发生的原因,而是以最快速度处理故障,保障系统的可用性。我们常见的故障和处理措施如下:

 

>>>>

自动修复

 

针对自动修复部分,我们常见的故障都是三方不稳定造成的,针对这种情况,就是上面说的系统会自动进行重路由。

 

>>>>

服务降级

 

服务降级指在出现故障的情况下又无法快速修复的情况下,把某些功能关闭,以保证核心功能的使用。我们针对商户促销的时候,如果某个商户交易量过大,会实时的调整这个商户的流量,使此商户服务降级,从而不会影响到其他商户,类似这样的场景还有很多,具体的服务降级功能会在后续系列介绍。

© 著作权归作者所有

共有 人打赏支持
vshcxl
粉丝 19
博文 278
码字总数 32372
作品 0
浦东
高级程序员
架构知识整理

架构概念 一套系统的软件架构就是这个系统所需的结构体的集合,包括:软件元素,软件元素之间的关系,以及二者的属性。 从视角不同一般分为: 逻辑架构 关注于各个组件之间的关系,如:用户界...

古城痴人 ⋅ 2015/08/19 ⋅ 0

【撸码师读书笔记】 大型网站技术架构——核心原理与案例分析

合理的预估系统的瓶颈及制定有效伸缩性架构预案; 架构设计应从性能,可用性,伸缩性,扩展性及安全这五个要素方面出发; 与传统企业应用系统相比,大型互联网应用系统有一下特点: 01.高并发...

开源中国首席撸码官 ⋅ 2015/04/28 ⋅ 0

大型分布式网站架构技术总结

大型分布式网站架构 大型分布式网站架构技术总结 本文是学习大型分布式网站架构的技术总结。对架构一个高性能,高可用,可伸缩,可扩展的分布式网站进行了概要性描述,并给出一个架构参考。一...

陶邦仁 ⋅ 2016/03/15 ⋅ 0

方法论与技术栈双管齐下的运维可用性能力建设

业务的不断演进,系统的数据量不断扩大,技术栈越来越复杂,系统模块越来越多,造成信息系统中断的风险场景越来越多,中断事件的频率和种类持续增长,且有相当一部份事件会造成业务中断,可用...

广发证券 彭华盛 ⋅ 05/09 ⋅ 0

大型分布式网站架构技术总结

原文出处: ITFLY8 本文是学习大型分布式网站架构的技术总结。对架构一个高性能,高可用,可伸缩,可扩展的分布式网站进行了概要性描述,并给出一个架构参考。一部分为读书笔记,一部分是个人...

umgsai ⋅ 2017/09/26 ⋅ 0

招商银行周伟:Fintech数据开放平台之数据库军规和内功修炼(含PPT)

在2018数据库大会上,招商银行的资深数据库架构师周伟,分享了招行金融科技数据开放平台的建设经验。这个主题引起了现场听众的广泛关注,我们在此整理发布出来,以其让更多用户能够借鉴到招商...

技术小能手 ⋅ 05/28 ⋅ 0

SDCC 2017·上海站震撼来袭,首批讲师和议题大公开

时隔一年,2017年3月17-19日,SDCC 2017·上海站震撼来袭,特开设了三大主题会场,分别是互联网运维开发实战峰会、数据库核心技术与应用实战峰会和互联网应用架构实战峰会,为期三天,为软件...

玄学酱 ⋅ 04/19 ⋅ 0

架构设计思路

前言 我们一般在做架构设计的时候,会经历过三个阶段:需求分析、概要设计和详细设计。 需求分析阶段: 主要梳理所有用例(Use case)和场景,并抽象出面向系统的用户与角色,梳理出需求提供哪...

6776jkjk ⋅ 2017/11/19 ⋅ 0

大型网站技术架构 核心原理与案例分析 2016-4-17

第1篇 概述 1 大型网站架构演化 2 1.1 大型网站软件系统的特点 3 1.2 大型网站架构演化发展历程 4 1.2.1 初始阶段的网站架构 4 1.2.2 应用服务和数据服务分离 4 1.2.3 使用缓存改善网站性能 ...

jayronwang ⋅ 2016/04/17 ⋅ 0

分布式系统实战

导言:记得在自己大学毕业的2006年到之后近五年的工作里,源于工作经历和有限的视野,几乎对“分布式系统”没有任何概念。当然,彼时的互联网/移动互联网还未对我们的生活呈覆盖颠覆之势,很...

yaocoder ⋅ 2017/12/03 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

那些证书相关的玩意儿(SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12等)

之前没接触过证书加密的话,对证书相关的这些概念真是感觉挺棘手的,因为一下子来了一大堆新名词,看起来像是另一个领域的东西,而不是我们所熟悉的编程领域的那些东西,起码我个人感觉如此,且很长...

颖辉小居 ⋅ 17分钟前 ⋅ 0

利用有限制通配符提升API灵活性(28)

1、参数化类型是不可变的 List<String> 不是List<Object>的子类,但是二者是有联系的 利用有限制的通配符类型处理类似情况 List<? extends Object>(生产者) Collection<? super E>(消费者......

职业搬砖20年 ⋅ 23分钟前 ⋅ 0

ssm框架 +bootstrap分页

这里有两种方式 方式一:自己写分页 方式二:使用插件PageHelper 1.自己写分页 1.1 效果 1.2 实现过程 1.2.1 创建分页公共类 //---------------------------1.属性-------------------------...

Lucky_Me ⋅ 30分钟前 ⋅ 0

Istio

helm template install/kubernetes/helm/istio --name istio --namespace istio-system > $HOME/istio.yaml after $ kubectl create namespace istio-system$ kubectl create -f $HOME/ist......

openthings ⋅ 31分钟前 ⋅ 0

内核线程、轻量级进程、用户线程

线程与进程概念 在现代操作系统中,进程支持多线程。 进程是资源管理的最小单元; 线程是程序执行的最小单元。 即线程作为调度和分配的基本单位,进程作为资源分配的基本单位 一个进程的组成...

117 ⋅ 36分钟前 ⋅ 0

elasticsearch2.4.6升级为elasticsearch-5.5.0的经历

将elasticsearch-5.5.0 中的配置 path.data 指向原来的数据路径 即 path.data: /usr/local/src/elasticsearch-2.4.6/data 注意: elasticsearch-5.5.0 需要将jdk版本升级到1.8...

晨猫 ⋅ 36分钟前 ⋅ 1

lvm讲解 磁盘故障小案例

1

oschina130111 ⋅ 40分钟前 ⋅ 0

那些提升开发人员工作效率的在线工具

本文转载自公众号 Hollis 作为一个Java开发人员,经常要和各种各样的工具打交道,除了我们常用的IDE工具以外,其实还有很多工具是我们在日常开发及学习过程中要经常使用到的。 Hollis偏爱使用...

时刻在奔跑 ⋅ 53分钟前 ⋅ 0

restful风格 实现DELETE PUT请求 的web.xml的配置

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframe......

泉天下 ⋅ 58分钟前 ⋅ 0

Shell数组

Shell数组 Shell在编程方面比Windows批处理强大很多,无论是在循环、运算。 bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数...

蜗牛奔跑 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部