@[toc] 这篇文章并没有具体的业务实现,而只是对于如何设计一个高可用,稳定的网关列举出了一些思考。如果对概念没有兴趣的可以直接略过这篇文章。
高可用分析
高可用(High Availability,HA)是指一个系统或服务在面临各种故障、错误或恶劣条件时仍然能够保持连续运行和可用性的能力。高可用性是计算机系统、网络和应用程序设计中的一个重要目标,它旨在减少系统停机时间,确保业务连续性,以满足用户和客户的需求。 为什么需要高可用我们可以从如下几个点切入:
-
业务连续性:在今天的数字化世界中,许多组织依赖于计算机系统和网络来支持其核心业务。系统的停机时间可能会导致业务中断,损害声誉,造成财务损失,以及失去客户信任。高可用性确保系统持续可用,有助于维护业务连续性。
-
用户体验:用户期望能够随时随地访问应用程序和服务。如果系统经常不可用或响应时间过长,用户体验将受到影响,用户可能会寻找替代方案。高可用性可以改善用户体验,提高用户满意度。
-
故障容忍:硬件故障、软件错误、自然灾害或人为错误可能导致系统中断。高可用性设计可以使系统能够容忍这些故障,自动切换到备用设备或数据中心,以保持连续性。
-
数据保护:数据是组织的重要资产,丢失或损坏数据可能对业务造成重大损害。高可用性系统通常采用数据冗余和备份策略,以确保数据的完整性和可用性。
而网关作为请求的第一道门户,是项目中使用最频繁的服务之一,所以网关的可用性极大程度的决定了我们服务的可靠性,也一定程度上影响了用户的使用体验。 因此,对于网关的设计,我们需要尽可能的保证其高可用,而对于高可用,又可以从如下几个点切入实现:
-
硬件冗余:使用冗余组件,如双电源供应、热插拔硬件、磁盘阵列等,以减少硬件故障对系统的影响。
-
软件冗余:使用负载均衡和故障切换机制,确保应用程序和服务能够在多个服务器上运行,当一个服务器出现故障时,流量可以切换到另一个服务器上。
-
数据备份和恢复:实施定期的数据备份策略,以确保数据的安全性和可用性。备份数据可以用于在灾难发生时恢复系统。
-
监控和自动化:使用监控工具来实时监测系统性能和可用性。自动化系统管理和故障恢复操作可以帮助降低系统停机时间。
-
多数据中心部署:采用跨多个地理位置的数据中心部署,以提高系统的容错能力,即使一个数据中心发生故障,其他数据中心也能继续提供服务。
这里比较好理解的就是软件冗余了,也就是集群方式部署我们的服务。这样子,假设我们的服务正常运行的概率为90%,那么我们如果有三个服务实例组成的集群,那么高可用性就是:1- 0.1 * 0.1 * 0.1 = 99.9%。
软件架构
所以对于我们的一个服务,服务部署的情况可能就是这样子的,如下是部署架构 为了进一步实现高可用,我们还需要解决人工手动切换崩溃服务的情况,也就是实现主备热切换,在服务出现故障时自动实现切换。 我们可以通过定期的心跳检测,来判断服务是否存活,如果服务并不存活,那么我们将服务从注册中心等列表中剔除,使其暂时不会被访问到,然后我们进行对宕机服务的维修维护,并重新启动该项目。
心跳检测
自动恢复
同理的,当我们的服务已经出现问题导致下线了,我们希望服务能够自动启动,而不是让我们手动再去启动,我们可以通过脚本等方式让服务自动启动一定的次数,如果还不行我们就人工介入。
熔断降级
同时,为了进一步的实现网关的高可用,我们还需要实现熔断这个功能。 这里我们可以参考hystrix的实现思路。 hystrix提供了两个概念,分别是熔断和降级,这也是我面试小米的时候遇到的一个问题。 千万不要混淆了这两个概念。 熔断(Circuit Breaker)用于防止在分布式系统中不断请求故障的服务,它会在一段时间内停止对故障服务的请求,以允许服务有时间来恢复。这有助于防止系统因不断请求故障服务而耗尽资源,保护系统的可用性。
降级(Fallback)是Hystrix的另一个关键特性,它允许您定义备选的操作或响应,以在主要操作(通常是外部服务的调用)失败或超时时提供给用户或系统。降级操作通常是一种简化的、不会失败的操作,它可以确保系统在主要操作发生问题时仍然能够提供某种程度的服务。通过降级,您可以提供更好的用户体验,即使主要操作出现问题,也可以提供有用的信息或服务。
接口重试
并不是说失败一次可能就是真的不行,我们可以进行一定次数的重试。
隔离
隔离是指将系统或资源分割开,系统隔离是为了在系统发生故障时,能限定传播范围和影响范围,即发生故障后不会出现滚雪球效应,从而保证只有出问题的服务不可用,其他服务还是可用的。
-
网络安全:通过在不同网络之间放置网关,可以限制潜在威胁、恶意流量或未经授权的访问进入受保护的网络。这有助于减少网络攻击的风险,如入侵、恶意软件传播和数据泄漏。
-
访问控制:网关隔离可用于管理网络访问,以确保只有经过授权的用户或设备能够访问特定的网络资源。这有助于保护敏感数据和系统免受未经授权的访问。
-
网络性能:通过分隔网络,可以改善网络性能和流量管理。例如,不同部门或应用程序可以位于不同的子网中,以防止它们之间的流量冲突,从而提高整体网络效率。
-
隔离问题排查:将网络分成较小的区域可以更容易地识别和解决网络问题。如果问题发生在一个特定的子网中,管理员可以更快地确定问题的根本原因,而不会影响整个网络。
隔离的方式也比较多,线程隔离、进程隔离、集群隔离、机房隔离、读写隔离、快慢隔离、动静隔离、爬虫隔离。 我们常用的ThreadLocal就是线程隔离的,我们也主要分析和讲解这点。 例如我们的服务中,可以将服务划分为核心业务和非核心业务,那么我们就可以对这两个业务进行线程池上面的隔离。 也就是核心线程池和非核心线程池让他们来分别提供线程支持。
压测和预案
"压测" 是指对一个系统、应用程序或网络进行负载测试,以确定其在不同负载条件下的性能、稳定性和可靠性。压测通常通过模拟大量用户或请求,以评估系统的性能极限,以确保系统在实际使用中能够满足性能要求。压测可以帮助发现系统中的性能瓶颈、内存泄漏、响应时间问题以及其他性能相关的问题。这种测试是在开发、测试和生产部署之前进行的,以确保系统能够在不同工作负载条件下正常运行。
"预案"(也称为应急预案或危机管理计划)是一种文档或计划,旨在规定在紧急情况下采取的行动和程序。它包括应对不同类型的紧急事件或危机的方法,以最大程度地减小损失、确保员工和客户的安全,以及恢复正常业务运营。预案通常包括如何应对火灾、自然灾害、网络攻击、数据泄漏、供应链中断等各种紧急情况。预案通常由组织的紧急管理团队或危机管理团队编制,并需要定期更新和测试,以确保其有效性。
在技术领域,压测和预案也可以有关联。压测可以帮助组织确定其系统在应对高负载或紧急情况时的性能表现,而预案可以规定在技术故障、网络攻击或其他技术相关的危机情况下采取的行动步骤。这些行动可能包括系统维护、恢复备份数据、通知相关方或采取其他技术措施。因此,压测和预案都是确保系统和组织在各种情况下能够正常运行和管理的重要工具和计划。
多机房灾备以及双活数据中心
多机房灾备(Disaster Recovery, DR)和双活数据中心(Active-Active Data Centers)是两种用于提高系统和应用程序高可用性的策略。它们结合了多个数据中心或机房,以在发生故障或灾难情况下保障服务的连续可用性。以下是它们如何保障高可用性的方式:
多机房灾备(Disaster Recovery):
-
数据冗余:数据通常会复制到不同机房,以确保数据备份和恢复的可用性。这可以通过数据库复制、备份和异地存储等方法来实现。
-
自动故障切换:多机房灾备通常会使用自动故障切换系统,当一个数据中心发生故障时,它可以自动将流量重定向到备用数据中心,以保持服务的连续性。
-
热备份:备用数据中心通常是热备份,即它们已经准备好接收流量并持续运行,以减少切换时间。
-
定期演练:组织会定期进行灾难恢复演练,以确保灾难发生时,人员明白如何切换到备用数据中心,以及系统恢复流程是否有效。
双活数据中心(Active-Active Data Centers):
- 负载均衡:流量通常会分布到不同的数据中心,以确保负载均衡。这可以通过负载均衡器和DNS解析来实现。
- 数据同步:数据在多个数据中心之间实时同步,以确保数据的一致性。这可以通过数据库复制、文件同步、消息队列等机制来实现。
- 冗余基础设施:双活数据中心通常会有冗余的网络、存储、服务器等基础设施,以确保在一个数据中心出现故障时,流量可以无缝切换到另一个数据中心。
- 自动故障切换:当一个数据中心出现故障时,负载均衡器通常会自动将流量切换到另一个数据中心,以确保服务的连续性。
- 性能监控:实时性能监控系统可帮助检测故障和问题,并触发自动故障切换。
这里的多机房灾备的一种重要解决方案为:两地三中心,同城灾备和异地灾备。 两地三中心的意思是:生产中心,同城容灾中心,异地容灾中心。 这里的异地灾备就需要考虑合理的数据复制/备份技术。 对于这个数据备份方案,需要结合具体的通信时间以及用户最大可以容忍的恢复时间。
双活数据中心和容灾备份中心不太一样。 前者是一直处于工作状态,而后者是在出现故障时才进行使用。 当然,双活数据中心实现困难,国内目前并没有具体的实现方案,但是脑子里可以存一下这个知识点。
异常处理机制
异常处理机制能帮助我们定位问题,统计分析。 异常机制通常有两类,分为人和机器。 人:用户、运营、开发等 机器:根据程序状态码进行处理 异常预警方式很多,可以根据团队的需求进行处理。 比如简单的发送邮件、或者公司内部的IM系统进行通知。 稳妥一点也可以使用电话、短信等方式进行通知。 而网关作为请求的门户,也需要对异常进行处理。 网关需要做到如下异常处理:
- 异常码统一管理
- 异常处理进行收拢,具体业务应该关注业务本身
- 异常状态码对应的消息应该由网关统一处理
而对于异常处理,我们可以使用高容错重试的方式来处理。 重试,是提高系统容错能力的一种手段。
重试
重试有一定的优缺点。 有点是实现简单,并且如果是由于网络问题,在网络快速恢复之后重试是最好的解决方法。 当然,缺点就是重试会带来双倍的流量,冲击服务系统,有可能导致直接将服务冲垮。 解决方案就是主动计算服务的成功率,成功率过低,就直接不进行重试了。 那么这里的成功率其实就可以使用计数等方式进行计算了。
主备服务自动切换
单一的进行服务重试可能会出现一些问题。 那么我们是否可以考虑主动的进行主备服务之间的切换呢? 也就是当请求服务A失败之后,我们主动切换,将请求发送到服务B(备份)上,这样子就不会对A造成双倍的流量冲击了。但是这种切换,以及重试,都会导致用户的体验下降,因为响应时间增加了。同时主服务如果是因为流量过大承受不住,那么备份服务也大概率承受不住。 同时,大部分时候我们都是使用主服务,那么备份服务就存在了一种资源浪费的情况。 当然,如果备份的是订单支付等核心场景,备份依旧是很重要的。
动态剔除或恢复异常机器
所有服务与存储,无状态路由 无状态路由和有状态路由的区别 这样子做的好处是为了避免单点风险,避免某个服务挂了导致整个集群瘫痪的情况。 并且由于我们的日志监控等系统,一般数量也不会特别多,使用他们本身也并不就是完全可靠的。 所以我们这里使用无状态路由,直接将请求随机的分发到后台服务。
支持平行扩容。遇到大流量场景,支持加机器扩容 这种的实现方式就是在我们监测到大流量的时候,或者CPU飙升的情况,我们就可以通过前面设定好的功能进行扩容。
自动剔除异常机器 在我们的路由服务发现请求某个服务的异常率达到我们设定的值之后,我们就会将这个服务进行剔除。后续我们会发送一些试探性请求,如果它通过了试探,我们就会将这个服务在进行恢复。
超时时间的考虑
对于传统的情况,我们一般都会设定一个超时时间。 如果超时时间设置的过长,就会导致我们的工作线程长时间被阻塞占用,久而久之,越来越多的线程不可以使用,就会出现服务故障。 而如果设定的过短,又会导致没有处理完毕请求就直接超时,导致资源浪费和虽然处理成功,但是返回的却是处理失败。 所以我们将所有的服务按照一刀切的方式去设定一个超时时间肯定是不合理的,因此我们就需要考虑一些策略来解决这样子的问题。 目前我了解到的解决方向有: 1:快慢分离 字面意思,我们可以按照服务所需要处理的不同时间,分开部署,分开设置他们的超时时间。
2:解决同步阻塞等待 对于某些阻塞请求,比如下载IO资源等方式,就会导致系统中的资源长时间处于等待状态,无法响应新的请求,就会造成我们一定的资源浪费。 这里我们的解决方法可以使用IO多路复用,通过异步回调的方式来解决。其实我设计的网关本身就是使用CompletableFuture的方式来处理的,也就是异步处理。 这里异步处理只是提高了系统的吞吐率,但是用户的体验还是没有提示的。 这里其实还可以继续了解一下"协程"的概念。是一种类似于写同步代码的方式来实现异步回调的机制(公司工作要求转go)。
3:防重入 也就是幂等性处理。 因为如果你的请求等了很久还是没有处理完毕,用户很可能二次点击,那么就会造成请求重入,所以我们必须要做好幂等性。 解决方法就是分布式Redis、锁、可重入锁、唯一订单号配合bitmap等方式来解决。
服务设计
服务降级,自动屏蔽非核心分支异常 1: 针对服务核心环节,我们要重点监控,并设置较长的超时时间 2:针对服务非核心环境,我们设置较短的超时时间
服务解耦、物理隔离 1:服务分离、大服务拆为小服务 2:轻重分离,物理隔离
业务层面的容错,人为错误不可避免的。即使有很完善的监控系统,也不能保证就不出错,所以一就需要在系统的层面上去解决这个问题。 比如我们可以从业务上面进行解决,对于错误的配置信息,直接报错,这就需要开发一套智能的对不同业务进行配置检测的系统了。