服务拆分完成之后,各服务自治,共同支撑支付平台的运行。
我们把视角由整体支付平台的架构,缩小到交易服务这块。从交易服务的业务出发,来讨论交易服务应该如何保证高可用。
一、应用服务的高可用
1、横向扩展+负载均衡
说到高可用,我们的第一想法就是:鸡蛋不要放在同一个篮子里。准备多个篮子,每个篮子里都有鸡蛋,这样即便某个篮子出问题了,也能从其他篮子里获取到鸡蛋。所以高可用最朴实的方案就是:横向扩展,通过部署多个实例来保证服务的不间断。
横向扩展对于应用是有要求的。只有无状态的应用,才能做到低成本的横向扩展,不用为扩展而作出改造。
在第二章服务拆分中,我们引入了网关和服务注册/发现两个组件。这两个组件都维护了服务提供方的列表,能直接或间接的做到负载均衡。当某个服务无法到达的时候,网关和服务注册/发现组件都能感知异常,将它们从服务提供方列表中剔除,确保没有请求分发到有问题的服务提供方,从而保证请求的有效性。
2、动态扩容
应用服务可以通过横向扩展来达到高可用,但是扩到多少个节点能满足业务需求呢?这个没有固定答案,因为不同业务、不同时候的业务量是不一样的。
能够确定的是,某个版本的某个应用,最高支持的并发量,是可以通过压力测试来得到一个值的。
以这个值为单位,结合预期的业务并发量,用简单的除法,就能大致得出需要扩展到多少个节点。 业务上线之后,根据各应用的硬件监控情况、接口响应的时间分布,再对节点扩容做出调整。
3、异步解藕
在不影响客户体验的场景,将内部业务调用由同步直接调用,改造为通过消息队列异步触发。这样依赖,服务调用方和服务提供方通过消息队列做到了解藕,即便服务提供方存在系统异常无法正常工作,也不影响服务调用方的对外响应。等到服务提供方恢复正常,就会从队列里消费数据,进行业务处理。最终对外表现正常,不影响客户感知。
当然,这会导致系统设计更复杂,因为引入了消息队列组件。如果消息组件出问题,也可能会对业务产生影响。不过,消息队列组件,也是有它自身的高可用设计的。总的来说,大家都好才是真的好。
4、过载保护
过载保护其实是委婉的说法,直白一点,就是:老子扛不住了不干了。
限流
网关是流量入口,并且具备限流功能。当后端服务无法承受全部流量时,可以通过网关配置,拦截部分流量,不将请求分发到后端服务,由网关直接返回指定的提示,以保障后端服务正常。待后端服务准备完成后,再放开流量。
熔断
熔断主要用在内部服务的调用依赖之间。服务调用方发现业务强依赖的服务提供方的接口响应存在异常的时候,可以不再调用该服务,直接进入快速失败流程。这个在第二章中也有提到。
技术选型
常见组件为Hystrix、Sentinel、resilience4j。它们大致的差别如下:
该图来源于sentinel官方:在生产环境中使用 Sentinel · alibaba/Sentinel Wiki · GitHub
显然,alibaba将sentinel设计的很庞大,主要表现在对于集群的支持以及控制台的动态调控。阿里官方提供sentinel的服务,当然是收费的。当然我们也可以借由阿里开放的API来完成二次开发,如果有需要且有这个能力的话。
如果我们只是防止微服务雪崩,是不用考虑集群场景的,在单个应用服务中调用下游接口的时候,引入熔断降级即可。这样看来,三者都做到了这个支持。
5、业务设计容错
重试
在某些业务场景,可以通过重试来增加业务的成功率。如接口调用、数据库使用乐观锁的更新。重试次数可以通过配置中心动态配置,平衡重试带来的业务成功率和消耗的系统资源。
服务降级
服务降级是一个纯业务场景的设计,如果A方案突然不通,是否可以使用一个性能、效果都稍逊的B方案临时替代,等A方案完全正常的时候,再切回去。当然,这些方案都应该是需要尽量达到产品经理的设计预期。最后,必要时候,提供有损的服务,也不是不可以。
二、数据存储的高可用
数据在存储层面的高可用,都是通过冗余来实现的——还是搞多个篮子,即部署多个节点。接收数据写请求的节点,要将数据同步到其他节点。
1、副本集群存储
当数据体量不是太多,我们可以将数据整体复制到不同的存储节点。这样一来,同一份数据能够存储在多个节点上。如果其中一个节点故障,可以从其他的节点上读取到冗余的数据,达到数据的高可用。
主从复制
主从复制图示如下:
只有一个主节点,接收数据请求。写请求在主节点完成后,将通过一定的技术手段同步到另一个节点,即从节点。 从节点如果只做数据同步,不对外提供任何服务,则只是完成数据备份。当然从节点也可以对外提供读服务,实现读写分离。
主主复制
主主复制图示如下:
多个主节点都接受数据请求,并且将自身接受的写请求处理完成后,同步到另外的主节点。
2、分布式存储
分布式存储图示如下:
当数据量非常大的时候,单节点可能无法存储全量数据,这个时候就可以考虑将数据分片,将分片数据存储在不同的存储节点。当然,这就需要有一个协调者,在管理这些分片。如HDFS、ElasticSearch这些分布式系统都是采用了这样的思想。
当然,各个分片数据也是可以采用副本的方式达到冗余的效果,来实现高可用。如下:
三、运维部署的高可用
1、环境隔离
应用部署环境一般分为开发环境、测试环境、灰度环境、客户调试环境、生产环境。
其中开发环境、测试环境都设计为内部使用,测试环境和灰度环境即便出了问题,也不会有生产影响。
灰度环境为生产环境的预演练场所,流量要做到严格可控,即便出问题,也要做到只影响灰度客户。
客户调试环境,是让对接方做对接调试使用。调试过程中,对方的请求可能是不可预料的,产生的后果也是不可预料的。
2、部署维度
扩展应用服务节点的时候,可以考虑将节点分散到不通的维度。物理机、机房、地域等,都是可以参考的维度。不同维度的扩展,投入的资源不同,带来的成效也不一样。
3、监控告警
对不同的场景设计不同的监控指标,超过指标后即进行不同级别的告警,通过钉盯消息、短信、电话等通知相应的负责人进行查证处理。处理结果需要集中反馈,用来完善监控体系,来避免无意义的告警,也不漏告警。
常见监控常见如下:
系统监控:CPU使用率、内存使用率、硬盘使用率
业务监控:接口响应时间、业务成功率、应用主动告警
四、补偿:异常记录及后续处理
说到补偿,则一定是出问题之后了。是的,我将善后也当作了如何保证高可用的一环。谁还没擦过屁股呢?
生产环境出问题之后,业务中断,需要将整个业务处理掉的。一般有两个选择:一是将中断中的业务的回滚,让客户重新发起操作;第二个就是重试完成该业务。
不同业务场景的处理方式不一样。但是有一点是共通的,我们必须将出现问题的关键节点的信息记录下来。
日志记录是最简单的方式。记录的内容也是有规律可循的。如果是接口请求场景,则记录接口信息、请求参数。如果是异步发送场景,则记录发送目标队列,发送消息。然后想办法再次触发接口请求或异步消息发送。
当然,咱们也可以汇总一些异常场景,专门开发一个故障处置系统——应该是算KPI的吧?
最后
好吧。全是理论知识,下一章咱们基于交易服务,制定一个目标,进行高可用设计的实践。