为什么需要服务降级?
在分布式架构服务中,服务的调用链路非常的长,这样就会导致服务与服务之间的连接关系越来越复杂。为了保证服务的稳定运行,防止(服务雪崩)雪崩效应。
瞬间把服务器的CPU或者内存直接打满。导致服务器内存溢出或者宕机的情况。那么这个时候就需要一种链路中断或者做好服务降级。
Hystrix是什么东东?
Hystrix能够提供什么样的功能?
- 服务降级 fallback
- 服务熔断 break
- 服务限流 flowlimit
访问github地址查看如何使用: github.com/Netflix/Hystrix/wiki/How-To-Use
项目实战
pom.xml引入
<?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">
<parent>
<artifactId>spring-cloud-client</artifactId>
<groupId>com.wolf.boy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-consumer-hystrix-client</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--额外增加hystrix即可-->
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
然后对应的服务注册中心还是连接到Eureka,所有引入了eureka-client客户端
yml配置
- 服务调用者
############对应hystrix并没有增加额外的配置信息######
#下面这些配置信息都是上一章openFeign引入的
server:
port: 7773
spring:
application:
name: spring-cloud-consumer-hystrix-client
#设置openFeign的超时时间
ribbon:
ReadTimeout: 1000
ConnectTimeout: 1000
#Eureka服务提供者配置
eureka:
client:
#false表示不向注册中心注册自己(你自己就是一个eureka服务,没必要把自己注册进去了)
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
#这个主要结合discoveryClient使用
fetch-registry: true
service-url:
#单机版
defaultZone: http://localhost:9999/eureka
#用于服务降级,在注解@FeignClient中添加fallbackFactory属性值
feign:
hystrix:
enabled: true #开启对hystrix的支持
新增配置
feign: hystrix: enabled: true #开启对hystrix的支持
- 服务提供者
############对应hystrix并没有增加额外的配置信息######
#下面这些配置信息都是上一章openFeign引入的
server:
port: 7773
spring:
application:
name: spring-cloud-consumer-hystrix-client
#设置openFeign的超时时间
ribbon:
ReadTimeout: 1000
ConnectTimeout: 1000
#Eureka服务提供者配置
eureka:
client:
#false表示不向注册中心注册自己(你自己就是一个eureka服务,没必要把自己注册进去了)
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
#这个主要结合discoveryClient使用
fetch-registry: true
service-url:
#单机版
defaultZone: http://localhost:9999/eureka
主启动类
- 客户端
@SpringBootApplication
@EnableEurekaClient
//启用feign客户端
@EnableFeignClients
//开启熔断
@EnableHystrix
public class RunClientMain {
public static void main(String[] args) {
SpringApplication.run(RunClientMain.class,args);
}
}
- 服务提供者
@SpringBootApplication
@EnableEurekaClient
//启用feign客户端
@EnableFeignClients
//开启回路
@EnableCircuitBreaker
public class RunClientMain {
public static void main(String[] args) {
SpringApplication.run(RunClientMain.class,args);
}
}
服务降级代码
- 简单版
在客户端的controller代码里面
@RequestMapping("consumer/openFeign/getTimeout")
//使用hystrix,并且设置了fallback降级对应调用的方法,然后配置了一个请求超时的设置
@HystrixCommand(fallbackMethod = "fallbackMethod",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
public JsonResult getUserTimeOut() {
JsonResult obj = demoService.getUserTimeOut();
System.out.println(obj);
return obj;
}
JsonResult fallbackMethod() {
return new JsonResult(-1, "接口响应超时,服务降级处理,返回默认提示");
}
- 如果一个controller里面有很多方法都想用默认的,怎么办;这个时候需要有一个通用的或者默认的降级代码
@RestController
@RequestMapping("/user/V2/")
@DefaultProperties(defaultFallback = "fallbackMethod")
public class DemoController2 {
@Autowired
private DemoService demoService;
@RequestMapping("consumer/openFeign/get")
public JsonResult getOpenFeignUser() {
JsonResult obj = demoService.getUser();
System.out.println(obj);
return obj;
}
@RequestMapping("consumer/openFeign/getTimeout")
@HystrixCommand
public JsonResult getUserTimeOut() {
JsonResult obj = demoService.getUserTimeOut();
System.out.println(obj);
return obj;
}
JsonResult fallbackMethod() {
return new JsonResult(-1, "接口响应超时,服务降级处理,返回默认提示");
}
}
变化点
1、在请求的类上加入注解
@DefaultProperties(defaultFallback = "fallbackMethod")
并设置降级调用方法
2、在原controller里面的方法上,只需要设置@HystrixCommand即可,无需配置其他参数。
- 对openFeign设置的接口的服务,设置默认的降级方法
@Component
//@FeignClient(value = "SPRING-CLOUD-EUREKA-PROVIDER-SERVER-01")
@FeignClient(value = "SPRING-CLOUD-EUREKA-PROVIDER-SERVER-01",fallback = DemoServiceFallBack.class)
public interface DemoService {
/**
* 通过springMvc的方式,指定服务提供者访问地址
*
*
* @return
*/
@RequestMapping("/user/get")
public JsonResult getUser();
@RequestMapping("/user/get/timeout")
public JsonResult getUserTimeOut();
}
@Component
public class DemoServiceFallBack implements DemoService {
@Override
public JsonResult getUser() {
return new JsonResult(0, "非常抱歉,基于DemoServiceFallBack实现,没有拿到默认内容");
}
@Override
public JsonResult getUserTimeOut() {
return new JsonResult(0, "非常抱歉,基于DemoServiceFallBack实现,没有拿到默认内容");
}
}
服务熔断
代码讲解
/**
* 熔断
*
* @return
*/
@RequestMapping("get/timeoutRd")
//使用hystrix,并且设置了fallback降级对应调用的方法,然后配置了一个请求超时的设置
@HystrixCommand(fallbackMethod = "fallbackMethodRd", commandProperties = {
//为什么配置信息该这么配置:看HystrixCommandProperties这个类
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启熔断服务
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")//失败率达到多少后,进入熔断
})
public JsonResult getUserTimeOutRd(String val) {
if (val == null) {
long sleepTime = 3L;
try {
TimeUnit.SECONDS.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
Integer i = Integer.parseInt(val);
}
return new JsonResult(0, "从服务端口:" + serverPort + "获取用户信息!开始使用熔断策略,响应你" + val);
}
JsonResult fallbackMethodRd(String val) {
return new JsonResult(-1, "从服务端口:" + serverPort + "服务端处理业务处理繁忙,返回默认!" + val + "格式有误");
}
如果请求路径
http://localhost:8881/user/get/timeoutRd?val=ddss 不携带val值,那么请求conroller就拿到val=ddss,然后转int就出错了。如果错误率达到60%的时候,就熔断。这个时候就算给予正确的
http://localhost:8881/user/get/timeoutRd?val=1 请求,客户端输出的效果还是熔断效果。因为这个时候已经发生了熔断
http://localhost:8881/user/get/timeoutRd?val=ddss 把这个请求一直多来几次,让失败率大于60%,然后在访问http://localhost:8881/user/get/timeoutRd?val=1
输出:
{"code":-1,"msg":null,"result":"从服务端口:8881服务端处理业务处理繁忙,返回默认!1格式有误"}
{"code":-1,"msg":null,"result":"从服务端口:8881服务端处理业务处理繁忙,返回默认!ddss格式有误"}
过几秒后,在访问:http://localhost:8881/user/get/timeoutRd?val=1
{"code":0,"msg":null,"result":"从服务端口:8881获取用户信息!开始使用熔断策略,响应你1"}
看效果就是正确的响应。熔断后自动完成恢复!
结论:
所有配置说明: