前言:分布式系统面临的问题
复杂分布式体系结构中的应用程序 有数10个依赖关系,每个依赖关系在某些时候将不可避免地失败,如下图所示:
服务降级
:当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。服务熔断
:如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。服务限流
:限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。再来一个图解:
在pom.xml文件中导入以下依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
在application.yml文件中添加以下配置:
#开启hystrix熔断器
feign:
hystrix:
enabled: true
注意添加位置:(顶格书写)
8001服务提供端控制器代码:
@RestController
@RequestMapping("provider")
public class ProviderController {
@GetMapping("getInfo")
public String getInfo(){
return "这是8001端口在提供服务";
}
/**
* 模拟超时过程
* @return
*/
@GetMapping("timeOut")
public String timeOut() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
return "这是8001端口在模拟超时过程";
}
}
这里主要说明的是利用@HystrixCommand注解给特定接口实现服务降级。实现过程如下:
编写ConsumerClient类调用服务提供端:
@FeignClient(value = "service-provider")
public interface ConsumerClient {
// 测试调用
@GetMapping("/provider/getInfo")
public String getInfo();
/**
* 调用模拟超时接口
* @return
*/
@GetMapping("/provider/timeOut")
public String timeOut();
}
控制器代码如下:
@RequestMapping("consumer")
@RestController
public class ConsumerController {
@Autowired
private ConsumerClient consumerClient;
/**
* 正常方法调用
* @return
*/
@RequestMapping("getInfo")
public String getInfoByTemplate(){
return consumerClient.getInfo();
}
/**
* 模拟超时调用
* @return
*/
@RequestMapping("timeOut")
//配置熔断器,fallbackMethod为降级调用方法,commandProperties为触发服务降级配置
@HystrixCommand(fallbackMethod = "Error",commandProperties = {
//条件设置为2s后未返回结果则发生降级
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})
public String timeOut(){
//模拟程序出错
//int a=10/0;
return consumerClient.timeOut();
}
/**
* timeOut降级处理方法
* @return
*/
public String Error(){
return "这是 timeOut接口 服务出错/超时发生的降级处理方法";
}
需要注意的是:
依次启动,这里做一个补充说明,消费端端口为8777,服务提供端端口为8001,访问http://localhost:8777/consumer/timeOut,出现以下内容:
这就说明timeOut接口超时发生,走了指定的服务降级方法,这是因为在服务提供端该方法需要运行三秒钟以上,而在消费端做了配置只能等两秒,指定的时间内得不到返回的结果就会实行降级处理。
让我们将配置条件注释掉,再打开刚刚注释的模拟错误:
再次运行,结果一致:
如果不采用服务降级处理,则程序会直接出现报错,无法运行,很容易造成服务雪崩:
这里是使用@HystrixCommand+@DefaultProperties注解实现服务降级处理,@DefaultProperties是给该类中全部要做降级处理的接口方法统一制定一个降级调用方法,但是如果接口另外做了特殊指定,则调用该接口指定的降级调用方法。实现过程如下:
控制器代码如下:
@RequestMapping("consumer")
@RestController
@DefaultProperties(defaultFallback = "GlobalError")
public class ConsumerController {
@Autowired
private ConsumerClient consumerClient;
@RequestMapping("getInfo")
@HystrixCommand
public String getInfoByTemplate(){
//模拟程序出错
int a=10/0;
return consumerClient.getInfo();
}
/**
* 模拟超时调用
* @return
*/
@RequestMapping("timeOut")
//配置熔断器,fallbackMethod为降级调用方法,commandProperties为触发服务降级配置
@HystrixCommand(fallbackMethod = "Error",commandProperties = {
//条件设置为2s后未返回结果则发生降级
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})
public String timeOut(){
//模拟程序出错
//int a=10/0;
return consumerClient.timeOut();
}
/**
* timeOut降级处理方法
* @return
*/
public String Error(){
return "这是 timeOut接口 服务出错/超时发生的降级处理方法";
}
public String GlobalError(){
return "这是 全局指定 服务出错/超时发生的降级处理方法";
}
}
重新启动,访问http://localhost:8777/consumer/getInfo,发现该接口是调用的全局默认的降级方法:
访问http://localhost:8777/consumer/timeOut,发现该接口调用的还是指定的降级方法:
相信很多的小伙伴看完以上两种实现方法,发现了一个问题。我们平时在控制器里面应该就是注重业务逻辑的编写,你再引入一些全局或者指定的降级方法,这样控制器代码不就乱套了吗?代码耦合度不就高了吗?我们平时追求的就是降低代码的耦合度,尽量使得代码看起来优雅美观,还能有优美的实现方法吗?那么它就来了!这里是用@FeignClient注解来实现的,它是由一个实现了服务调用接口的类,然后在该类相应的实现中编写了降级方法。具体实现过程如下:
服务调用类:
@FeignClient(value = "service-provider",fallback = ConsumerFallback.class)
public interface ConsumerClient {
// 测试调用
@GetMapping("/provider/getInfo")
public String getInfo();
/**
* 调用模拟超时接口
* @return
*/
@GetMapping("/provider/timeOut")
public String timeOut();
}
服务调用接口实现类:
@Component
public class ConsumerFallback implements ConsumerClient {
@Override
public String getInfo() {
return "这是 @FeignClient注解 返回的服务降级办法,getInfo";
}
@Override
public String timeOut() {
return "这是 @FeignClient注解 返回的服务降级办法,timeOut";
}
}
注意说明: