zl程序教程

您现在的位置是:首页 >  其它

当前栏目

关于软件系统中的高可用问题的碎碎念

关于 可用 问题 软件系统
2023-09-14 08:57:00 时间

什么是高可用

在定义什么是高可用,可以先定义下什么是不可用,一个网站的内容最终呈现在用户面前需要经过若干个环节,而其中只要任何一个环节出现了故障,都可能导致网站页面不可访问,这个也就是网站不可用的情况。

参考维基百科,看看维基怎么定义高可用:

系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。

这个难点或是重点在于“无中断”,要做到 7x24 小时无中断无异常的服务提供。

 

为什么需要高可用

一套对外提供服务的系统是需要硬件,软件相结合,但是我们的硬件总是会出故障,软件会有 Bug,硬件会慢慢老化,网络总是不稳定,软件会越来越复杂和庞大。

除了硬件软件在本质上无法做到“无中断”,外部环境也可能导致服务的中断,例如断电,地震,火灾,光纤被挖掘机挖断,这些影响的程度可能更大。

 

 高可用  不难理解, 但是一系列的"如果"都叠加过来的话, 问题变得复杂而不好理解了  

 

通常, 我们会问, 如果这个机器可能出现单点故障, 那个服务器挂掉了, 怎么办.. 

但是, 如果业务量很少的话,  机器怎么会那么容易挂掉呢? 总是问这种问题,  总是顾虑这些问题, 烦不烦啊!!   其实如果业务量, 请求量不多, 数据量不是很大,  我们可能根本不需要做到高可用, 根本不需要考虑那些..

 

高可用是好的, 做不到确实会影响用户体验, 但是如果客户是"自己人", 是不在意这些, 那么其实不要紧.   况且, 影响用户体验的还有很多方面, 比如系统前端的美观/ 易用性/ 操作便利性, 布局,  功能是否清晰明确 , 是否好玩有趣, 是否炫酷,  业务逻辑是否流程? 

 

还有, 数据不能丢失, 丢失通常是不能忍的..  但是最不能忍的, 恐怕还是 不可用,  如果你的系统, 经常各种崩溃, 各种bug , 各种异常不能用, 那么就非常考验用户关系了!  关系好的吃顿饭, 赔礼道歉就解决, 关系不好的就可能需要给赔偿了!

 

其实呢,  如果业务量很大,   如果导致了 一个机器的长时间高负荷运行,  那么确实就容易出现故障, 导致机器不能正常运行, 甚至down机, 死掉,  那么就需要 事先做好 数据的备份迁移. 如果出现了故障,  那么就立即的启动并把业务迁移到另外一个相同功能的 服务器上去!

 如果是 能够无缝的切换,  那么就是热备,   如果需要手动启动另外一个机器,  那么就是冷备..

 

所以说, 如果是企业用户, 基本上, 上班时间可用就可以了 ,  互联网用户, 那么需要很高的可用性了!  但是不要总是说什么集群 , 高可用 什么的, 重要的是 可用, 好用!

 

高可用 并不是说100% 可用, 基本上很难做到100% 无影响.  一般还是需要暂停一下业务的,  少则几毫秒, 几秒,  几分钟,  多则几个小时,  几天... 

 

如果是低并发, 或者很少人访问, 比如半夜, 那么就不需要 什么高可用了, 不可用也不会有影响 ,

如果是高并发, 特别是对于那些很重要的业务, 那么需要保证 不管什么 机器/服务节点 故障, 都应该能够提供正常服务. 即使不能快速的处理完用户请求, 但至少能够比较快的处理, 比如准实时的, 或者过几分钟/ 几小时后 给处理结果的通知的方式.

当然, 出现故障之后, 可能还是会造成一定的不好的影响. 比如, 如果之前是3个节点并行执行, 后面挂掉一个, 那么就只剩两个, 然后服务的处理能力 就自然而然的 肯定会下降大概 1/3.

但这个也不是绝对的, 如果 之前是两个节点 做冗余, 无论是冷备或者热备, 那么其中一个节点挂掉, 另外一个节点直接替换上就好了.

 

高可用的评价纬度

在业界有一套比较出名的评定网站可用性的指标,常用 N 个 9 来量化可用性,可以直接映射到网站正常运行时间的百分比上:

之前就职的一家互联网公司也是按照这个指标去界定可用性,不过在执行的过程中也碰到了一些问题。

例如,有一些服务的升级或数据迁移明明可以在深夜停机或停服务进行,然而考虑到以后的报告要显示出我们的系统达到了多少个 9 的高可用,而放弃停服务这种简单的解决方案,例如停机 2 个小时,就永远也达不到 4 个 9。

然而在一些高并发的场合,例如在秒杀或拼团,虽然服务停止了几分钟,但是这个对整个公司业务的影响可能是非常重大的,分分钟丢失的订单可能是一个庞大的数量。

所以 N 个 9 来量化可用性其实也得考虑业务的情况。

 

微服务高可用设计手段

高可用是一个比较复杂的命题,基本上在所有的处理中都会涉及到高可用,所有在设计高可用方案也涉及到了方方面面。

 

引用https://zhuanlan.zhihu.com/p/88152015 : 

高可用(HA)是系统架构设计中必须要考虑的,是指系统所能提供无故障服务的一种能力。


简单的说就是避免因服务器宕机而造成的服务不可用的情况,像Elasticsearch并不会因为一节点的宕机而造成整个搜索服务不可用(可以看前面分享的ES分布式搜索原理)。

如何衡量高可用

假设你的系统全年都是正常提供服务,那么就是说你系统的可用性是100%,当然这个值是理想状态下,一般都是以几个9来表示系统的可用性,99.99的可用性较多,9越多就代表可用性越强,下面来看看这个几个9是如何计算出来的

可用性=平均故障间隔/(平均故障间隔 + 故障恢复平均时间)

如何设计系统的高可用

想要高可用就要避免使用单点,你想想看你的单台服务器再强应用优化的再极致,只要它宕机,就啥都凉凉了,所以需要多台机器也就是需要集群,方法论中叫冗余。只是有了集群是不能完全满足复杂业务的高可用的,我们要让系统在当前节点宕机的情况下,自己进行切换到好的节点去,这即所谓的故障转移。所以我们现在设计高可用系统的目标明确了,

那就是:冗余 + 故障转移 

 

 

设计系统高可用方法论

上面介绍了我们在宏观方面怎么设计系统高可用,其实我们在编码的时候除了故障转移方案,同样需要考虑很多东西来保证系统的可用性,主要体现在,失败转移, 超时机制、降级、限流, 缓存, 横向/纵向扩展, 异步解耦, 削峰填谷

对于rpc请求, 不管是对其他微服务的rpc 还是对数据库/ redis/ mq 的rpc, 都应该有一个超时时间,  防止远程服务的假死, 进而导致现有系统的过多的请求阻塞, 导致 占用很多的资源不释放, 导致内存不够用溢出等问题.. 

所以呢, 是需要一个超时时间的, 但是适当的等待是需要的, 因为不等待而返回异常, 总是体验不好的!

 

失败转移

失败转移就是说一个节点挂掉, 立即使用其他的备用节点, 或者上游系统做探测, 发现某节点失败, 直接把请求转移到另外的相同功能的节点,  同时把后续的请求也不在转发到此节点, 直到它恢复之后.

 

降级

降级就是说, 原来提供了1级水平的服务, 现在因为某些故障, 只能提供2级或3级水平了, 相当于是下降了服务的等级.

它往往是跟远程访问时 远程服务的异常/超时 有关,  此时, 如果直接把原本的错误返回前端, 肯定是不好的, 那么返回一个降级的结果, 比如一个默认值,  一个估计值, 或者实在不行就返回一个友好的提示, 也总比没有的好.

不重要的服务停掉, 或者降级掉.. 

 

异步解耦/削峰填谷

这个跟高可用没有直接关系, 它其实跟高并发有一定关系,  而高并发跟高可用有一定关系.  我们说高可用, 一般也是在高并发的场景下.  通过 异步解耦/削峰填谷 , 我们可以一定程度的迂回的降低系统压力, 从而也一定程度提高了高可用.

异步解耦/削峰填谷 之后, 我们响应是没有那么快了,  但是我们只要保证了最终一致性, 那也是挺好的. 基本上用户也不会有太大的怨言..

 

兜底 

兜底和降级很相似,  降级主要是讲把服务停掉或部分停掉, 降低其对资源的要求, 比如原来给他4c8u, 现在给他1c2u, 或者直接停掉它, 而兜底可用理解为 fallback, 

 

限流

限流, 这个主要就是说在客户端请求特别大的时候, 我们一应该把请求直接在前面的服务或网关 就直接拒绝它,  避免它打到了后端的服务.  这个是很有用的一种方式,  可以说得上是一种哲学, 一种智慧. 通常是, 经过压测之后, 我们大概知道我们的系统的处理能力/ 吞吐能力( 一般也只能精确到数据量级的水平), 这个时候 我们估计用户的请求的并发请求总量, 然后我们就把超出处理能力那部分的请求 直接降级.

 

当然, 其实应该分场景来做限流, 对于是恶意请求, 我们全部拒绝.  比如对于黑名单的ip/ 用户, 对于那种恶意发起大量以致海量请求的用户, 明显不正常, 就可以调查他, 警告他, 然后拒绝它的全部请求.

对于那种无法区分是不是恶意的请求,  我们可能选择性的 随机的拒绝它, 然后给他一个温馨的,抱歉的响应.

 

秒杀场景之下,  我们的商品数量是有限的, 比如只有100 个优惠商品, 用户请求却有1w 甚至10w, 那么就可以直接 拒绝其中一大部分, 比如我们可以给全国各省都分配一定的定额,  在保证定额的前提下, 选择性的拒绝其他的请求 

当然, 我们应该至少留100个请求, 但是呢, 这100个请求, 可能有一部分请求会最终没法完成支付, 那么我们就把这部分的定额释放出来,   就是说, 剩余的优惠商品数量, 应该是可以实时查看的, 先来的有机会抢到,  后到的机会少一些, 但也是留有机会的.

同时呢, 我们可能希望活动能够持续一段时间, 而不是, 真正的 一下子就结束,  我们把100个优惠商品, 分2个小时 放出,  如每隔10分钟放出10个名额, 抢到的优先..

 

横向/纵向扩展

高扩展性个高可用是两个话题, 有一定的关联, 怎么说呢, 如果系统有很好的扩展性, 那么自然的, 可用性也会得到提升.

 

 

一个简单的高可用设计实例

我们来先看看当前大部分互联网公司的系统架构: 

 

 

 

这个其实只有一个应用服务的架构, 如果是很大应用服务, 那么.  红色的表示可以允许挂掉的节点, 同名的方框表示相同的软件系统.

 

可以看到,这样的架构其实就保证了非常高的可用性.

1 如果nginx 挂掉一台, 立即切换, 毫无影响 __  图中 vip 没画出来, app 应该是通过vip 访问nginx , 而不是直接范围, 否则,  app 需要配置多个nginx ip,  其中一个发生故障, 则自动使用另外一个 

2 service1 应用节点挂掉, 毫无影响 ,  当然, 我们需要做成是无状态的, 否则那就多少还是会有一定的影响,  特别是session状态.  我们把所有的软件服务看成一个大的系统,  一个整体,  它肯定是需要登录的,  每个应用内部不需要session, 但是鉴权还是需要的吧,  鉴权怎么做?  还是说鉴权也可以省去呢?

3 缓存从节点挂掉, 毫无影响 , 因为我们是集群,  主节点挂掉, 从升级为主, 因为缓存做了主从同步.. 所以也是几乎没有影响,  有影响也是影响到 那么几秒钟..

4 数据库也是类似缓存, 不过数据安全要求更高..

5 当然, 用户终端也可以会挂点, 当然, 这种问题是用户的问题了, 跟我们的系统没关系, 超出我们处理范围, 不予考虑