10、Hystrix介绍/第一个Hystrix程序

90 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情

10、Hystrix介绍/第一个Hystrix程序

一、Hystrix介绍

实际问题:

image.png

  用户访问服务A模块,服务通过 Web 接口或者其他方式访问基础服务模块,基础服务模块访问数据库。   如果数据库因为某些原因变得不可用,基础服务将会得到“数据库无法访问”的信息, 并且会将此信息告知服务A模块。在出现问题时,用户不断地请求服务A模块,而服务A模块则继续请求基础服务模块 基础服务模块仍然不停地连接有问题的数据库直到超时,大量的用户请求(包括重试的请求〉会发送过来,整个应用不堪重负。   实际情况可能更加糟糕,用户请求不停地发送给服务A模块,而由于数据库的原因,基础服务模块迟迟无法响应,有可能造成整个机房的网络阻塞, 受害 的不仅仅是该应用程序,机房中的所有服务都有可能因为网络原因而瘫痪

集群容错框架 Hystrix

  在分布式环境中,总会有一些被依赖的服务会失效,例如像网络短暂无法访问、服务器宕机情况。 Hystrix是Netflix 下的一个Java 库, Spring Cloud将Hystrix 整合到 Netflix项目 中, Hystrix 通过添加延迟阑值以及容错的逻辑,来帮助我们控制分布式系统间组件的交互。 Hystrix 通过隔离服务间的访问点、停止它们之间的级联故障、提供可回退操作来实现容错。   例如我们前面所讲到的问题,如果数据库层面出现问题,服务A模块在访问基础模块时必定会出现超时的情况,此时可以将基础模块隔离开来,服务A在短时间内不再调用础模块,并且快速响应用户的请求,从而保证服务A自身乃至整个集群的稳定性,这是Hystrix 可以解决的问题。 加入容错机制,当出现前面所说的问题时,原来的应用程序将变为下图所示的结构。   如图 ,当前基础服务模块或者数据库不可用时,服务A将对其进行“熔断”,在一定的时间内,服务A都不会再调用基础服务,以维持本身的稳定

image.png

Hystrix 的功能

Hystrix主要实现以下功能。

  • 当所依赖的网络服务发生延迟或者失败时 对访问的客户端程序进行保护, 就像前 面例子中对服务A模块进行保护一样。
  • 在分布式系统中,停止级联故障。
  • 网络服务恢复正常后,可以快速恢复客户端的访问能力
  • 调用失败时执行服务回退。
  • 可支持实时监控、报警和其他操作。 接下来,我们将讲述 Hystrix 的相关功能。

二、第一个Hystrix程序

准备两个服务接口: 一个正常的接口,一个睡眠10秒,Hystrix默认1秒不出结果,就认为请求不通

@GetMapping("/hello")
@ResponseBody
public String hello() {
    return "Hello World";
}

@GetMapping("/errorHello")
@ResponseBody
public String errorHello() throws InterruptedException {
    Thread.sleep(10000);
    return "Hello World";
}

Hystrix的依赖:

   <!--Hystrix 的依赖 start-->
    <dependency>
        <groupId>com.netflix.hystrix</groupId>
        <artifactId>hystrix-core</artifactId>
        <version>1.5.12</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.2</version>
    </dependency>
    <!--Hystrix 的依赖 end-->

新建一个命令类,实现请见代码

public class HelloCommand extends HystrixCommand<String> {
    
    private String url;

    CloseableHttpClient httpclient;

    public HelloCommand(String url) {
        //调用父类的构造器,设直命令组的 key ,默认用来作为线程池的 key
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        //创建 ttpClient 客户端
        this.httpclient = HttpClients.createDefault();
        this.url = url;
    }

    @Override
    protected String run() throws Exception {
        try {
            //调用 GET 方法请求服务
            HttpGet httpget = new HttpGet(url);
            //得到服务响应
            HttpResponse response = httpclient.execute(httpget);
            //解析并返回命令执行结果
            return EntityUtils.toString(response.getEntity());
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
    
    @Override
    protected String getFallback() {
        System.out.println("执行HelloCommand的回退方法");
        return "error";
    }
}

新建运行类

正常情况下,直接调用 HttpClient API 来请求 Web 服务,而前面的命令类与运行类 则通过命令来执行调用的工作。在命令类 HelloCommand 中, 实现了父类的 run 方法,使 HttpClient 调用服务的过程,都放到了该方法中。运行 HelloMain 类,可以看到,结果与 平常调用 Web 服务无异。

public class HelloMain {

    public static void main1(String[] args) {
        //请求正常的服务
        String normalUrl = "http://localhost:8888/hello";
        HelloCommand command = new HelloCommand(normalUrl);
        String result = command.execute();
        System.out.println("请求i正常的服务,结果: " + result);
    }

    public static void main(String[] args) {
        //请求正常的服务
        String normalUrl = "http://localhost:8888/errorHello";
        HelloCommand command = new HelloCommand(normalUrl);
        String result = command.execute();
        System.out.println("请求超时的服务,结果: " + result);
    }
}

执行超时的服务请求: 执行HelloCommand的回退方法 请求超时的服务,结果: error

根据结果可知,回退方法被执行。本例中调用的 errorHello 服务,会阻塞 10秒才有返 回。默认情况下,如果调用的 Web 服务无法在 秒内完成,那么将会触发回退。 回退更像是一个备胎,当请求的服务无法正常返回时,就调用该“备胎”的实现。这 样做可以很好地保护客户端,服务端所提供的服务受网络等条件的制约,如果有服务真的 需要 10秒才能返回结果,而客户端又没有容错机制,后果就是,客户端将 直等待返回 直到网络超时或者服务有响应,而外界会一直不停地发送请求给客户端,最终导致的结果 就是,客户端因请求过多而瘫痪。

Hystrix 的运作流程

从之前的例子看,当服务器出现无响应现象的时候,Hystrix会自动使用容错机制,看似简单,其实有一套较为复杂的执行逻辑

第一步:在命令开始执行时,会做一些准备工作(如:为命令创建相应的线程池等) 第二步:判断是否打开了缓存,打开了缓存就直接查找缓存并返回结果 第三步:判断断路器是否打开,如果打开了,就表示链路不可以用,直接执行回退方法 结合本章开头的例子,可理解为基础服务模块不可用 ,服务A模块直接执行回 退,响应用户请求。 第四步:判断线程池、信号量(计数器)条件(如:线程池超负荷,则返回回退方法,否则,就去执行命令的内容) 第五步:执行命令,判断是否要对断路器进行处理,执行完成后,如果满足一定条件,则需要开启断路器。如果执行成功,则返回结果,反之则执行回退。 整个流程最主要的点,就是在于断路器是否被打开,后面会讲解断路器的相关内容。我们 的客户端在使用 Hystrix 时,表面上只是创建了一个命令来执行,实际上 Hystrix 己经为客 户端添加了几层保护。

image.png