环境:Java8+maven+spring boot2.x+openfeign+hystrix,本文基于微服务已经搭建完毕的情况和大家一起交流,如果有小伙伴对微服务搭建还不熟悉的话,可以看我之前的两篇文章:
springboot+openfeign 基于ribbon完成负载均衡
spring boot2.x + openfeign 实现服务提供与消费
本文demo下载:
工程下载
自定义jar包下载
关于熔断降级,举个例子:
现在有三个微服务:订单服务、积分服务、库存服务
订单服务需要调用积分服务和库存服务,当出现网络故障、机器故障、服务器宕机等情况导致即积分服务访问不通的时候,如果不做处理,后续还会有大量的请求进来请求已经访问不通的积分服务,由于积分服务没有返回,资源未释放,订单服务会不断的创建线程导致订单服务资源耗尽,这就是微服务中的“雪崩效应”,一个服务不可用,引发的整个系统崩溃。为了避免雪崩效应,我们引入了hystrix进行服务熔断、降级。
原理简介:
例如,当积分服务出现访问不通、访问超时的问题时,如果有足够多的请求,hystrix会判断某个时间段内错误率是否达到阈值,如果达到阈值后,再有请求就会启动熔断机制,这时如果请求了积分服务就直接调用fallback的路径,当断路器打开之后会开启一个时间窗,在这个时间窗内如果有请求过来断路器会放过一个请求给积分服务而不是fallback,去试探积分服务是否回复正常,如果成功则断路器关闭,否则断路器保持开启状态。这样就保证了无论积分服务是否好用,都能保证接口快速返回或快速失败。保证整个系统的正常运行,避免“雪崩效应”。
工程目录:
红框中标记的类就是我们服务熔断降级的类,下面会细讲。
由于我们只需要在服务消费者端做服务熔断和降级,故只有消费者端需要集成hystrix。
先上pom.xml
需要依赖“spring-cloud-starter-netflix-hystrix”
<?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>
<groupId>com.demo</groupId>
<artifactId>consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>consumer</name>
<description>服务消费者</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath />
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- springCloud注测中心服务 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--w微服务jar包-->
<dependency>
<groupId>com.demo</groupId>
<artifactId>feign-service-api-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
</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>
properties文件
spring.application.name=spring-cloud-consumer
server.port=9003
eureka.client.serviceUrl.defaultZone=http://192.168.1.192:1111/eureka/
logging.config=classpath:logback-spring.xml
# 配置请求GZIP压缩
feign.compression.request.enabled=true
# 配置响应GZIP压缩
feign.compression.response.enabled=true
# 配置压缩支持的MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 配置压缩数据大小的下限
feign.compression.request.min-request-size=2048
#配置负载均衡
spring-cloud-provider.ribbon.NFLoadBalancerRuleClassName = com.netflix.loadbalancer.RandomRule
#########################配置hystrix###########################################
feign.hystrix.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
其中只有两行关于hystrix的配置(springcloud真的好简单!!),
1.打开hystrix
2.设置超时时间
TestService.java
package com.demo.eurekaFeignService;
import com.demo.HystrixFallBack.TestServiceHystrix;
import com.demo.api.test.TestApi;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name = "spring-cloud-provider",path = "/",fallbackFactory = TestServiceHystrix.class)
public interface TestService extends TestApi {
}
注意,服务降级有两种实现方式:
1.消费者端本地实现远程接口,即fallback形式,此方式相对简单方便,但无法处理异常,因此个人不推荐此种方式实现的服务降级
2.消费者端本地实现 FallbackFactory<>接口,即fallbackFactory形式,此方式也比较简单,并且可以处理异常,推荐此方式实现。
本文以第二种方式实现,只需要在feign接口上添加"fallbackFactory = TestServiceHystrix.class"即可
TestServiceHystrix.java
package com.demo.HystrixFallBack;
import com.demo.eurekaFeignService.TestService;
import com.demo.model.UserTest;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class TestServiceHystrix implements FallbackFactory<TestService> {
@Override
public TestService create(Throwable throwable) {
return new TestService() {
@Override
public List<UserTest> getBigCat(UserTest userTest) {
userTest.setUserName("服务调用失败");
List<UserTest> userTests = new ArrayList<>();
userTests.add(userTest);
System.out.println(userTest.getUserName());
return userTests;
}
};
}
}
可以看到我们实现了FallbackFactory接口的create接口,返回一个实例化的TestService(因为实现了接口中的所有方法),当远程微服务出现问题时,服务降级就会调用此实例对应的方法。
至此服务熔断降级已配置完毕,启动一个provider和consumer,访问“http://localhost:9003/api/test”,可以正常访问到远程服务
关闭provider,再次访问“http://localhost:9003/api/test”,可以看到断路器已经开启,服务已被降级。
在此启动provider,在经过几次降级之后,服务会恢复正常。本文使用的是最简单的配置,其余配置都保持了默认,常用配置参考如下:
#feign接口开启hystrix
feign.hystrix.enabled=true
#是否允许超时,默认、建议true
hystrix.command.default.execution.timeout.enabled=true
#超时时间,“hystrix.command.default.execution.timeout.enabled=true”时会生效
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
#超时是否中断,默认、建议为true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true
#取消是否中断,默认、建议为false
hystrix.command.default.execution.isolation.thread.interruptOnCancel=false
#最大并发请求,默认10,建议根据实际情况设定此值
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=100
#最大并发降级请求处理上线,默认10,建议根据实际情况设此值,降级请求并发量高于此值时,抛出异常
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=20
#此属性确定断路器是否用于跟踪健康状况,以及当断路器打开的时候是否用于短路请求(使请求快速失败进入降级逻辑)。
#默认、建议true
hystrix.command.default.circuitBreaker.enabled=true
#断路器请求量阈值,默认20,建议默认,部分接口如果不能容忍此值可单独配置
#例如,如果值是20,那么如果在滑动窗口中只接收到19个请求(比如一个10秒的窗口),
# 即使所有19个请求都失败了,断路器也不会打开
hystrix.command.default.circuitBreaker.requestVolumeThreshold=10
#隔离策略,默认、建议THREAD
hystrix.command.default.execution.isolation.strategy=THREAD
#断路器等待窗口时间,此属性设置断路器打开后拒绝请求的时间量,
# 每隔一段时间( sleepWindowInMilliseconds ,单位是毫秒)允许再次尝试(也就是放行一个请求)确定是否应该关闭断路器。
#默认5000,建议保持默认
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
#断路器错误百分比阈值,设置一个错误百分比,当请求错误率超过设定值,断路器就会打开。
#默认50,建议保持默认
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50