当前位置: 首页 > 科技观察

工作十年,说说我的高可用架构和系统设计心得

时间:2023-03-16 13:04:40 科技观察

1.高可用架构和系统设计思路可用性和高可用概念可用性是一个量化指标,计算公式在维基百科中有描述:基于系统损坏、不可用时间和从非运行状态到运行状态的时间,与系统总运行时间的比较。业界一般用几个9来表示可用性指标,应用可用性的一般衡量标准是三个9到五个9;通常,我们的系统至少需要4个9(99.99%)的可用性才能谈及高可用性。高可用性的定义:(来自维基百科)是一个IT术语,指系统不间断地执行其功能的能力,代表系统的可用性程度,是系统设计的标准之一。一个服务是不可能做到100%可用的,所以要完善我们的高可用设计,就要尽量提高我们服务的可用性,提高可用性指标。一句话概括就是:高可用就是让我们的服务在任何情况下都尽可能好地对外提供服务。高可用系统设计思路高可用系统的设计需要一个相对科学的工程管理套路。必须从产品、开发、运维、基础设施等各个方面来考虑和设计。高可用系统的设计思路包括但不限于:做好研发规范。该系统由研发人员设计和编码。因此,首先,研发层面要有规范和标准,做好产能规划和评估。基本认知有助于合理的架构设计和演进。在服务层面做好高可用,主要是负载均衡、弹性伸缩、异步解耦、容错、过载保护等。在存储层面做好高可用,主要是冗余备份(热备份)、冷备份)、故障转移(确认、转移、恢复)等。在运维层面做好高可用,主要包括发布测试、监控告警、容灾、故障演练等。在产品层面做好高可用,主要是自下而上的策略。做好应急预案,主要是在出现问题后如何快速恢复,不至于扩大我们的异常情况。2.研发规范层次方案设计和编码规范研发规范层次这是一个很容易被大家忽略的点。但是我们所有的设计都是由研发人员完成的,从设计文档到编码再到发布上线。所以,在研发层面,也有一个标准化的流程和套路,让我们更好的开发和维护一个高可用的系统:在设计阶段,将相关方案设计文档的模板和大纲标准化,让团队可以维护统一。可以参考我的文章《??技术方案设计模板??》方案设计必审。在我们团队,新项目要review,重构项目要review,大规模的系统优化或升级要review,其他一般的研发工作量超过一周也要review。的。在编码阶段,不要随便登录。要访问远程日志,您需要能够跟踪分布式链接。写好代码后,需要有一定的单机测试,以保证代码的健壮性。同时也可以保证我们后期调整逻辑或者优化的时候,代码能够得到保障。稳定性包括增量覆盖和全覆盖。具体覆盖范围可根据团队内部实际情况确定。在我们的团队中,设定的规则是50%的覆盖率。项目布局目录结构规范,团队内部保持统一,遵循团队内部的代码规范,尽量简洁。一般公司都有对应语言的规范。如果没有,请参考官方规格。代码规范可以大大减少错误并提高可用性。执行代码规范单测覆盖率日志规范发布阶段,参考后面运维部署级灰度发布和接口测试相关说明的章节容量规划和评估容量评估是指我们要评估好,我们的系统,就是处理一个什么规模的业务,业务请求量的平均值和峰值是什么级别。如果是新系统,那么需要先根据产品和运营同学对业务有个大概的预估,然后开发同学再根据产品给的数据做详细的评估。如果是老系统,那么可以根据历史数据进行评估。评估的时候,需要从整体的角度看整体的量级,然后细化到每个子业务模块必须承载的量级。容量规划就是我们在设计系统的时候,要能够初步规划出我们的系统能够承受的数量级,比如10万或者百万请求,或者更多。不同量级对应的系统架构设计会完全不同,尤其是达到千万级、亿级量级时,在架构设计上会有很多考量。当然,这里需要注意的是,我们不需要一上来就设计一个远远超过我们当前业务真实流量的系统,根据实际业务情况来设计。同时,容量规划也涉及到我们系统的上下游模块、依赖的存储、依赖的第三方服务需要多少资源,我们需要相对可量化的数据。在容量规划阶段,我们需要更多的依靠自己和团队的经验,比如了解我们的日志、redis、rpc接口、服务框架等的性能,然后根据各个组件的性能进行综合评估您设计的系统的整体性能。在容量评估和容量规划之后,我们还需要做一件事,就是性能压测,最好是全链路压测。性能压力测试的目的是确保您的容量规划是准确的。比如我设计的这个系统就是设计来承受千万级请求的。事实上,它真的能承受得住吗?上线前首先要根据经验判断,然后还要经过性能压测才能得出准确的结论。性能压测需要关注的指标有很多,但重点是两个指标,一个是QPS,一个是响应耗时。要确保压力测试的结果符合预期。压测的步骤可以先由个别模块进行,最后如果情况允许最好进行全链路压测。QPS预估(漏斗型)QPS预估(漏斗型)指的是一个真实的请求,从接入层开始,我们整个系统经过了哪些层和模块,然后从各个层级的QPS来看请求链接,级别越低,越低的级别要逐渐降低,因为每经过一个级别,可能会有一些请求被各种条件过滤掉。比如进入活动页面查看商品详情再下单后,先进入活动页面,所有请求都会被访问;那么只有部分用户会查询产品详情;最后,这些查看商品详情的用户,只会有一部分用户会下单,所以这里会形成一个漏斗,从上层模块到下层模块的量级要逐渐减小。QPS预估(漏斗式)需要我们根据请求的层级和模块来构建我们的预估漏斗模型,然后预估每个层级的量级,包括但不限于服务、接口、分布式缓存等层级来预估,最终构成我们完整的QPS漏斗模型。3、应用服务层面的无状态和负载均衡的设计,一般都需要系统的高可用。我们应用服务的常规设计是无状态的,这意味着我们可以部署多个实例来提高我们系统的性能。可用性,以及这些多个实例之间的流量分配,需要依赖于我们的负载均衡能力。无状态+负载均衡不仅可以提高我们系统的并发性,还可以提高我们系统的可用性。如果我们的业务服务是使用各种微服务框架开发的,那么微服务框架很有可能会包含服务发现和负载均衡的能力。这是一套完整的流程,包括服务注册和发现、负载均衡、健康检查和自动剔除。当我们的任何一个服务实例出现故障时,它会被自动移除,而当我们添加新的服务实例时,它会自动添加以提供服务。如果我们不是使用微服务框架来开发,那么我们就需要依赖负载均衡代理服务,比如LVS、Nginx来帮助我们实现负载均衡。弹性伸缩设计弹性伸缩设计是应对突如其来的流量高峰非常有效的手段之一,也是保证我们服务可用性的必要手段。弹性伸缩是针对我们的无状态应用服务。因为服务是无状态的,可以根据请求量的大小随时扩缩容。如果流量很大,可以扩展容量来处理大量的请求。如果流量小的时候就该收缩以减少资源消耗。如何实现弹性伸缩?现阶段处于云原生时代,大部分公司采用容器化(K8s)部署。基于这种情况,弹性伸缩是很容易的。只需要配置K8s的弹性条件根据CPU使用率自动调整即可实现。如果不是容器化部署,而是物理机部署,那么要实现弹性伸缩,就需要有公司内部的基础设施能力,可以监控运行平台上服务的CPU或者QPS。比例会自动伸缩,和K8s的弹性原理一样,但是需要自己实现。异步解耦和调峰设计(消息队列)如果我们的系统要做到高可用,那么从架构层面来说,必须分层、分模块设计。分层和模块之后,模块之间的关系,也可以进行异步处理和解耦处理。目的是互相不影响。通过异步和解耦,我们的架构可以大大提高可用性。架构层面异步解耦的方式是使用消息队列(比如常见的Kafka),同时消息队列还有削峰的作用,两者都可以提高我们架构的可用性:异步解耦:使用消息队列后,可以将同步过程转换为异步过程。消息生成者和消费者都只需要与消息队列进行交互即可。这样既进行了异步处理,又隔离了消息的生成者和消费者。异步处理的好处是无论消息后续处理的业务服务是否ok,只要消息队列未满,就可以对外提供服务,消费者可以根据需要消费消息到自己的处理能力,然后处理它。解耦的好处是,如果消费者出现异常,不会影响生产者,仍然可以对外提供服务。消息消费者恢复后,可以继续从消息队列中消费数据,然后执行业务逻辑。可以达到调峰的作用。当并发很高,甚至流量爆发的时候,只要消息生产者能够将消息写入消息队列,那么消息就不会丢失,后续的处理逻辑就可以慢慢的去消息队列了消耗这些突发的流量数据。这样就不会因为突如其来的流量而让整个系统宕机。容错与容错设计任何服务都难免会失败,不可能100%可用。在服务在线运行的过程中,总会出现各种意想不到的问题,导致你的服务失败,所以业界评价可用性SLA是说有多少个9,比如4个9(99.99%)可用性。为此,我们的设计方案遵循“为故障而设计”的设计原则,设计一个容错系统,需要尽快返回并自动修复。详情如下。遵循快速失败原则。failfast原则是指当我们主流程的任何一步出现问题时,应该快速合理地结束整个流程,尽快返回错误,而不是坐等负面影响被解决处理。有能力保护自己。当我们所依赖的其他服务出现问题时,我们必须第一时间采取降级、掩盖等各种异常保护措施,避免连锁反应导致整个服务完全不可用。比如,当我们依赖的数据存储出现问题时,我们不能一直重试,数据就会完全不可用。过载保护设计(限流、熔断、降级)系统不能高可用的一个重要原因是我们的系统经常会出现突发流量,导致我们的服务超负荷运行。这个时候,首先要做的当然是快速扩张,要提前储备一定的冗余。还有一种情况,即使我们扩容了,还是会超载,比如超过下游依赖存储的最大容量,或者超过下游依赖第三方服务的最大容量。那么这个时候就需要实现我们的过载保护策略,主要包括限流、熔断、降级。过载保护是保证部分服务可用,而不是整个服务完全不可用。限制。限流是指对进入系统的请求进行限流处理。如果请求量超过我们系统的最大处理能力或超过我们指定的处理能力,那么该请求将被直接拒绝。通过这种方式丢弃一些请求,可以保证整个系统有一定的可用性,而不至于整个系统完全不可用。如何判断超出最大处理能力?一般根据QPS来判断。如果QPS超过阈值,则直接拒绝请求。限流有很多详细的策略,比如接口限流、服务限流、用户限流等。保险丝。熔断器、开路(opencircuit)的值是为了限制故障影响的范围。我们希望控制、减少或中断与故障系统的通信,以减轻故障系统的负载,有利于系统的恢复。一般我们的服务都会有很多的下游依赖。如果下游依赖的服务出现问题,比如开始超时,甚至响应很慢,如果我们什么都不做,就会导致我们整个请求卡住,超时。那么我们的业务服务将无法对外提供任何正常的功能。为此,融合策略可以解决这个问题。熔断就是当我们所依赖的下游服务出现问题时,我们可以快速的进行熔断(不需要发起请求),这样我们的业务服务至少可以提供一些功能。熔断器设计至少需要包括三个部分:熔断器请求判断机制算法、熔断器回收、熔断器告警。降级就是我们把系统的核心功能和非核心功能划分开来,然后当我们的系统超过最大处理能力的时候,我们直接关闭非核心功能,从而保证核心功能的可用性。关闭非核心功能后,我们的系统可以释放一些资源,让我们有资源去处理核心功能。熔断和降级这两种策略看起来很像,字面意思是应该迅速拒绝请求。但它们是二维设计。降级的目的是处理系统本身的故障,熔断的目的是处理我们系统所依赖的外部服务的故障。4、存储层面在现在的互联网时代,应用服务基本都是无状态的,所以应用服务的高可用比较简单,但是数据存储的高可用相对复杂一些,因为数据是可用的。状态,我们来分析下如何保证数据存储的高可用。存储层面高可用解决方案的本质是通过数据冗余实现高可用。将数据复制到多个存储介质可以有效避免数据丢失,提高并发能力,因为数据是可用的。因此,它比服务的高可用要复杂得多,主要体现在以下几个方面:如何复制数据?每个节点的职责是什么?如何处理复制延迟?如何处理复制中断?存储高可用的常见解决方案有两种:集群存储和分布式存储。大部分行业都是围绕这些构建的,或者做相关的衍生和扩展。集群存储(集中式存储)集群是逻辑上处理同一个任务的机器的集合,可以属于同一个机房??,也可以属于不同的机房。集群存储就是将多台机器上存储的数据组合在一起,对外形成一个统一的系统。集群存储适用于业务存储量一般的场景,对于常规的业务数据存储,集群存储一般足够了。现在我们业务数据存储一般默认使用集群的方式,比如Redis、MySQL等存储类型。一般大中型互联网公司都默认使用集群存储方式。集群存储就是我们常说的一主多备或者一主多从的架构。数据通过master写入,数据一般通过slave读取。集群存储主要需要考虑以下几个问题:主机如何将数据复制到备机(从机)数据是通过主机写入的,所以数据同步到备机(从机)就是将数据复制到备机(从机)。还需要考虑主备之间同步的时间延迟。backup(slave)如何检测master的状态master发生故障后,backup(slave)如何切换为主?在主从架构中,如果master发生故障,备份(slave)可以直接切换到master1。主从复制主备复制是最常见也是最简单的存储高可用方案。几乎所有的存储系统都提供了主备复制功能,比如MySQL、Redis、MongoDB。主备架构中的“备机”主要作为备份,不承担实际的业务读写操作。如果要将备用机更改为主机,则需要手动操作。所以一般的使用场景都是用在一些内部的后台管理系统中。2.Master-slavereplicationmaster-slavereplication和master-slavereplication虽然只是一字之差,但是是不同的设计思路。“Slave”是“follower、slave”的意思,“standby”是备份的意思。“从”机制是为了工作,所以它负责数据的“读取”操作。一般主机负责读写操作,从机只负责读操作,不负责写操作。3、主从切换主从复制和主从复制方案有两个共同的问题:主机出现故障后,无法进行写操作。如果无法恢复主机,则需要手动分配新的主机角色。主从交换机(master-standbyswitch)就是为了解决这两个问题而产生的。具体设计是在原有方案的基础上增加“自动切换”的能力。或者从奴隶切换到主人。这是比较实用的解决方案之一,因为我们必须要有一个机制来保证在master出现异常后,slave能够自动切换到master。4.Master-master复制Master-master复制就是两台机器都是主机,它们互相复制数据。客户端可以任意选择其中一台机器进行读写操作。如果采用master-master复制架构,必须保证数据的双向复制。这个要求比较高。分布式存储集群是指将若干台服务器聚合起来,实现相同的业务。分布式是指不同的业务分布在不同的地方,分布式系统中的每个节点都可以作为一个集群。分布式存储就是通过网络利用企业内每台机器上的磁盘空间,将这些分散的存储资源形成一个虚拟的存储设备,将数据分散存储在企业的各个角落。分布式存储中的每台服务器都可以处理读写请求,因此没有像集中式存储中主机负责写入的角色。但是在分布式存储中,必须有一个角色负责执行数据分配算法。这个角色可以是独立的服务器,也可以是集群自己选举出来的服务器。分布式存储适用于超大规模的数据存储,可用于业务数据量巨大的场景。常见的分布式存储如Hadoop(HDFS)、HBase、Elasticsearch等。5、产品层面产品层面的高可用架构解决方案,基本上是指我们底线的产品策略。降级/限流策略更多的是从后端业务服务和架构的设计上考虑相关的解决方案。这里所说的底线策略也可以称为弹性降级策略,更多是在产品层面考虑的。比如当我们的页面无法获取数据或者无法访问时,如何友好的告知用户,比如【稍后重试】。比如当我们的真实页面无法访问时,产品需要提供一个默认页面。如果后台获取不到真实数据,则直接渲染默认页面。比如服务器需要停机维护,那么在产品层面给出一个停机页面,所有用户只会弹出停机页面,不会去请求彩票产品等后台服务。defaultbottom-lineproduct...六、运维部署级开发阶段-灰度发布、接口测试设计、灰度发布、接口测试、接口拨号测试系列设计包括但都是不限于:灰度发布,当我们的服务发布上线的时候,肯定有一个灰度的过程,先灰度1-2个服务Instances,然后一步步观察,如果一切ok,再逐步灰度化,直到所有实例释放完毕,接口测试完成。每次发布上线服务时,服务提供的各种接口都必须有接口测试用例,接口测试用例运行完成后,服务才能发布上线。目的是检查我们对外提供的接口是否正常,避免在服务发布上线后才进行灰度发布和接口测试问题。一般大公司都会有相关的DevOps流程来保证。发展阶段——监控与报警设计监控与报警的设计对于大公司来说根本不是问题,因为必须要有相对敬业的一群人来搭建这个基础能力,才会有相应的配套系统和业务开发同学.只需要配置或使用它。如果公司内部没有相关的基础设施,那么就需要单独对接相应的系统。监控系统一般监控系统的开源方案包括但不限于:ELK(Elasticsearch,Logstash,Kibana)日志收集和分析我们的日志记录不能存储在本地,因为微服务之后,日志分散在很多机器上所以,有必须是一个远程日志系统。ELK是最好的选择。Prometheus监控采集可以监控各种系统级的指标,包括一些自定义的业务指标。OpenTracing分布式全链路跟踪一个请求的上下游。多服务,我们如何把一个请求的上下游全部连接起来,那么就要靠OpenTracing,它可以把一个请求下的所有环节都连接起来,并且有详细的记录OpenTelemetry可观察系统标准的最新标准,统一,能力通过收集跟踪数据(Traces)、指标数据(Metrics)和日志数据(Logs)来观察分布式系统的状态。我们会依赖开源系统进行自建或扩容,甚至直接使用,然后我们的监控指标一般包括:基础设施层监控:主要针对网络、交换机、路由器等底层基础设施设备。如果这些设备出现问题,依赖它们的业务服务肯定无法稳定提供服务。我们常见的核心监控指标包括网络流量(进出)、网络丢包、网络连接数等。操作系统层的监控:这里需要包括物理机和容器。常用核心指标监控包括CPU使用率、内存使用率、磁盘IO、网络带宽。应用服务层的监控:这里会有很多指标,比如调用请求数、被调用请求数、接口成功率、接口失败率、响应时间(平均值、P99、P95等)等等。业务内部自定义监控:每个业务服务都有自己的自定义监控指标。比如这里的电商系统:浏览、支付、发货等各种情况下的业务指标监控。终端用户层面的监控:之前的监控更多是在内部系统层面,但是当用户真正访问页面的时候,有外部的情况,比如用户真正获取页面所用的时间等信息数据和打开页面所用的时间也很重要,但这一般需要客户端或前端进行统计。报警系统这些系统连接起来后,只做监控和统计的作用。当出现问题时,需要实时报警,所以也就有了实时报警系统。如果没有实时告警,我们将无法快速感知,从而无法快速处理,将对我们的业务造成重大故障和灾难。报警设计需要包括:实时:实现秒级监控;全面性:涵盖所有系统服务;实用性:预警分为多级,监测人员可以方便实用地根据预警的严重程度做出准确的决策;多样性:预警方式提供推拉方式,包括短信、邮件、可视化界面,便于监控人员及时发现问题。被外部恶意攻击一般有几种策略:在公司级流量入口做好统一的防刷和鉴权能力,比如统一接入层封装到业务服务里面,做一个做好相关业务认证工作。权限,比如登录状态信息,比如在逻辑部署阶段加入业务鉴权——多机房部署(容灾设计),一般的高可用策略是针对一个机房的服务级别设计的,但是如果整个机房不可使用,如地震、火灾、光纤被挖等。..那么遇到这种情况我们应该怎么办呢?这就需要我们的服务和存储能够进行容灾。常见的容灾方案是部署多个机房。多机房部署服务还是比较容易的,因为我们的服务是无状态的,所以只要名字服务能发现不同机房的服务就可以调用了。这里需要注意的是名称服务(或负载均衡服务)要能够有就近访问的能力。存储的多机房部署难度有点大,因为存储是有状态的,不同机房部署涉及到存储的同步和复制。如果条件不允许,我们保证业务服务可以部署在多个机房。在线操作阶段-故障演练(混沌实验)故障演练是大公司常用的方法;在业界,Netflix早在2010年就构建了混沌实验工具ChaosMonkey,该混沌实验项目对提高复杂分布式系统的健壮性很有帮助。性和可靠性发挥了重要作用。一个简单的故障演练就是在机房模拟停电、网络中断、服务挂断等场景,然后看看我们整个系统是否正常运行。系统需要参考混沌实验项目进行详细的规划设计。这是一个比较大的项目,效果也不错,但是开发这种基础设施需要大量的人力。上线运行阶段-接口拨测系列设计接口拨测,类似巡检,即服务上线后,每隔一段时间(比如5s)调用后台的各个接口,如果有则告警界面异常,一般会有相关的配套设施来提供相关的能力实现,如果没有提供,那么我们可以自己写一个接口拨测(检查)服务,定时调用重要的接口。7、我们在异常应急响应层面做了那么多保障,但毕竟经不起线上各种异常情况。如果出现问题,我们的服务出现异常,无法提供服务,我们还是需要最后一根稻草。它是一种将异常服务损失降到最低的应急预案。应急预案就是我们需要提前做好计划。我们的业务系统在各个层面出现问题后,都需要尽快恢复,并制定相关的规则和流程。当出现异常情况时,我们可以按照现有程序进行。这样可以避免出现问题后,恐慌导致事态扩大。