2024年最新SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?,程序员进阶

总目录展示

该笔记共八个节点(由浅入深),分为三大模块。

高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这4个方面重点介绍。

一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。

高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。


篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)

由于内容太多,这里只截取部分的内容。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在Spring Boot的启动类上添加@EnableCircuitBreaker注解即可,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

如果我们集成了Spring Cloud Netflix Eureka,还有一种简便的写法,可以直接使用@SpringCloudApplication注解来替代其他注解,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

通过查看@SpringCloudApplication的源码可以发现,实际上SpringCloudApplication就包括 了 @SpringBootApplication、@EnableDiscoveryClient和@EnableCircuitBreaker 3个注解,SpringCloudApplication的源码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

这样就完成了断路器的基本配置,之前提到过,断路器的开启有触发条件,通常通过设置阈值来作为断路器的触发条件,Spring Cloud Netflix Hystrix 提供了几种常用的阈值配置 。 首先由circuitBreakerEnabled的配置项来配置是否启用断路器,默认开启,但是如果使用Spring Cloud Feign作为远程调用框架,那么这里需要额外的配置来启动Hystrix,在application.yml中添加如下配置即可。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

其实,设置阈值就是和现实情况的统计做对比,如服务失败比例、最大并发数等,一旦统计的结果超过阈值,就开启断路器。既然做统计,就会有一个维度,通过观察Hystrix的源码,我们发现在Hystrix中称这个维度为Statistical Window Buckets(统计窗口时段),即采用时间的维度,默认10s,也就是10个Bucket,通过在application.yml的配置设置

metrics.rollingStats.numBuckets的值可以改变这个窗口时段。例如,想设置统计时间是20s,那么代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

需要注意的是,numBuckets的配置不能动态更新,如果修改了,只能重启服务器才能生效。断路器提供了错误百分比的阈值设置,表示错误的百分比,默认是50%。也就是说,如果我们的numBuckets是20,就表示在20s内,请求的失败比例超过50%时,断路器就开启,当然可以通过配置来修改它,具体如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

断路器还提供了最大请求数的阈值设置,表示在指定的时段内,请求数量超过了阈值,则启动断路器,这项配置主要针对高并发时服务负载过大的情况下,有效地缓解服务的压力,做到负载保护,这也是断路器的一个比较核心的功能,对应的配置如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

断路器中还有个配置,称为sleepWindowInMilliseconds,源码中的注释是seconds that we will sleep before trying again after tripping the circuit(我们将会切断线路之后在重试之前休眠的秒数),意思就是断路器一旦启动,就会熔断之前的请求线路,当新的请求进来时,则会采用一定的降级策略来处理请求,但断路器并不会一直运行,在一定时间后会进入半开启状态,即会释放一部分的请求进行重试,若重试结果正常则断路器关闭,若仍然有问题,则断路器再由半开启状态进入完全开启状态。sleepWindowInMilliseconds就是用来配置断路器开启后多长时间会进入半开启状态的,即配置断路器的工作时间,具体如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

常用的配置还有很多,如设置请求的超时时间 :

execution.isolation.thread、timeoutIn Milliseconds等,这里就不一一介绍了,其他的阈值配置大家可以通过查看GitHub的Netflix Hystrix的教程wiki,或者查看HystrixCommandProperties类的源码来学习。

服务降级

====

之前讲解了开启断路器的方法和断路器启动的阈值配置,那么断路器开启后会如何处理新到来的请求呢?最常用的就是采用服务降级的方式,即提前指定好降级方法,当断路器启动时,则调用降级方法而不再调用原来的服务,以达到服务降级、保护负载的目的。

下面来看一下在Spring Cloud Netflix Hystrix中如何做到服务降级。其实很简单,如果在服务消费者端使用的是SpringCloudRibbon,那么只需在调用服务的方法上增加@HystrixCommand注解,然后指定对应熔断后调用的方法名即可,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在上述代码中,fallbackMethod="findUserNameByIdFallback"就指定了服务被熔断时的降级方法,当断路器启动后,原来调用restTemplate的方法(findUserNameById)就不会被执行,findUserNameById 方法将直接返回findUserNameByIdFallback的执行结果(ZhangGang)。

在之前介绍过,Spring Cloud Feign是对Spring Cloud Ribbon的封装,提供了更简便的远程方法调用方式,其实Spring Cloud Feign不仅封装了Ribbon,还封装了Hystrix,因为几乎在微服务架构中每个服务调用方都需要使用断路器,Spring Cloud干脆就开发了一个集成了远 程 调 用 ( Spring Cloud Ribbon ) 和 断 路 器 ( Spring Cloud Netflix Hystrix)的框架,它就是Spring Cloud Feign。

那么,在Feign中如何使用服务降级呢?配置代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在以上代码中,Spring Cloud Feign可以直接定义类级别的fallback,也可以直接在Feign的接口注解@FeignClient 中 配 置FallbackFactory ,指定降级服务为UserFeignClientFallBack。

UserFeignClientFallBack的代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

由以上代码可以看出,fallback的类继承了FallbackFactory,实现的create方法会创建一个新的服务实现,用来代替之前的真实方法来达到降级的目的。

线程隔离

====

线程隔离又称为舱壁机制。一般为了提高船的生存能力,会将船体分为多个舱室,当船体发生事故导致进水时,只有受损的舱室会进水,其他舱室由于舱壁的隔离,并不会受到影响,从而将船体浮力的下降控制到最小。

Spring Cloud Netflix Hystrix也采用了同样的设计原理来保护微服务应用,在微服务的远程调用中,如果所有的请求都在一个线程池中,一旦有个别请求响应缓慢,这些请求可能会不断地消耗可用的资源,直至占满整个资源,其他的请求都会进入等待队列,从而拖垮整个应用。

那么,如何做到线程隔离呢?Hystrix提供了两种实现方式:线程池、信号量。

1. 线程池

线程池的做法很简单,可以将同一个请求分到同一个线程池中,使不同的请求拥有不同的线程池,这样每个请求就像有了自己的舱室,当其中一个舱室由于故障导致线程池被占满后,并不会影响其他“舱室”的请求。

Hystrix采用命令模式来完成线程池隔离线程的功能(命令模式是一种数据驱动的设计模式,也属于行为型模式)。也就是说,请求会以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适对象,并把该命令传给相应的对象,由该对象执行命令。图3.3所示为Hystrix的命令模式+线程池模式实现线程隔离的方式。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

由图3.3可知,当依赖B调用的后端服务故障时,请求可能会超时或报错,但是只会阻碍自己资源池内的请求,并不会影响依赖A和依赖C中的请求。

Hystrix中要执行Command有4种方式:execute()、queue()、observe()和toObservable()。详细说明如下。

(1)execute()是以阻塞的方式运行Command的,在执行时会先以与queue()同样的方式获得Future对象,然后调用Future的get方法,get方法会阻塞execute的执行,直到方法运行完成。

(2)queue()是以非阻塞的方式运行Command的,调用queue方法会直接返回Future对象。需要调用者使用get方法来获取Command的返回,get是阻塞式的。

(3)observe()表示立即订阅并开始执行Command,然后返回一个Observable,当subscribe到该对象时,会重新触发流程的排序和通知。

( 4 ) toObservable() 与 observe 类 似 , 返 回 Observable , 但Command不会立即执行,必须subscribe后才能真正开始执行命令的流程。

下面来看一个简单的HelloCommand,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在上述代码中,要执行一个hello的方法,那么首先可以定义一个HelloCommand,继承HystrixCommand,再通过构造器将参数传递到Command,然后通过设置HystrixCommand GroupKey.Factory.asKey(“HelloGroup”)的方法返回HystrixCommandGroupKey,Hystrix通过HystrixCommandGroupKey来定义线程池,当HystrixCommandGroupKey相同时,则会在同一个线程池内执行,最后通过getFallback方法来定义断路后的返回,当我们在执行execute时,run方法首先被执行,然后执行异常后,getFallback将被执行,以达到优雅的降级。

2. 信号量

尽管线程池的做法能够优雅地将应用程序的依赖服务保护起来,而不会在依赖服务出错时影响到服务本身,但是通常线程池更适合于远程调用依赖服务的模式,当我们在调用一些本地依赖服务时,或者网络开销基本忽略不计、服务响应延迟极低时,就没有必要再拥有另外一个线程,线程池本身就会增加一定计算的开销,从而影响到应用程序本身的性能。

Hystrix提供了信号量作为另一种方式来实现线程隔离,信号量又称为计数器,其实就是对任何给定依赖项的并发调用数进行计数,然后限制信号量的大小,而不是使用线程池/队列大小。因此,信号量的开销是很低的,其前提是保障依赖服务能够快速地返回异常,所以通常我们在非远程的依赖服务的调用中使用信号量来做线程隔离。

那么,具体是如何使用的呢?其实相比于线程池的用法,使用信号量只需在Command的构造器中设置隔离策略为SEMAPHORE,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

需要注意的是,在使用线程隔离时,无论是信号量的方式还是线程池的方式,客户端在调用依赖服务时都需要有超时的设置来防止服务被无限期阻塞,避免隔离区长期饱和。

请求合并

====

除提供基本的断路器和服务降级功能之外,Hystrix还有其他的高级功能,如请求合并和请求缓存。请求合并就是把多次请求合并为一次请求,为什么要合并请求,合并什么样的请求?带着这些问题,我们来了解一下请求合并的功能。

请求合并是在前端开发中常用的手段。例如,我们往往将一些页面的JS、CSS和图片分别合并到一个文件中,如webpack,然后只通过一次请求就可以加载这个文件;再如,一些BFF(Backend For Frontend)的框架,也都将短时间内的请求合并为一次请求进行发送,这些都是请求合并的体现,这样做可以减少与后端服务建立连接的次数,有效地减少不必要的网络消耗,提高系统性能和负载。

那么,后端的请求合并是如何做的呢?请求未合并和合并的对比具体如图3.4所示。

我的面试宝典:一线互联网大厂Java核心面试题库

以下是我个人的一些做法,希望可以给各位提供一些帮助:

整理了很长一段时间,拿来复习面试刷题非常合适,其中包括了Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等,且还会持续的更新…可star一下!

image

283页的Java进阶核心pdf文档

Java部分:Java基础,集合,并发,多线程,JVM,设计模式

数据结构算法:Java算法,数据结构

开源框架部分:Spring,MyBatis,MVC,netty,tomcat

分布式部分:架构设计,Redis缓存,Zookeeper,kafka,RabbitMQ,负载均衡等

微服务部分:SpringBoot,SpringCloud,Dubbo,Docker

image

还有源码相关的阅读学习

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

990)]

还有源码相关的阅读学习

[外链图片转存中…(img-iCeJmBjI-1715226453990)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值