SpringCloud(五):Hystrix的请求熔断与服务降级【Greenwich 版】

Hystrix的功能

1.请求熔断:当HystrixCommand请求后端服务失败数量超过一定比例(默认50%),断路器会切换到开路状态(Open).这时所有请求会直接失败而不会发送到后端服务.断路器保持在开路状态一段时间后(默认5秒),自动切换到半开路状态(HALF-OPEN).这时会判断下一次请求的返回情况,如果请求成功,断路器切回闭路状态(CLOSED),否则重新切换到开路状态(OPEN).如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用

2.服务降级:Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值.fallback方法的返回值一般是设置的默认值或者来自缓存.告知后面的请求服务不可用了

3.线程隔离:Hystrix在用户请求和服务之间加入了线程池。Hystrix为每个依赖调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。线程数是可以被设定的。 原理:用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,则会进行降级处理,用户的请求不会被阻塞,至少可以看到一个执行结果(例如返回友好的提示信息),而不是无休止的等待或者看到系统崩溃。

4、请求缓存:这个缓存是基于request的,因为每次请求来之前都必须HystrixRequestContext.initializeContext();进行初始化,每请求一次controller就会走一次filter,上下文又会初始化一次,前面缓存的就失效了,又得重新来。

5、请求合并:把重复的请求批量的用一个HystrixCommand命令去执行,以减少通信消耗和线程数的占用,默认合并10ms内的请求
优点:减少了通信开销,
缺点:请求延迟增加

创建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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.top</groupId>
        <artifactId>spring-cloud</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>hystrix-client-8007</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hystrix-client-8007</name>
    <description>Hystrix客户端8007</description>
    <packaging>jar</packaging>

    <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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

</dependency>

添加配置,同样到注册中心注册服务

server:
  port: 8007
spring:
  application:
    name: hystrix-client-8007
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8001/eureka/,http://localhost:8002/eureka/

在启动类上添加@EnableCircuitBreaker表示允许熔断器
启动类上配置一个RestTemplate ,等会用这个去调用服务

@EnableEurekaClient
@EnableCircuitBreaker
@SpringBootApplication
public class HystrixClient8007Application {

    public static void main(String[] args) {
        SpringApplication.run(HystrixClient8007Application.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

HystrixCommand继承方式

public class MyHystrixCommand extends HystrixCommand<String> {

    private RestTemplate restTemplate;

    public MyHystrixCommand(RestTemplate restTemplate){
        super(HystrixCommandGroupKey.Factory.asKey("MyHystrixCommand"));
        this.restTemplate = restTemplate;
    }

	//HystrixCommand会执行这个方法发送请求
    @Override
    protected String run() throws Exception {
        return restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody();
    }

	//HystrixCommand会在熔断器开路、线程池拒绝、run方法执行过程中出现错误、超时等情况时执行该降级方法
    @Override
    protected String getFallback() {
        return "调用eureka-client服务失败";
    }
}

controller方法

@RestController
public class MyHystrixController {
    @Autowired
    private RestTemplate restTemplate;

    //同步执行
    @RequestMapping("/execute")
    public void execute(){
        MyHystrixCommand myHystrixCommand = new MyHystrixCommand(restTemplate);
        System.out.println(myHystrixCommand.execute());
    }

    //异步执行
    @RequestMapping("/queue")
    public void queue() throws ExecutionException, InterruptedException {
        MyHystrixCommand myHystrixCommand = new MyHystrixCommand(restTemplate);
        System.out.println(myHystrixCommand.queue().get());
    }
}

首先启动两个注册中心,两个客户端,接着启动Hystrix-client-8007模块,进行访问
在这里插入图片描述
当我们关掉8004客户端时,会看到下面的情况,首先服务依然会轮询访问,当请求发送到8004客户端时,这时服务会调用失败,就会走hystrix的降级方法(getFallback)。最后会熔断掉8004的通道,将请求全部发送到8003客户端。当8004重启上线之后,服务又会接着轮询发送请求。
在这里插入图片描述

HystrixCommand注解方式

@Service
public class MyHystrixService {

    @Autowired
    private RestTemplate restTemplate;

    //同步请求
    @HystrixCommand(fallbackMethod = "getFallBack")
    public String execute(){
        return restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody();
    }

    //异步请求
    @HystrixCommand(fallbackMethod = "getFallBack")
    public Future<String> queue(){
        return new AsyncResult<String>() {
            @Override
            public String invoke() {
                return restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody();
            }

            @Override
            public String get() throws UnsupportedOperationException {
                return invoke();
            }
        };
    }

    public String getFallBack(){
        return "调用eureka-client服务失败";
    }
}

这里要注意,异步请求的时候同时要实现get方法,否则会报这个错,点进get源码看一下就知道了
java.lang.UnsupportedOperationException: AsyncResult is just a stub and cannot be used as complete implementation of Future

controller中添加方法

	@Autowired
    private MyHystrixService myHystrixService;

    //同步执行
    @RequestMapping("/execute2")
    public void execute2(){
        System.out.println(myHystrixService.execute());
    }

    //异步执行
    @RequestMapping("/queue2")
    public void queue2() throws ExecutionException, InterruptedException {
        System.out.println(myHystrixService.queue().get());
    }

测试结果和上面一样,这里就不演示了。

HystrixObservableCommand继承方式

HystrixObservableCommand,其实这种模式就运用了Java的RX编程中,RxJava 是基于Java观察者设计模式的。

RxJava 最核心的两个东西是Observable(被观察者,事件源)和Subscriber(观察者)。Observable 发出一系列事件,Subscriber 处理这些事件。一个 Observable 可以发出零个或者多个事件,直到结束或者出错。每发出一个事件,就会调用它的Subscriber的onNext方法,最后调用Subscriber.onCompleted()或者Subscriber.onError()结束

public class MyHystrixObservableCommand extends HystrixObservableCommand<String> {

    private RestTemplate restTemplate;

    public MyHystrixObservableCommand(RestTemplate restTemplate){
        super(HystrixCommandGroupKey.Factory.asKey("MyHystrixObservableCommand"));
        this.restTemplate = restTemplate;
    }

    //执行逻辑
    @Override
    protected Observable<String> construct() {
        return Observable.create(subscriber -> {
            try {
                if (!subscriber.isUnsubscribed()){
                    //将结果收集起来
                    subscriber.onNext(restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody());
                    subscriber.onNext(restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody());
                    subscriber.onNext(restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody());
                    //通知执行完成
                    subscriber.onCompleted();
                }
            } catch (Exception e) {
                subscriber.onError(e);
            }
        });
    }

    //服务降级
    @Override
    public Observable<String> resumeWithFallback(){
        return Observable.create(subscriber -> {
            try {
                if (!subscriber.isUnsubscribed()){
                    subscriber.onNext("调用eureka-client服务失败");
                    subscriber.onCompleted();
                }
            } catch (Exception e) {
                subscriber.onError(e);
            }
        });
    }
}

controller方法:

@RestController
public class MyHystrixObservableController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/observe")
    public void observe(){
        List<String> list = new ArrayList<>();

        MyHystrixObservableCommand myHystrixObservableCommand = new MyHystrixObservableCommand(restTemplate);
        //冷执行
        //Observable<String> observe = myHystrixObservableCommand.toObservable();
        
        //热执行
        Observable<String> observe = myHystrixObservableCommand.observe();
        observe.subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                System.out.println("执行完成");
            }

            @Override
            public void onError(Throwable throwable) {
                throwable.printStackTrace();
            }

            @Override
            public void onNext(String result) {
                System.out.println("收集结果");
                list.add(result);
            }
        });

        System.out.println(list);
    }
}

执行的结果:
在这里插入图片描述
关闭8004客户端的访问结果,请求发送到8003客户端时正常,当发送到8004时会调用降级方法。最后hystrix会熔断掉8004的通道,将请求全部发送到8003,直到8004修复上线
在这里插入图片描述

HystrixObservableCommand注解方式

@Service
public class MyHystrixObservableService {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * ObservableExecutionMode.EAGER 热执行(默认)
     * ObservableExecutionMode.LAZY 冷执行
     * @return
     */
    @HystrixCommand(fallbackMethod = "fallback", observableExecutionMode = ObservableExecutionMode.LAZY)
    public Observable<String> construct(){
        return Observable.create(subscriber ->{
            try {
                if (!subscriber.isUnsubscribed()){
                    //将结果收集起来
                    subscriber.onNext(restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody());
                    subscriber.onNext(restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody());
                    subscriber.onNext(restTemplate.getForEntity("http://eureka-client/sayHello/",String.class).getBody());
                    //通知执行完成
                    subscriber.onCompleted();
                }
            }catch (Exception e){
                subscriber.onError(e);
            }

        });
    }

    public Observable<String> fallback(){
        return Observable.create(subscriber -> {
            try {
                if (!subscriber.isUnsubscribed()){
                    subscriber.onNext("调用eureka-client服务失败");
                    subscriber.onCompleted();
                }
            } catch (Exception e) {
                subscriber.onError(e);
            }
        });
    }
}

controller层面的代码调用和上面类似:

	@Autowired
    private MyHystrixObservableService myHystrixObservableService;

    @RequestMapping("/observe2")
    public void observe2(){
        List<String> list = new ArrayList<>();

        Observable<String> observe = myHystrixObservableService.construct();
        observe.subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                System.out.println("执行完成");
            }

            @Override
            public void onError(Throwable throwable) {
                throwable.printStackTrace();
            }

            @Override
            public void onNext(String result) {
                System.out.println("收集结果");
                list.add(result);
            }
        });
        System.out.println(list);
    }

执行结果和上面一样。

另外服务降级方法还可以进行二次降级,但是一般没人这么干
如果想在降级方法中获取异常,只需要加上 Throwable就可以

@HystrixCommand(fallbackMethod = "fallback2")
public String fallback(Throwable throwable){
    System.out.println(throwable.getMessage());
    return "error";
}

总结:
HystrixCommand依赖调用方法是run,服务降级方法是getFallback
HystrixObservableCommand依赖调用方法是construct,服务降级方法是resumeWithFallback

HystrixCommand是发出一个请求,返回一个结果

  • execute() — 从依赖的服务返回一个单一的结果对象,同步阻塞式;
  • queue() — 返回一个Future对象,异步非阻塞式;

HystrixObservableCommand可以发出多个请求,返回一个结果

  • observe() — 返回Obervable对象,热执行
  • toObservable() — 返回Obervable对象,冷执行

热执行就是不管你事件有没有注册完(onCompleted(),onError,onNext这三个事件注册),就去执行我的业务方法即(HystrixObservableCommand实现类中的construct()方法)
冷执行就是,事件监听方法注册完成后,才执行业务方法

相关阅读

项目代码
SpringCloud 汇总【Greenwich 版】
SpringCloud(一):Eureka注册中心【Greenwich 版】
SpringCloud(二):Ribbon负载均衡【Greenwich 版】
SpringCloud(三):Feign声明式服务调用【Greenwich 版】
SpringCloud(四):Hystrix熔断器介绍【Greenwich 版】
SpringCloud(五):Hystrix的请求熔断与服务降级【Greenwich 版】
SpringCloud(六):Hystrix的请求合并【Greenwich 版】
SpringCloud(七):Hystrix仪表盘与Turbine集群监控【Greenwich 版】
SpringCloud(八):Zuul网关【Greenwich 版】
SpringCloud(九):Config配置中心【Greenwich 版】
SpringCloud(十):Bus消息总线【Greenwich 版】
SpringCloud(十一):Stream消息驱动 + RabbitMQ【Greenwich 版】
SpringCloud(十二):Sleuth链路跟踪【Greenwich 版】

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值