高可用
2024-03-18 12:39:54 1 举报
AI智能生成
高可用
作者其他创作
大纲/内容
高可用系统
高可用描述的是一个系统在大部分时间都是可用的,可以为我们提供服务的。高可用代表系统即使在发生硬件故障或者系统升级的时候,服务仍然是可用的。
不可用情况
黑客攻击。
硬件故障,比如服务器坏掉。<br>
并发量/用户请求量激增导致整个服务宕掉或者部分服务不可用。<br>
代码中的坏味道导致内存泄漏或者其他问题导致程序挂掉。<br>
网站架构某个重要的角色比如 Nginx 或者数据库突然不可用。<br>
自然灾害或者人为破坏。<br>
提高可用性方法
注重代码质量,测试严格把关
最重要。
比较实际可用的就是 CodeReview,不要在乎每天多花的那 1 个小时左右的时间,作用可大着呢!
神器
Sonarqube
Alibaba 开源的 Java 诊断工具 Arthas
阿里巴巴 Java 代码规范(Alibaba Java Code Guidelines)
IDEA 自带的代码分析等工具<br>
使用集群,减少单点故障
限流
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,<br>当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
超时和重试机制设置
一旦用户请求超过某个时间的得不到响应,就抛出异常。
熔断机制
超时和重试机制设置之外,熔断机制也是很重要的。
熔断机制说的是系统自动收集所依赖服务的资源使用情况和性能指标,<br>当所依赖的服务恶化或者调用失败次数达到某个阈值的时候就迅速失败,让当前系统立即切换依赖其他备用服务。
比较常用的流量控制和熔断降级框架是 Netflix 的 Hystrix 和 alibaba 的 Sentinel。<br>
异步调用
异步调用的话我们不需要关心最后的结果,这样我们就可以用户请求完成之后就立即返回结果,具体处理我们可以后续再做,秒杀场景用这个还是蛮多的。
使用缓存
如果我们的系统属于并发量比较高的话,如果我们单纯使用数据库的话,当大量请求直接落到数据库可能数据库就会直接挂掉。
灰度发布
定义
灰度发布(⼜名⾦丝雀发布) 是⼀种平滑发布新版本系统的⽅式。
是先分配⼀⼩部分请求流量到新版本,看看有没有问题,没问题的话,再⼀点点地增加流量,最终让所有流量都切换到新版本。
为了直观对新版本服务的稳定性进⾏观测,灰度发布的正确完成还需要依赖可靠的 监控系统 。
方式
AB 测试
是把⽤户分成两组,⼀组⽤户使⽤ A ⽅案(新版本),⼀组⽤户使⽤ B ⽅案(⽼版本)。
方案
基于 Nginx+OpenResty+Redis+Lua 实现流量动态分流来实现灰度发布,新浪的 ABTestingGateway 就是这种基于这种⽅案的⼀个开源项⽬。
使⽤ Jenkins + Nginx 实现灰度发布策,这种⽅案的原理和第⼀种类似,都是通过对 Nginx ⽂件的修改来实现流量的定向分流。<br>类似地,如果你⽤到了其他⽹关⽐如 Spring Cloud Gateway 的话,思路也是⼀样的。<br>另外, Spring Cloud Gateway 配合 Spring Cloud LoadBalancer(官⽅推荐)/Ribbon 也可以实现简单的灰度发布。
基于 Apollo 动态更新配置加上其⾃带的灰度发布策略来实现灰度发布。<br>是通过修改灰度发布配置的⽅式来实现灰度发布,如果灰度的配置测试没问题的话,再全量发布配置。<br>
通过⼀些现成的⼯具来做,⽐如说 Rainbond(云原⽣应⽤管理平台)就⾃带了灰度发布解决⽅案并且还⽀持滚动发布和蓝绿发布。
Flagger 是⼀种渐进式交付⼯具,可⾃动控制 Kubernetes 上应⽤程序的发布过程。<br>通过指标监控和运⾏⼀致性测试,将流量逐渐切换到新版本,降低在⽣产环境中发布新软件版本导致的⻛险。<br>Flagger 可以使⽤ Service Mesh(App Mesh,Istio,Linkerd)或 Ingress Controller(Contour,Gloo,Nginx)来实现多种部署策略(⾦丝雀发布,A/B<br>测试,蓝绿发布)。<br>
其他
核心应用和服务优先使用更好的硬件。<br>
监控系统资源使用情况增加报警设置。<br>
注意备份,必要时候回滚。<br>
定期检查/更换硬件: 如果不是购买的云服务的话,定期还是需要对硬件进行一波检查的,对于一些需要更换或者升级的硬件,要及时更换或者升级。<br>
冗余设计
定义
冗余设计是保证系统和数据高可用的最常的手段。<br>
对于服务来说,冗余的思想就是相同的服务部署多份,<br>如果正在使用的服务突然挂掉的话,系统可以很快切换到备份服务上,大大减少系统的不可用时间,提高系统的可用性。<br>
对于数据来说,冗余的思想就是相同的数据备份多份,这样就可以很简单地提高数据的安全性。<br>
应用
高可用集群
高可用集群(High Availability Cluster,简称 HA Cluster)
同一份服务部署两份或者多份,当正在使用的服务突然挂掉的话,可以切换到另外一台服务,从而保证服务的高可用。
同城灾备
一整个集群可以部署在同一个机房,而同城灾备中相同服务部署在同一个城市的不同机房中。<br>并且,备用服务不处理请求。这样可以避免机房出现意外情况比如停电、火灾。
异地灾备
类似于同城灾备,不同的是,相同服务部署在异地(通常距离较远,甚至是在不同的城市或者国家)的不同机房中。
同城多活
类似于同城灾备,但备用服务可以处理请求,这样可以充分利用系统资源,提高系统的并发。
异地多活
将服务部署在异地的不同机房中,并且,它们可以同时对外提供服务。<br>
故障转移
简单来说就是实现不可用服务快速且自动地切换到可用服务,整个过程不需要人为干涉。
服务限流
定义
针对软件系统来说,限流就是对请求的速率进行限制,避免瞬时的大量请求击垮软件系统。<br>毕竟,软件系统的处理能力是有限的。如果说超过了其处理能力的范围,软件系统可能直接就挂掉了。<br>
限流可能会导致用户的请求无法被正确处理,不过,这往往也是权衡了软件系统的稳定性之后得到的最优解。<br>
算法
固定窗口计数器算法
固定窗口其实就是时间窗口。固定窗口计数器算法 规定了我们单位时间处理的请求数量。<br>
滑动窗口计数器算法
滑动窗口计数器算法 算的上是固定窗口计数器算法的升级版。<br>
滑动窗口计数器算法相比于固定窗口计数器算法的优化在于:它把时间以一定比例分片 。<br>
当滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。
漏桶算法
定义
我们可以把发请求的动作比作成注水到桶中,我们处理请求的过程可以比喻为漏桶漏水。<br>我们往桶中以任意速率流入水,以一定速率流出水。当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。
如果想要实现这个算法的话也很简单,准备一个队列用来保存请求,然后我们定期从队列中拿请求来执行就好了(和消息队列削峰/限流的思想是一样的)。
漏桶算法可以控制限流速率,避免网络拥塞和系统过载。但漏桶算法无法应对突然激增的流量,因为只能以固定的速率处理请求,对系统资源利用不够友好。<br>
实际业务场景中,基本不会使用漏桶算法。<br>
令牌桶算法
令牌桶算法也比较简单。和漏桶算法算法一样,我们的主角还是桶(这限流算法和桶过不去啊)。<br>不过现在桶里装的是令牌了,请求在被处理之前需要拿到一个令牌,请求处理完毕之后将这个令牌丢弃(删除)。<br>我们根据限流大小,按照一定的速率往桶里添加令牌。如果桶装满了,就不能继续往里面继续添加令牌了。<br>
令牌桶算法可以限制平均速率和应对突然激增的流量,还可以动态调整生成令牌的速率。<br>不过,如果令牌产生速率和桶的容量设置不合理,可能会出现问题比如大量的请求被丢弃、系统过载。<br>
限流对象
IP :针对 IP 进行限流,适用面较广,简单粗暴。<br>
业务 ID:挑选唯一的业务 ID 以实现更针对性地限流。例如,基于用户 ID 进行限流。<br>
个性化:根据用户的属性或行为,进行不同的限流策略。<br>例如, VIP 用户不限流,而普通用户限流。<br>根据系统的运行指标(如 QPS、并发调用数、系统负载等),动态调整限流策略。例如,当系统负载较高的时候,控制每秒通过的请求减少。
阿里的 Sentinel 还支持 基于调用关系的限流(包括基于调用方限流、基于调用链入口限流、关联流量限流等)<br>以及更细维度的 热点参数限流(实时的统计热点参数并针对热点参数的资源调用进行流量控制)。
单机限流
单机限流针对的是单体架构应用。<br>
RateLimiter
单机限流可以直接使用 Google Guava 自带的限流工具类 RateLimiter 。 RateLimiter 基于令牌桶算法,可以应对突发流量。<br>
除了最基本的令牌桶算法(平滑突发限流)实现之外,Guava 的 RateLimiter 还提供了 平滑预热限流 的算法实现。<br>
平滑突发限流就是按照指定的速率放令牌到桶里,而平滑预热限流会有一段预热时间,预热时间之内,速率会逐渐提升到配置的速率。<br>
Bucket4j
一个非常不错的基于令牌/漏桶算法的限流库。
相对于,Guava 的限流工具类来说,Bucket4j 提供的限流功能更加全面。<br>不仅支持单机限流和分布式限流,还可以集成监控,搭配 Prometheus 和 Grafana 使用。
Spring Cloud Gateway 中自带的单机限流的早期版本就是基于 Bucket4j 实现的。后来,替换成了 Resilience4j。<br>
Resilience4j
一个轻量级的容错组件,其灵感来自于 Hystrix。
一般情况下,为了保证系统的高可用,项目的限流和熔断都是要一起做的。<br>
Resilience4j 不仅提供限流,还提供了熔断、负载保护、自动重试等保障系统高可用开箱即用的功能。<br>并且,Resilience4j 的生态也更好,很多网关都使用 Resilience4j 来做限流熔断的。
因此,在绝大部分场景下 Resilience4j 或许会是更好的选择。如果是一些比较简单的限流场景的话,Guava 或者 Bucket4j 也是不错的选择。<br>
分布式限流
分布式限流针对的分布式/微服务应用架构应用,在这种架构下,单机限流就不适用了,因为会存在多种服务,并且一种服务也可能会被部署多份。<br>
方案
借助中间件架限流:可以借助 Sentinel 或者使用 Redis 来自己实现对应的限流逻辑。<br>
网关层限流:比较常用的一种方案,直接在网关层把限流给安排上了。不过,通常网关层限流通常也需要借助到中间件/框架。<br>就比如 Spring Cloud Gateway 的分布式限流实现 RedisRateLimiter 就是基于 Redis+Lua 来实现的,<br>再比如 Spring Cloud Gateway 还可以整合 Sentinel 来做限流。
如果你要基于 Redis 来手动实现限流逻辑的话,建议配合 Lua 脚本来做。<br>
减少了网络开销
原子性
如果不想自己写 Lua 脚本的话,也可以直接利用 Redisson 中的 RRateLimiter 来实现分布式限流,其底层实现就是基于 Lua 代码。
Redisson 是一个开源的 Java 语言 Redis 客户端,提供了很多开箱即用的功能,<br>比如 Java 中常用的数据结构实现、分布式锁、延迟队列等等。并且,Redisson 还支持 Redis 单机、Redis Sentinel、Redis Cluster 等多种部署架构。
RRateLimiter 的使用方式非常简单。我们首先需要获取一个RRateLimiter对象,直接通过 Redisson 客户端获取即可。然后,设置限流规则就好。<br>
降级&熔断
降级
定义
降级是从系统功能优先级的⻆度考虑如何应对系统故障。
服务降级指的是当服务器压⼒剧增的情况下,根据当前业务情况及流量对⼀些服务和⻚⾯有策略的降级,以此释放服务器资源以保证核⼼任务的正常运⾏。
特征
原因:整体负荷超出整体负载承受能⼒。
⽬的:保证重要或基本服务正常运⾏,⾮重要服务延迟使⽤或暂停使⽤。
⼤⼩:降低服务粒度,要考虑整体模块粒度的⼤⼩,将粒度控制在合适的范围内。
可控性:在服务粒度⼤⼩的基础上增加服务的可控性,后台服务开关的功能是⼀项必要配置(单机可配置⽂件),可分为⼿动控制和⾃动控制。
次序:⼀般从外围延伸服务开始降级,需要有⼀定的配置项,重要性低的优先降级,如分组设置等级 1-10,当服务需降级到某⼀个级别时,进⾏相关配置。
方式
延迟服务:⽐如发表了评论,重要服务,⽐如在⽂章中显示正常,但是延迟给⽤户增加积分,只是放到⼀个缓存中,等服务平稳之后再执⾏。
在粒度范围内关闭服务(⽚段降级或服务功能降级):⽐如关闭相关⽂章的推荐,直接关闭推荐区。
⻚⾯异步请求降级:⽐如商品详情⻚上有推荐信息/配送⾄等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进⾏降级。
⻚⾯跳转(⻚⾯降级):⽐如可以有相关⽂章推荐,但是更多的⻚⾯则直接跳转到某⼀个地址。
写降级:⽐如秒杀抢购,我们可以只进⾏ Cache 的更新,然后异步同步扣减库存到 DB,保证最终⼀致性即可,此时可以将 DB 降级为 Cache。
读降级:⽐如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种⽅式适⽤于对读⼀致性要求不⾼的场景。
分类
⾃动开关降级
超时降级:主要配置好超时时间和超时重试次数和机制,并使⽤异步机制探测回复情况。
失败次数降级:主要是⼀些不稳定的 api,当失败调⽤次数达到⼀定阀值⾃动降级,同样要使⽤异步机制探测回复情况。
故障降级:⽐如要调⽤的远程服务挂掉了(⽹络故障、DNS 故障、http 服务返回错误的状态码、rpc 服务抛出异常),则可以直接降级。<br>降级后处理⽅案:默认值(库存服务挂了,返回默认现货)、兜底数据(⼴告挂了,返回提前准备好的⼀些静态⻚⾯)、缓存(之前暂存的⼀些缓存数据)。
限流降级:当去秒杀或者抢购⼀些限购商品时,可能会因为访问量太⼤⽽导致系统崩溃,此时开发者会使⽤限流来进⾏限制访问量,当达到限流阀值,后续请求会被降级;降级后处理⽅案:排队⻚⾯(将⽤户导流到排队⻚⾯等⼀会重试)、⽆货(直接告知⽤户没货了)、错误⻚(如活动太⽕爆了,稍后重试)。
⼈⼯开关降级(秒杀、电商⼤促等)
分布式系统
需要技术和产品提前对业务和系统进⾏梳理,根据梳理结果确定哪些服务可以降级,哪些服务不可以降级,降级策略是什么,降级顺序怎么样。
熔断
定义
熔断是应对微服务雪崩效应的⼀种链路保护机制,类似股市、保险丝。
降级和熔断
降级的⽬的在于应对系统⾃身的故障,⽽熔断的⽬的在于应对当前系统依赖的外部系统或者第三⽅系统的故障。
组件
Hystrix
Hystrix 是 Netflix 开源的熔断降级组件。
Sentinel
Sentinel 是阿⾥中间件团队开源的⼀款不光具有熔断降级功能,同时还⽀持系统负载保护的组件。
Spring Retry
Resilience4J
超时&重试
定义
由于网络问题、系统或者服务内部的 Bug、服务器宕机、操作系统崩溃等问题的不确定性,系统或者服务永远不可能保证时刻都是可用的状态。<br>
为了最大限度的减小系统或者服务出现故障之后带来的影响,我们需要用到的 超时(Timeout) 和 重试(Retry) 机制。<br>
超时机制<br>
当一个请求超过指定的时间(比如 1s)还没有被处理的话,这个请求就会直接被取消并抛出指定的异常或者错误(比如 504 Gateway Timeout)。
分类
连接超时(ConnectTimeout):客户端与服务端建立连接的最长等待时间。<br>
读取超时(ReadTimeout):客户端和服务端已经建立连接,客户端等待服务端处理完请求的最长时间。实际项目中,关注比较多的还是读取超时。<br>
如果没有设置超时的话,就可能会导致服务端连接数爆炸和大量请求堆积的问题。<br>这些堆积的连接和请求会消耗系统资源,影响新收到的请求的处理。严重的情况下,甚至会拖垮整个系统或者服务。<br>
设置
超时到底设置多长时间是一个难题!超时值设置太高或者太低都有风险。
太高
会降低超时机制的有效性,比如你设置超时为 10s 的话,那设置超时就没啥意义了,系统依然可能会出现大量慢请求堆积的问题。
太低
可能会导致在系统或者服务在某些处理请求速度变慢的情况下(比如请求突然增多),<br>大量请求重试(超时通常会结合重试)继续加重系统或者服务的压力,进而导致整个系统或者服务被拖垮的问题。
重试机制<br>
重试机制一般配合超时机制一起使用,指的是多次发送相同的请求来避免瞬态故障和偶然性故障。<br>
瞬态故障可以简单理解为某一瞬间系统偶然出现的故障,并不会持久。偶然性故障可以理解为哪些在某些情况下偶尔出现的故障,频率通常较低。<br>
重试的核心思想是通过消耗服务器的资源来尽可能获得请求更大概率被成功处理。<br>由于瞬态故障和偶然性故障是很少发生的,因此,重试对于服务器的资源消耗几乎是可以被忽略的。<br>
策略
固定间隔时间重试
每次重试之间都使用相同的时间间隔,比如每隔 1.5 秒进行一次重试。
优点
实现起来比较简单,不需要考虑重试次数和时间的关系,也不需要维护额外的状态信息。
缺点
可能会导致重试过于频繁或过于稀疏,从而影响系统的性能和效率。
如果重试间隔太短,可能会对目标系统造成过大的压力,导致雪崩效应;如果重试间隔太长,可能会导致用户等待时间过长,影响用户体验。
应用
适用于目标系统恢复时间比较稳定和可预测的场景,比如网络波动或服务重启。
梯度间隔重试
根据重试次数的增加去延长下次重试时间,比如第一次重试间隔为 1 秒,第二次为 2 秒,第三次为 4 秒,以此类推。
优点
能够有效提高重试成功的几率,也能通过柔性化的重试避免对下游系统造成更大压力。
缺点
实现起来比较复杂,需要考虑重试次数和时间的关系,以及设置合理的上限和下限值。
可能会导致用户等待时间过长,影响用户体验。
应用
适用于目标系统恢复时间比较长或不可预测的场景,比如网络故障和服务故障。<br>
重试次数
重试的次数不宜过多,否则依然会对系统负载造成比较大的压力。<br>
重试的次数通常建议设为 3 次。大部分情况下,我们还是更建议使用梯度间隔重试策略。
重试幂等
超时和重试机制在实际项目中使用的话,需要注意保证同一个请求没有被多次执行。<br>
触发
客户端等待服务端完成请求完成超时但此时服务端已经执行了请求,只是由于短暂的网络波动导致响应在发送给客户端的过程中延迟了。
实现
如果要手动编写代码实现重试逻辑的话,可以通过循环(例如 while 或 for 循环)或者递归实现。
第三方开源库提供了更完善的重试机制实现,例如 Spring Retry、Resilience4j、Guava Retrying。(推荐)<br>
性能测试
不同角色看网站性能<br>
用户
根据响应速度的快慢来评判网站的性能。<br>
开发人员
项目架构是分布式的吗?<br>
用到了缓存和消息队列没有?<br>
高并发的业务有没有特殊处理?<br>
数据库设计是否合理?<br>
系统用到的算法是否还需要优化?<br>
系统是否存在内存泄露的问题?<br>
项目使用的 Redis 缓存多大?服务器性能如何?用的是机械硬盘还是固态硬盘?<br>
测试人员
响应时间、请求成功率、吞吐量。<br>
运维人员
根据基础设施和资源的利用率来判断网站的性能。
注意
性能测试之前更需要你了解当前的系统的业务场景。
历史数据非常有用。
指标
响应时间
响应时间就是用户发出请求到用户收到系统处理结果所需要的时间。
2-5-8 原则:通常来说,2 到 5 秒 页面体验会比较好,5 到 8 秒还可以接受,8 秒以上基本就很难接受了。据统计当网站慢一秒就会流失十分之一的客户。
并发数
并发数是系统能同时处理请求的数目即同时提交请求的用户数目。
吞吐量
吞吐量指的是系统单位时间内系统处理的请求数量。衡量吞吐量有几个重要的参数:QPS(TPS)、并发数、响应时间。<br>
QPS(Query Per Second):服务器每秒可以执行的查询次数。<br>
TPS(Transaction Per Second):服务器每秒处理的事务数(这里的一个事务可以理解为客户发出请求到收到服务器的过程)。<br>
并发数;系统能同时处理请求的数目即同时提交请求的用户数目。<br>
响应时间:一般取多次请求的平均响应时间。<br>
QPS(TPS) = 并发数/平均响应时间;并发数 = QPS*平均响应时间;<br>
性能计数器
性能计数器是描述服务器或者操作系统的一些数据指标如内存使用、CPU 使用、磁盘与网络 I/O 等情况。<br>
分类
性能测试
通过测试工具模拟用户请求系统,目的主要是为了测试系统的性能是否满足要求。
负载测试
对被测试的系统继续加大请求压力,直到服务器的某个资源已经达到饱和了。
压力测试
不去管系统资源的使用情况,对系统继续加大请求压力,直到服务器崩溃无法再继续提供服务。<br>
稳定性测试
模拟真实场景,给系统一定压力,看看业务是否能稳定运行。<br>
工具
后端
Jmeter:Apache JMeter 是 JAVA 开发的性能测试工具。<br>
LoadRunner:一款商业的性能测试工具。(收费)<br>
Galtling:一款基于 Scala 开发的高性能服务器性能测试工具。<br>
ab:全称为 Apache Bench 。Apache 旗下的一款测试工具,非常实用。<br>
前端
Fiddler:抓包工具,它可以修改请求的数据,甚至可以修改服务器返回的数据,功能非常强大,是 Web 调试的利器。<br>
HttpWatch: 可用于录制 HTTP 请求信息的工具。<br>
优化策略
系统是否需要缓存?<br>
系统架构本身是不是就有问题?<br>
系统是否存在死锁的地方?<br>
系统是否存在内存泄漏?(Java 的自动回收内存虽然很方便,但是,有时候代码写的不好真的会造成内存泄漏)<br>
数据库索引使用是否合理?<br>
0 条评论
下一页