雪崩效应
服务雪崩效应:在微服务架构中,服务众多,通常会涉及多个服务级层的调用,一旦基础服务发生故障,很可能会导致级联故障,进而造成整个系统不能用。
服务雪崩效应是一种“因服务提供者”的不可用导致“服务消费者”的不可用并将这种不可用逐渐放大的过程。
比如一个系统中A是服务提供者,B是A的服务消费者,C和D又是B的服务消费者。如果A发生故障。则会引起B不可用,B不可用会引起C和D不可用,当这种不可用像滚雪球一样逐渐放大的时候,雪崩效应就形成了。
熔断服务
熔断器的原理很简短,如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到很多类似的错误,就会强迫其以后的多个调用快速失败,不在访问远程服务器,从而防止应用程序不断的尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超市产生。
熔断器也可以使应用程序能够诊断错误是否已经修正,如果已修正,应用程序会再次尝试调用操作。熔断器模式就像是那些容易导致错误操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。熔断器是保护服务高可用的最后一道防线。
Hystrix特性
断路器机制
断路器很好理解,当Hystrix Command请求后端失败数量超过一定比例(默认为50%),断路器会切换到开路状态(Open)。这时所有的请求会直接失败而不会发送到后端服务。
断路器保持在开路状态下一段时间后(默认5秒),自动切换到半开路状态下(HALPF_OPEN)。这时会判断下一次请求返回的情况,如果请求成功,断路器切回闭路状态(CLOSED),否种重新切换会开路状态(OPEN)。
fallback
fallback相当于降级操作。对于查询操作,我们可以实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回值。fallback方法返回值一般是设置默认值或者来自缓存。
资源隔离
在Hystrix中,主要通过线程池来实现资源隔离。通常在使用的时候我们会根据调用的远程服务划分多个线程。
例如,调用产品服务的command放入A线程池,调用账户服务的command放入B线程池。这样做的主要优点是运行环境被隔离开了。这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽,也不会对系统的其他服务造成影响,但是带来的代价就是维护多个线程池会对系统带领额外的性能开销。如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出现问题,就可以使用Hystrix的信号模式(Semaphores)来隔离资源。
Feign Hystrix
因为在Feign中已经依赖了Hystrix,所以在Maven上不需要做任何改动,我们直接使用mango-consumer项目直接改造。
修改YML配置文件
添加以下内容
#开启熔断器
feign:
hystrix:
enabled : true
创建回调类
创建一个回调类MangoProducerHystrix,实现MangoProducerService接口,并实现对应方法,返回调用失败后信息,代码如下:
@Component
public class MangoProducerHystrix implements MangoProducerService {
@RequestMapping("/hello")
public String hello() {
return "sorry, hello service call failed.";
}
}
修改MangoProducerService,在@FeignClient注解中加入fallback属性,绑定我们创建的失败回调处理类,代码如下:
@FeignClient(name = "mango-producer", fallback = MangoProducerHystrix.class)
public interface MangoProducerService {
@RequestMapping("/hello")
public String hello();
}
到此配置完成,启动我们的服务
注册中心,服务消费
访问http://localhost:8005/feign/call
可以看到可以正常访问,然后我们把mango-producer服务停了,再访问
可以看到,已经熔断,返回了我写的默认错误。
注意:不知道为什么,我开始spring boot 版本是2.4.4,cloud版本是2020,这个
@FeignClient(name = "mango-producer", fallback = MangoProducerHystrix.class)
无效,虽然停了服务,是熔断了,浏览器不会返回默认的错误信息,而是报如下错误
当我降低版本后就可以了,降版本后的pom如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.louis</groupId>
<artifactId>mango-consumer</artifactId>
<version>${project.version}</version>
<packaging>jar</packaging>
<name>mango-consumer</name>
<description>mango-consumer</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.version>1.0.0</project.version>
<java.version>1.8</java.version>
<swagger.version>2.9.2</swagger.version>
<mybatis.spring.version>1.3.2</mybatis.spring.version>
<spring.boot.admin.version>2.0.4</spring.boot.admin.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--spring-boot-admin-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring.boot.admin.version}</version>
</dependency>
<!--consul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--test-->
<!--actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--hystrix-dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Hystrix Dashboard
hystrix-dashboard是针对Hystrix进行实时监控的工具,通过Hystrix Dashboard 我们可以直观地看各Hystrix Command 的请求响应时间、请求成功率等数据。
新建一个mango-hystrix工程,修改pom文件,添加相关依赖,如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.louis</groupId>
<artifactId>mango-hystrix</artifactId>
<version>${project.version}</version>
<packaging>jar</packaging>
<name>mango-hystrix</name>
<description>mango-hystrix</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.version>1.0.0</project.version>
<java.version>1.8</java.version>
<spring.boot.admin.version>2.0.4</spring.boot.admin.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--consul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--spring-boot-admin-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring.boot.admin.version}</version>
</dependency>
<!--hystrix-dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!--srping cloud-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改启动类
在启动类添加@EnableHystrixDashboard开启熔断监控支持、
@EnableDiscoveryClient开启服务发现支持。
注解,。
@EnableHystrixDashboard
@SpringBootApplication
public class MangoHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(MangoHystrixApplication.class, args);
}
}
修改YML配置文件
修改配置文件为yml,把服务注册到注册中心
server:
port:8501
spring:
application:
name: mango-hystrix
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: ${spring.application.name} #注册到consul的服务名称
配置监控路径
打开mango-consumer工程
修改启动类,添加服务监控路径配置
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
到此我们配置完成,依次启动monitor、product、consumer、hystrix服务
启动后我们访问http://localhost:8501/hystrix
在地址栏输入http://localhost:8005/hystrix.stream 点击按钮
刚进来我们看到是空的
然后我们反复请求http://localhost:8005/feign/call,再看一下监控界面就有数据了
上面我们集成了Hystrix Dashboard ,使用Hystrix Dashboard可以看到单个应用内的服务信息。因为我们是微服务,显然这是不够,我们还需要一个能让我们汇总系统内多个服务的数据并显示到Hystrix Dashboard上,所以我们要集成Turbine。
首先,添加依赖
在mango-hystrix的pom中添加turbine依赖包。
注意:因为我们使用的注册中心是consul,所以需要排除默认的euraka包,不然会出现冲突,导致启动报错。
<!--turbine-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</exclusion>
</exclusions>
</dependency>
修改启动类
在启动类上添加@EnableTurbine开启turbine支持
@EnableTurbine
@EnableHystrixDashboard
@EnableDiscoveryClient
@SpringBootApplication
public class MangoHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(MangoHystrixApplication.class, args);
}
}
修改yml配置文件
添加Turbine配置信息
turbine:
instanceUrlSuffix: /hystrix.stream # 指定收集路径
appConfig: mango-consumer # 指定了需要收集监控信息的服务名,多个以“,”进行区分
clusterNameExpression: "'default'" # 指定集群名称,若为default则为默认集群,多个集群则通过此配置区分
combine-host-port: true # 此配置默认为false,则服务是以host进行区分,若设置为true则以host+port进行区分
到此我们已经配置完成,依次启动monitor、product、consumer、hystrix服务
启动后访问http://localhost:8501/hystrix
在地址栏输入http://localhost:8501/turbine.stream
为了模拟多个消费者服务,我将mango-consumer复制了一份出来
修改了端口,在服务提供者mango-product中新增了hello2方法,
在浏览器访问http://localhost:8005/feign/call 和http://localhost:8006/feign/call2
可以看到我们的Turbine中已经监控到了两个消费者的访问信息
好了,到此我们就完成了配置
看完了,别忘了点赞哦