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

你觉得这个东西能叫高可用吗?

时间:2023-03-13 20:48:51 科技观察

1。前言高可用性(HA)的主要目的是保证“业务连续性”,即在用户眼中,业务会一直正常(或基本正常)对外提供服务。高可用主要是针对架构的,所以要做高可用首先要设计好架构,第一步我们一般采用分层的思想将一个庞大的IT系统拆分成应用层、中间件、数据存储层等独立的层,然后将每一层分成更细粒度的组件。第二步是让各个组件对外提供服务。毕竟每个组件都不是孤立存在的,需要相互配合才能对外提供服务。才有意义。为保证架构的高可用,需要确保架构中的所有组件及其暴露的服务都是为高可用而设计的。任何不具备高可用性的组件或其服务都意味着系统存在风险。那么这么多组件如何设计高可用呢?事实上,任何组件都需要在没有“冗余”和“自动故障转移”的情况下实现高可用。众所周知,单点是高可用的大敌,所以组件一般都是以集群的形式存在(至少两台机器),这样只要某台机器出现问题,集群中的其他机器可以随时替换它。这就是“冗余”。简单计算一下,假设一台机器的可用性是90%,两台机器组成的集群的可用性是1-0.1*0.1=99%,那么显然冗余机器越多,可用性就越高。但仅有冗余是不够的。如果机器出现问题,人工切换既费时费力,又容易出错。因此,我们需要借助第三方工具(即仲裁器)的力量来实现“自动”故障转移。要达到近实时故障转移的目标,近实时故障转移是高可用性的主要含义。什么样的系统才能称为高可用?业界一般用几个9来衡量系统的可用性,如下:一般要达到两个9就很简单了。毕竟每天宕机14分钟,已经严重影响了业务。这样的公司迟早会倒闭。对于食品,大厂一般要求4个9,其他要求高的企业要求5个9以上。例如,如果由于计算机故障导致所有列车停运,数以万计的人的正常生活将受到阻碍。在这种情况下,需要超过五个九。下面我们就来看看架构中的各个组件是如何借助“冗余”和“故障自动切换”实现高可用的。2.互联网架构分析目前互联网大部分都会采用微服务架构。常见的架构如下:可以看到架构主要分为以下几层。接入层:F5硬件或LVS软件,主要承载所有流量入口。反向代理层:Nginx,主要负责根据url分发流量,限流等网关:主要负责流量控制、风控、协议转换等站点层:主要负责调用会员、推广等基础服务组装json等数据返回给客户端基础服务:其实site层和site层都属于微服务,属于层级关系,但是基础服务属于基础设施,可以被上层调用-级业务层服务器。存储层:即DB,如MySQL、Oracle等,一般由基础服务调用返回站点层中间件:ZK、ES、Redis、MQ等,主要是加快数据访问速度以及其他功能,下面我们简单介绍下各个组件的作用前面提到,要实现整体架构的高可用,就必须要实现各个层级的组件的高可用,下面我们来看看各个组件是如何实现的级别实现高可用性。3、接入层&反向代理层的高可用是和keepalived相关的,所以我们把它们结合起来看看外面的世界。两个LVS以master和backup的形式提供服务。注意只有master在工作(也就是此时VIP对master生效),master宕机后会有另外一个backup接替master的工作,那么backup怎么知道master是否正常?答案是通过keepalived在主备机上都安装keepalived软件。通过心跳检测彼此的健康状态。一旦master宕机,keepalived会检测到,这样backup会自动转交给master对外提供服务。这时候VIP地址(即图中的115.204.94.139)就会对备份生效,也就是我们常说的“IP漂移”就是通过这种方式解决了LVS的高可用。keepalived的心跳检测主要通过发送ICMP报文进行检测,或者使用TCP端口连接扫描检测。同样的,它也可以用来检测Nginx暴露的端口,这样如果某个Nginx出现异常,Keepalived也可以检测到并从LVS可以转发的服务列表中移除。Nginx还可以通过端口查看服务的健康状态。借用第三方工具keepalived,同时实现了LVS和Nginx的高可用。同时,当出现故障时,可以将宕机信息发送到相应开发者的邮箱,以便他们及时收到通知。真的很方便。Keepalived应用广泛,下面我们会看到它也可以用在MySQL上,实现MySQL的高可用。4.微服务接下来我们来看一下“网关”、“站点层”、“基础服务层”。这三个一般就是我们所说的微服务架构组件。当然,这些微服务组件也需要通过一些RPC框架,比如使用Dubbo来支持通信,所以要实现微服务的高可用,也就意味着dubbo等RPC框架也必须提供支持高可用的能力微服务。下面以dubbo为例,看看它是如何实现高可用的。简单看一下dubbo的基本架构:思路也很简单。首先,Provider(服务提供者)向Registry(注册中心,如ZK或Nacos等)注册服务,然后Consumer(服务消费者)向注册中心注册。订阅并拉取Provider服务列表。Consumer获得服务列表后,可以根据自己的负载均衡策略选择其中一个Provider向其发送请求。当其中一个Provider不可用时(离线或者由于GC阻塞等),它会被注册中心及时监听到(通过心跳机制),也会及时推送给消费者,所以消费者可以将其从可用提供者列表中移除,实现故障的自动转移。不难看出,注册中心启动了类似keepalived的作用。5、中间件我们来看看ZK、Redis等这些中间件是如何实现高可用的?1、我们在ZK的微服务部分提到了注册中心,下面以ZK(ZooKeeper)为例,看看它的高可用是如何实现的。我们先看一下它的整体架构图如下:Zookeeper主要作用如下1)Leader:领导者,集群中只有一个Leader,作为事务请求的唯一调度器和处理器,主要承担以下功能,保证集群事务处理的顺序,所有follower的写请求都会转发给Leader执行,用于保证事务的一致性集群中各server的调度器:处理完事务请求后,会同步广播数据给每个Follower,统计Follower写入成功的次数。如果超过半数的Follower写入成功,Leader会认为写入请求提交成功,并通知所有Follower提交写入操作,以保证即使集群崩溃后恢复或重启,写入操作不会丢失。2)Follower:处理客户端的非事务性请求,将事务性请求转发给leader服务器,参与对该事务请求Proposal的投票(需要超过半数的服务器通过,通知leader提交数据;proposalLeader发起的需要Follower投票)参与Leader选举的投票画外音:Zookeeper3.0之后新增了一个Observer的角色,但是与这里讨论的ZK的高可用关系不大。为了简化问题,省略。可以看出,由于只有一个Leader,很明显这个Leader存在单点隐患。那么ZK是如何解决这个问题的呢?首先,Follower和Leader会使用心跳机制来维持连接。如果Leader出现问题(宕机或者由于FullGC等其他原因无法响应),Follower将无法感知Leader的心跳,会认为Leader出现问题,于是会发起一次投票选举,最终在多个Followers中选出一个Leader(这里主要使用ZookeeperAtomicBroadcast,即ZAB协议,是专门为ZK设计的支持崩溃恢复的共识协议),选举详情不是本文的重点,这里不再详述。除了ZAB协议,Paxos、Raft等协议算法是业界常用的,也可以用在Leader选举中,即在分布式架构中,这些协议算法承担了“第三方”的角色或仲裁员,承担失败的自动转移。2、RedisRedis的高可用需要从它的部署模式来看,主要分为“主从模式”和“集群分片模式”两种。1)主从模式首先看主从模式,架构如下:主从模式是指一主多从(一个或多个从节点),其中主节点主要负责读取和写,然后同步数据到多个从节点上,Client也可以向多个从节点发起读请求,这样可以减轻主节点的压力,但是和ZK一样,由于只有一个主节点,所以有一个单点危险,因此必须引入第三方仲裁机制来判断主节点是否宕机,并在主节点宕机后快速选择一个从节点作为主节点。这个第三方仲裁者在Redis中一般被称为“哨兵”。当然,哨兵进程本身也有可能挂掉,所以为了安全起见,需要部署多个哨兵(即哨兵集群)。这些哨兵通过gossip协议接收主服务器是否下线的信息,在判断主节点宕机后使用Raft协议选举新的主节点。2)集群分片集群的主从模式看似完美,但存在以下问题:难以降低主节点的写压力:因为只有一个主节点可以接收写请求,如果写请求是高并发下非常快如果太高,可能主节点的网卡被占满,导致主节点无法对外服务。随着业务量的增长,缓存数据很可能呈直线上升,直至达到存储瓶颈。同步风暴:因为数据是从master同步到slave的,如果有多个slave节点,master节点的压力会很大。为了解决master-slave模式来解决以上问题,分片集群应运而生。所谓分片集群就是数据分片,每条数据由对应的主节点读写。这样就有多个master节点来分担写入压力,每个节点只存储一部分数据,解决了单机存储瓶颈问题,但是需要注意的是每个master节点都是单点的问题,所以需要对每个master节点做高可用。整体架构如下:原理也很简单,Proxy接收到client执行的redis读写命令后,会先为key计算一个值,如果该值落在值范围内即对应的master负责(一般每个数称为slot,Redis一共有16384个slot),然后将这个redis命令发送给对应的master去执行,可以看到每个master节点都是只负责处理一部分redis数据,同时为了避免每个master的单点问题,还配备了多个slave节点组成集群。当主节点宕机时,集群会使用Raft算法从从节点中选举一个主节点。3、ES下面我们来看看ES是如何实现高可用的。在ES中,数据以分片的形式存在。如下图,一个节点中的索引数据分为三个分片存储:但是如果只有一个节点,显然和Redis的主从架构一样存在单点问题。如果这个节点宕机了,那么ES也会宕机,显然需要创建多个节点。一旦创建了多个节点,分片(图中P为主分片,R为副本分片)的优势就体现出来了,分片后的数据可以分布存储在其他节点上,大大提高了数据的安全性。水平扩展能力,同时每个节点都可以承担读写请求,采用负载均衡的形式避免单点读写压力。ES的写入机制与Redis和MySQL的主从架构有些不同(后两者直接写入master节点,而ES不会),所以这里稍微解释一下ES的工作原理:首先,下一个节点节点(Node)的工作机制分为主节点(MasterNode)和从节点(SlaveNode)。该节点是集群的一部分,并决定将哪些分片分配给相关节点。主节点只有一个,一般通过Bully-like算法选举产生。如果主节点不可用,也可以通过该算法选举其他从节点。为了实现集群的高可用,任何一个节点都可以接收到读写请求,达到负载均衡的目的。再来说说sharding的工作原理。Sharding分为PrimaryShard(图中P0、P1、P2)和ReplicaShard(图中R0、R1、R2)。主分片负责Data的写操作,所以虽然任何节点都可以接收到读写请求,但是如果节点接收到写请求,并且没有写入数据的主分片,则节点会将写请求调度到该节点主分片所在的位置,主分片写入主分片后,将数据复制到其他节点的副本分片。以一个有两个副本的集群为例,写操作如下:4.MQES使用数据分片来提高高可用性和可扩展性的思想也应用到其他组件的架构设计中。下面以MQ中的Kafka为例,再来看数据分片的应用:Kafka高可用设计,图片来自《武哥漫谈IT》上面是Kafka集群,可以看到每个topic的各个Partition分布存储在其他消息服务器,这样一旦一个Partition不可用,可以从followers中选出一个leader继续服务。但是,与ES中的数据分片不同,followerPartitions是冷备的。也就是说它在正常情况下是不会对外提供服务的,只有在leader挂掉后从followers中选出leader才能对外提供服务。六、存储层接下来我们来看最后一层,存储层(DB)。这里我们以MySQL为例简单讨论一下它的高可用设计。其实,如果你看了上面的高可用设计,你会发现,MySQL的高可用也是如此,思路也差不多。和Redis类似,它也有主从和分片两种架构(也就是我们常说的分库分表)。主从类似于LVS。一般使用keepalived来实现高可用,如下图:如果master挂了,Keepalived会及时发现,于是从库升级主库,VIP“漂移”到原来生效从数据库,所以你在项目中配置的MySQL地址一般都是VIP,保证数据量大后高可用分库分表,所以会有多个master,就像redis分片集群一样.每个master需要配多个slave,如下:有读者问为什么分库分表后还需要主从。现在我想大家应该明白了,不是为了解决读写性能的问题,主要是为了实现高可用。7.小结看完架构层面的高可用设计,相信大家对高可用“冗余”和“故障自动切换”的核心思想会有更深刻的理解。观察上面架构中的组件,你会发现冗余的主要原因是因为只有一个master,为什么不能有多个master呢?不是不可以,但是在分布式系统中保证数据的一致性是非常困难的,尤其是节点越多,数据之间的同步就越困难。这是一个很大的问题,所以大部分组件都是采用一个master的形式,然后在master和多个slave之间进行同步。大多数组件之所以选择一个master,本质上是一种技术上的权衡。那么每个组件的高可用做好之后,整个架构真的可用吗?不,这只能说是第一步。生产中还有很多突发事件会让我们的系统面临挑战,例如:瞬时流量问题:比如我们可能会面临秒杀带来的流量突然激增,这会导致系统的承载能力不堪重负。这种情况可能会影响到日常交易等核心环节,因此需要对系统进行隔离。比如单独为秒杀部署一套独立的集群。安全问题:如DDOS攻击,爬虫频繁请求,甚至删库跑路等,导致系统拒绝服务。在发布过程中突然暂停当前正在运行的服务是不可接受的。需要优雅关闭,顺利发布第三方问题:比如我们之前的服务依赖第三方系统,第三方问题可能会影响到我们的核心业务不可抗力:比如机房断电off,要做好容灾,多住异地。之前我们的业务,因为机房故障导致服务无法使用四个小时,损失惨重。所以,除了让架构高可用,我们还需要做好。通过系统隔离、限流、熔断、风控、降级、关键操作操作员权限限制等措施保证系统的可用性。这里特别提一下降级,降级是保证系统可用性的常用措施。举几个例子:我们之前联系过一个第三方出资人,由于自身原因导致借贷功能出现问题,无法借贷。为了避免这种情况,引起用户的恐慌,所以我们在用户申请第三方贷款时返回了一份类似“为了提高您的信用额度,出资人正在升级系统”的副本,避免了客户投诉.在流媒体领域,当用户在观看直播时出现严重卡顿时,很多公司的首选并不是通过查看日志来排查问题,而是自动为用户降低码率。因为比起画质的下降,看不到卡对用户来说显然更加痛苦。在双十一0:00的高峰期,我们停止了用户注册、登录等非核心功能,以保证下单等核心流程的顺畅。另外我们最好能够提前防御,在系统出现问题之前将其扼杀在摇篮中,所以我们需要做单元测试、全链路压力测试等来发现问题,同时我们也需要做好对CPU、线程数等的监控,当达到我们设置的阈值时,会触发报警,以便我们及时发现问题并修复(我们公司遇到过类似的生产事故审查之前,大家可以看一下),而且除了做好单元测试的前提下,还是有可能因为代码中的潜在bug而导致上线问题,所以我们需要封网(也就是不允许在关键时候(比如双十一期间)发布代码,并且我们还需要在事故发生后快速定位问题,快速回滚,这需要记录每次发布时间,发布人等。这里的release不仅包括项目的发布,还包括t的发布配置中心等。画外音:上图为我司发布实录。可以看到有代码改动,回滚等,这样发现有什么问题可以一键回滚。最后,我们用一张图来总结一下常用的高可用手段: