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

为了做到微服务的高可用,鬼知道我出了多少张牌

时间:2023-03-20 15:13:36 科技观察

为了实现微服务的高可用,鬼知道我打了多少卡。知道我有多少卡要串联起来处理这些环节才能最终形成整个系统的高可用落地方案。图片来自Pexels什么是高可用定义什么是高可用时,可以先定义什么是不可用。一个网站需要经过几个链接才能将网站的内容最终呈现给用户,只要有一个链接失效,就可能导致网站页面无法访问,也就是网站不可用的情况。参考维基百科,看看Wiki是如何定义高可用性的:系统不间断地执行其功能的能力,代表了系统的可用性程度,是系统设计的标准之一。难点或重点在于“不间断”,需要提供7x24小时不间断、无异常的服务。为什么需要高可用性?一个对外提供服务的系统,需要硬件和软件的结合,但是我们的硬件总会出故障,软件总会有bug,硬件会逐渐老化,网络总会不稳定,软件也会越来越退化。更复杂和笨重。除了硬件和软件在本质上不能“不间断”之外,外部环境也可能导致服务中断,比如停电、地震、火灾、光纤被挖掘机切断等。这些影响可能更大。高可用的评估纬度在业界有一套比较知名的评估网站可用性的指标。N个9常用来量化可用性,可以直接映射到网站正常运行时间的百分比:我之前工作的一家互联网公司也是根据这个指标来定义可用性的,但是在实现过程中遇到了一些问题。比如一些服务升级或者数据迁移,明显可以半夜关机或者停止服务。但是考虑到以后的报告会显示我们系统有多少个9达到了高可用,我们会放弃简单的停服方案,比如宕机2小时的场景永远不会达到4个9。但是在一些高并发的情况下,比如闪购或者团购,虽然停服几分钟,但是对整个公司业务的影响可能非常大,每分钟丢失的订单数可能是数量巨大。所以,N个九来量化可用性其实是要考虑业务情况的。微服务高可用设计意味着高可用是一个相对复杂的命题。基本上所有的处理都涉及到高可用,设计高可用方案的方方面面也都涉及到。中间会出现的细节是多种多样的,所以我们需要对这样的微服务高可用方案进行顶层设计。围绕服务高可用,先看看我们手里有多少张牌。服务冗余①冗余策略每次访问可能由多个服务组成,每台机器、每个服务都可能出现问题,所以首先考虑的是每个服务必须有多个副本。是多个。所谓多一致服务,就是服务的冗余。这里所说的服务一般指的是机器服务、容器服务和微服务本身。在机器服务层面,需要考虑机器之间的冗余在物理空间上是否隔离、冗余。比如所有机器是否部署在不同机房,是否部署在同一机房的不同机柜,Docker容器是否部署在不同物理机等。采取的策略实际上取决于服务的业务,因此需要对服务采取不同的策略进行分级打分。不同的策略有不同的安全级别,随之而来的成本也不同。安全级别较高的服务可能不仅要考虑不同的机房,还需要考虑每个机房所在的区域。比如两个机房不能在同一个地震带等。②无状态服务的冗余性会要求我们随时扩容或者缩容服务,有可能我们从2台机器换到3台机器。如果我们想随时随地扩展和收缩服务,我们就要求我们的服务是无状态的。所谓无状态,就是各个服务的服务内容和数据是一致的。比如从我们的微服务架构来看,我们一共分为好几层。因为每一层都是无状态的,所以这一层架构的扩展非常简单。假设我们需要对网关进行扩容,只需要增加服务即可,不需要考虑网关是否存储了额外的数据。网关不保存任何会话数据,不提供会造成一致性的服务,将不一致的数据以多种方式存储,借助更擅长数据同步的中间件。这是目前主流的解决方案。服务本身尽可能提供逻辑服务,集中保证数据的一致性。这样就可以提取“状态”,让网关保持“无状态”。这里只是一个网关的例子。微服务中基本上所有的服务都应该这样来做。如果服务中有状态,状态应该由更擅长处理数据的组件提取和处理,而不是与微服务中的数据状态兼容。数据存储的高可用上面提到的服务冗余,可以简单理解为计算的高可用。计算的高可用只需要是无状态的,可以方便地扩容和缩容。但是,对于需要存储数据的系统来说,即数据本身是有状态的。与存储和计算相比,有一个本质的区别:将数据从一台机器移动到另一台机器,需要通过线路传输。网络不稳定,尤其是跨机房的网络。Ping的延迟可能是几十或几百毫秒。虽然毫秒对于人类来说几乎是无足轻重的,但是对于高可用系统来说,它们有着本质的区别。这意味着在整个系统中的某个时间点,数据肯定是不一致的。根据“数据+逻辑=业务”的公式,如果数据不一致,逻辑一致,最终的业务表现也会不一致。例如:无论是正常情况下的传输延迟,还是异常情况下的传输中断,都会导致系统的数据在某个时间点不一致。数据的不一致会导致业务出现问题,但如果数据不冗余,则无法保证系统的高可用。因此,存储高可用的难点不在于如何备份数据,而在于如何减少或避免数据不一致对业务的影响。分布式领域有一个著名的CAP定理,从理论上论证了存储高可用的复杂性,即存储高可用不可能同时满足“一致性、可用性和分区容错”。最多只能满足两个,分区容错是分布式必须的,这意味着我们在做架构设计的时候必须结合业务在一致性和可用性之间做一个权衡。存储高可用解决方案的本质是将数据复制到多个存储设备上,通过数据冗余实现高可用。复杂性主要是由于数据复制延迟或中断导致的数据不一致。我们在设计存储架构时,必须考虑以下几个方面:如何复制数据?架构中每个节点的职责是什么?如何处理数据复制延迟?当架构中的节点出现故障时,如何保证高可用?①数据主从复制master-slaveReplication是最常见也是最简单的存储高可用方案,如MySQL、Redis等。其架构的优点是简单。主机复制写入和读取,而从机只负责读取操作。当读并发较高时,可以通过扩展从库数量来降低压力。如果主机出现故障,读操作也可以保证读业务的顺利进行。执行。缺点是客户端必须感知主从关系的存在,将不同的操作发送到不同的机器上进行处理。而且,在主从复制中,从机负责读操作,可能会因为主从复制的延迟过长而导致数据不一致的问题。②数据主从切换刚才说了主从切换有两个问题:主机故障写操作无法进行。其中一台slave机器需要手动升级为master。为了解决这两个问题,我们可以设计一个主从自动切换方案,其中涉及到master的状态检测、切换决策、数据丢失和冲突等问题。主机状态检测:需要多个checkpoint检测宿主机是否正常,进程是否存在,是否超时,写操作是否不可执行,读操作是否不可执行,汇总,交给切换决定。切换决策:确定切换的时间决策,什么情况下slave应该升级为master,进程不存在,写操作不可行,检测到连续失败若干次后进行切换。选择哪个slave节点升级为主节点,一般来说应该选择同步步长最大的slave节点升级。开关是自动的还是半自动的,通过报警的方式,让人做一个确认。数据丢失和数据冲突:数据写入主机,但在复制到从机之前,主机挂掉了。这个时候怎么处理,这个还要考虑业务的方式,就是保证CP或者AP。还有一个数据冲突的问题需要考虑,这多是因为MySQL中的自增主键导致的。即使不考虑自增主键带来的数据冲突问题,实际上自增主键也会带来很多问题。这里不再赘述,避免使用自增主键。③数据分片上面提到的数据冗余可以通过数据复制来解决,但是数据扩容需要通过数据分片来解决(如果关系数据库是分表的话)。什么是数据分片(Segment、Fragment、Shard、Partition),就是将数据集按照一定的规则划分成相互独立且正交的数据子集,然后将数据子集分布到不同的节点上。HDFS和MongoDB的Sharding模式基本都是基于这种sharding模式实现的。我们在设计sharding时考虑的要点是:如何做数据分片,如何将数据映射到节点。数据分片的特征值,即根据数据中的哪些属性(字段)进行分片。数据分片的元数据管理,如何保证元数据服务器的高性能和高可用,如果是一组服务器如何保证强一致性。Flexibility/Asynchronization①Asynchronization在每次调用中,时间越长,超时的风险越大,逻辑越复杂,执行的步骤越多,失败的风险越大。如果业务允许,用户调用只给用户必要的结果,而不是需要同步的结果,可以在另一个地方异步操作,降低超时风险,将复杂的业务拆分,降低复杂度。花费。当然,异步的好处还有很多,比如削峰、解耦等,这里只是从易用性的角度。异步的实现方式大致有3种:服务端收到请求后,新建线程处理业务逻辑,服务端先响应客户端。服务端收到请求后,首先响应客户端,然后继续处理业务逻辑。服务端收到请求后,将信息保存在消息队列或数据库中,响应客户端,服务端业务处理进程从消息队列或数据库中读取信息处理业务逻辑。②灵活性什么是灵活性?想象一个场景,我们的系统会为每个下单的用户添加对应订单金额的积分。当用户下单时,我们会给他加分。出了什么问题。这个时候是取消订单还是让订单先过去,积分问题通过重置还是报警处理?所谓弹性,就是在我们业务允许的情况下,我们不能100%的给用户。可用,通过降级的方式给用户尽可能多的服务,而不是每次都要交100分或者0分的答卷。弹性如何做,更多的是对业务的理解和判断。灵活性更多的是一种思维,需要对业务场景有深入的理解。在电商订单场景下,下单、扣库存、付款是必须要执行的步骤。如果失败,则订单将失败。但加分、运费、售后可以灵活处理。即使有错误,也可以向日志发出告警,供人工检查。无需为了加分而失去整个订单的可用性。底线/容错底线可能就是我们常说的降级方案。计划是为了执行,但这里的底线可能更多的是一个想法,更多的是一个计划,每一个操作都会出错,我们也可以出错。但是我们必须为我们犯的每一个错误制定一个计划。这个方案其实是我们最大程度的容错或者避免更大危害的措施。其实也是一个不断退化的过程。例如:比如我们首页请求??的用户个性化商品推荐接口,发现推荐系统有误。我们不应该扩展(直接向用户抛出异常)或保留调用接口的错误,而应该兼容调用接口的错误,使调用更加灵活。这时可以选择获取之前没有失败接口的缓存数据。如果没有,您可以获得没有个性化推荐的一般产品。如果没有,您可以读取一些静态文本进行显示。因为我们的架构是分层的,分层App、网关、业务逻辑层、数据访问层等,组织架构也分了,对应前端组、后端业务逻辑组,甚至中间平台组等等。由于在代码和人员架构上有层级划分,所以每一层都必须有这样一个想法:容忍下一层的错误,并为上层提供尽可能无错误的服务。例如:产品的美元售价假设使用产品的人民币售价/汇率。这个时候错误发生在下层数据层。如果直接划分上层,肯定会抛出java.lang.ArithmeticException:/byzero。基于我们在任何层调用服务都是不可信的原则,我们应该进行容错处理,防止异常蔓延,并且我们必须确保我们层已经尽最大努力确定了上次服务。负载均衡相信负载均衡这个话题已经基本深入到每一位微服务开发者或者设计者的心中。负载均衡的实现包括硬件和软件。硬件包括F5、A10等机器;软件包括LVS、Nginx、HAProxy等,负载均衡算法包括Random、RoundRobin、ConsistentHash等。①Nginx负载均衡故障转移过程:Nginx根据给定的负载均衡算法进行调度。当请求到达Tomcat1时,Nginx发现Tomcat1出现连接错误(nodefailure),Nginx会按照一定的机制将Tomcat1从被调用的负载中转移过来。从列表中清除。在接下来的请求中,Nginx不会将请求分配给有问题的Tomcat1,而是将请求转给其他的Tomcat。节点故障:Nginx默认根据connectrefuse和timeout来判断节点故障,累积一个节点的故障。当失败次数大于max_fails时,节点失败。节点恢复:当一个节点失效超过max_fails但没有超过fail_timeout时,Nginx将不再检测该节点,直到超过失效时间或所有节点失效,Nginx才会重新检测该节点。②ZK负载均衡故障转移当使用ZK作为注册中心时,故障的发现由ZK完成,业务逻辑层通过Watch的心跳机制将自己注册到ZK,网关可以订阅ZK知道有多少可以调用的列表。当业务逻辑层重启或关闭时,会与ZK断心跳,ZK会更新可调用列表。使用ZK作为负载均衡的协调器,最大的问题是服务是否可用ZK是基于Pingpong的。只要服务心跳存在,ZK就认为服务处于可用状态,但如果服务处于假死状态,ZK就无从知晓。此时,业务逻辑服务是否真的可用,只能由网关知道。幂等设计:之所以会提出幂等设计的问题,主要是因为负载均衡的failover策略,就是重试失败的服务。一般来说,如果是读操作服务,重复执行不会出问题,但是想象一下,如果是创建订单,减少库存的操作,第一次调用Tomcat1会超时,然后再次调用Tomcat2.这个时候我们无法确认超时调用的Tomcat1是否真的被调用了。它可能根本不成功,或者它可能已成功调用但由于某种原因返回超时。因此,很大程度上,这个接口会被调用两次。如果我们不保证幂等性,一个订单可能会导致库存减少2次。所谓幂等性就是保证在同一个业务中,多次调用一个接口,得到的结果是一样的。服务限流和降级断路器首先说一下微服务中限流/断路器的用途。微服务之后,系统采用分布式部署方式,系统之间通过RPC框架进行通信。整个系统发生故障的概率随着系统规模的增长而增加,一个小的故障如果通过链路的传输放大可能会引起更大的故障。限流和高可用有什么关系?假设我们的系统最多只能承受500人的并发访问,但是某个时候突然增加到1000人,整个系统会一下子不堪重负。原来还有500人可以享受我们系统的服务,一下子都服务不到了。不是拒绝服务1000人,而是服务500人,拒绝服务另外500人。限流就是访问的隔离,保证用户的可用性在部门系统的容忍范围内。熔断器和高可用有什么关系?上面说了,微服务是一个错综复杂的调用链关系。假设模块A调用模块B,模块B调用模块C,模块C调用模块D,此时模块D出现问题,延迟严重。这时候整个调用链就会被模块D拖下水,A等B,B等C,C等D,ABCD的资源被锁死,无法释放。如果流量很大,很容易造成雪崩。熔断,主动丢弃模块D的调用,做一些功能降级,可以保证我们系统的健壮性。熔断器是模块的隔离,保证了最大功能的可用性。服务治理①服务模块划分服务模块和服务模块有着千丝万缕的联系,但服务模块在业务中各有权重。比如订单模块,可能是电商公司的重中之重,一旦出现问题,将直接影响到整个公司的营收。而一个后台查询服务模块可能也很重要,但其重要程度肯定不如顺序重要。所以在做服务治理的时候,需要明确各个服务模块的重要性等级,这样才能更好的监控和分配资源。这个每个公司每个公司都有一个标准。例如,在电子商务企业中,确定服务水平可能更倾向于使用用户请求数和收入作为指标。真正的划分可能比这更复杂,要根据具体业务来确定。这个可以从平时服务模块的访问量和流量来估算。往往更重要的模块也会提供更多的资源,所以不仅要了解技术架构,还要了解公司的各种业务形态。服务分类不仅在故障定义中起着重要作用,而且决定了服务监控的强度。服务监控起到保障高可用的作用。它不仅可以保留服务崩溃的站点以供以后恢复,更重要的是可以起到先知和预判的作用。在很多情况下,它可以预判危险,防患于未然。②服务监控服务监控是微服务治理的重要组成部分。监控系统的完善程度直接影响到我们微服务的质量。当我们的微服务在线运行时,是否有完善的监控系统来了解它的健康状况,对于整个系统的可靠性和稳定性来说是非常重要的。可靠性和稳定性是高可用性的先决条件。确保。服务监控更多的是风险预测。可以在不可用之前提前发现问题。如果系统获得了可以自我修复的监控和报警系统,就可以在无形中消除错误。如果系统发现告警无法自行修复,可以提前通知人员接入。一个比较完善的微服务监控系统需要涉及到哪些层次?如下图,大致可以分为五个层次的监控:基础设施监控:比如网络、交换机、路由器等底层设备。这些设备的可靠性是否稳定直接影响到上层服务应用的稳定性。因此需要对网络流量、丢包、错包、连接数等基础设施的核心指标进行监控。监控层面,监控几个核心指标,如CPU使用率、内存使用率、磁盘IO、网络带宽等。应用层监控:如URL访问性能、访问调用次数、访问时延,以及服务提供性能和服务错误率的监控。SQL也需要监控,看看有没有慢SQL。对于Cache,需要监控缓存的命中率和性能,每个服务的响应时间和QPS等等。业务监控:比如电商网站需要关注其用户登录状态、注册状态、订单状态、支付状态等。这些直接影响业务交易的实际触发。这种监控可以为运营和公司高管提供他们需要关注的数据,这些数据可能会直接影响公司的战略。终端用户监控:用户通过浏览器和客户端打开到我们服务的连接,那么客户端的用户体验如何,客户端的性能如何,有没有报错,这些信息也需要被监控和记录下来。如果没有监控,有可能因为某些原因或者性能问题,导致用户的体验非常差,但是我们没有察觉到。这包括监控用户终端的性能,返回代码,他们在哪个城市地区的使用情况,以及运营商的情况,包括电信和中国联通用户的连接状态。我们还需要进一步知道有没有什么渠道,哪些用户在访问的时候有问题,包括我们还需要知道客户端使用的操作系统浏览器的版本。总结了这么多牌,打牌也只是个套路,真道还是要静下心来,看看整个服务高可用的本质是什么。随着微服务架构的相互调用越来越复杂,链接只会越来越多。只有建立清晰的架构和层级,才能明确各个环节的高可用保障,做到简单。在手段上,实现高可用的主要技术手段是服务和数据的冗余备份和故障转移。可以在多个节点上备份一组服务或一组数据。当一台机器宕机或出现问题时,它可以从当前服务切换到其他可用服务,而不会影响系统的可用性或导致数据丢失。从架构的角度来看,高可用维护的是一个简单的架构。目前大多数网站采用的是比较经典的分层架构,应用层、服务层、数据层。应用层处理一些业务逻辑,服务层提供一些与业务密切相关的数据和服务,数据层负责读写数据。简单的架构可以保持应用层和服务层的无状态水平扩展,属于计算的高可用。与计算高可用相比,在数据层考虑的高可用属于数据高可用。与计算高可用相比,数据高可用需要考虑的数据一致性问题会更加复杂。这时候,CAP理论就会在其中发挥关键作用。选择AP还是CP取决于业务选择的模式。从硬件上看高可用,首先要确认硬件可能一直坏,网络一直不稳定。解决办法是一台服务器不够就加几个,一个机柜不够就加几个,一个机房不够就加几个。从软件的角度来看,高可用软件开发不严谨,发布不规范,也会导致各种不可用。控制软件开发过程的质量监控,通过测试、预发布和灰度发布也是降低不可用性的措施。从治理的角度来说,一个高可用的系统在线上运行的很好,但是我们不能保证下一秒会不会不可用。规范服务,提前做好服务分割和监控,预测不可用的发生,在不可用发生前发现问题并解决问题。【注:文章部分内容参考李云华《从 0 开始学架构》杨博老师《微服务》】作者:陈雨哲简介:十余年开发架构经验,国内较早一批微服务开发实施者.曾在国内互联网公司网易、唯品会担任高级研发工程师,后在初创公司担任技术总监/架构师。目前在洋葱集团担任技术研发副总监。【原创稿件,合作网站转载请注明原作者及出处为.com】