Hystrix服务容错处理(一)

在微服务架构中存在多个可直接调用的服务,这些服务若在调用时出现故障会导致连锁效应,也就是可能会让整个系统变得不可用,这种情况我们称之为服务雪崩效应。

如何避免服务雪崩效应?通过Hystrix就能够解决。

1.Hystrix

Hystrix是Netflix针对微服务分布式系统采用的熔断保护中间件, 相当于电路中的保险
丝。在微服务架构下,很多服务都相互依赖,如果不能对依赖的服务进行隔离,那么服务
本身也有可能发生故障, Hystrix通过Hystrix Command对调用进行隔离, 这样可以阻止故
障的连锁反应,能够让接口调用快速失败并迅速恢复正常,或者回退并优雅降级。

1.1 Hystrix的简单实用

创建一个空的Maven项目,增加Hystrix的依赖:

<dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-core</artifactId>
    <version>1.5.18</version>
</dependency>

编写第一个HystrixCommand,如代码所示:

public class MyHystrixCommand extends HystrixCommand<String>{
    private final String name;
    public MyHystrixCommand(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("MyGroup"));
        this.name=name;
    }
    @Override
    protected String run() throws Exception {
        // TODO Auto-generated method stub
        return this.name+":"+Thread.currentThread().getName();
    }

}

首先需要继承HystrixCommand,通过构造函数设置一个Groupkey。具体的逻辑在run方法中,我们返回了一个当前线程名称的值,写一个main方法来调用上面编写的MyHystrixCommand程序,如代码所示:

public static void main(String[] args) throws InterruptedException,ExecutionException{
        String result=new MyHystrixCommand("yinjihuan").execute();
        System.out.println(result);
    }

输出结果是:yinjihuan:hystrix-MyGroup-1.由此可以看出,构造函数中设置的组名变成了线程的名字。

上面是同步调用,如果需要异步调用可以使用代码如下:

public static void main(String[] args) throws InterruptedException,ExecutionException{

        Future<String> future=new MyHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());
    }

1.2 回退支持

下面我们通过增加执行时间模拟调用失败的情况,首先改造MyHystrixCommand,增加getFallback方法返回回退内容,如代码所示:

public class MyHystrixCommand extends HystrixCommand<String>{
    private final String name;
    public MyHystrixCommand(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("MyGroup"));
        this.name=name;
    }
    @Override
    protected String run() throws Exception {
        // TODO Auto-generated method stub
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return this.name+":"+Thread.currentThread().getName();
    }
    protected String getFallback() {
        return "失败了";
    }

    public static void main(String[] args) throws InterruptedException,ExecutionException{
//        String result=new MyHystrixCommand("yinjihuan").execute();
//        System.out.println(result);
        Future<String> future=new MyHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());
    }

}

执行代码,返回内容是”失败了”,证明触发了回退。

1.3 信号量策略配置

信号量策略配置方法如代码所示:

public MyHystrixCommand(String name) {
        super(HystrixCommand.Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(
                                HystrixCommandProperties
                                .ExecutionIsolationStrategy.SEMAPHORE)));
        this.name=name;
    }

之前在run方法中特意输出了线程名称,通过这个名称就可以确定当前是线程隔离还是信号量隔离。

1.4 线程隔离策略配置

系统默认采用线程隔离策略,我们可以通过andThreadPoolPropertiesDefaults配置线程池的一些参数,如代码所示:

public MyHystrixCommand(String name) {
        super(HystrixCommand.Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(
                                HystrixCommandProperties
                                .ExecutionIsolationStrategy.THREAD)
                        ).andThreadPoolPropertiesDefaults(
                                HystrixThreadPoolProperties.Setter()
                                .withCoreSize(10)
                                .withMaxQueueSize(100)
                                .withMaximumSize(100)
                                )
                );
        this.name=name;
    }

1.5 结果缓存

缓存在开发中经常用到,我们常用Redis这种第三方的缓存数据库来对数据进行缓存处理,Hystrix中也为我们提供了方法级别的缓存。通过重写getCacheKey来判断是否返回缓存的数据,getCacheKey可以根据参数来生成,这样同样的参数就可以都用到缓存了。

改写之前的MyHystrixCommand,在其中增加getCacheKey的重写实现,如代码所示:

//返回缓存key
protected String getCacheKey() {
        return String.valueOf(this.name);
    }

上面代码中我们创建对象传进来的name参数作为缓存的key。

为了证明能够用到缓存,在run方法中加一行输出,在调用多次的情况下,如果控制台只输出了一次,那么可以知道后面的都是走的缓存逻辑,如代码所示:

protected String run()  {
        // TODO Auto-generated method stub
        System.err.println("gat data");
        return this.name+":"+Thread.currentThread().getName();
    }

执行main方法,发现报错: Request caching is not available. Maybe you need to initialize the HystrixRequestContext?

根据错误提示可以知道,缓存的处理请求取决于请求的上下文,我们必须初始化Hystrix-RequestContext.

改造main方法中的调用代码,初始化HystrixRequestContext,如代码所示:

public static void main(String[] args) throws InterruptedException,ExecutionException{
        HystrixRequestContext context=HystrixRequestContext.initializeContext();
        String result=new MyHystrixCommand("yinjihuan").execute();
        System.out.println(result);
        Future<String> future=new MyHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());
        context.shutdown();
    }

改造完之后重新执行main方法,可以正常运行了,输出结构如下:

get data
yinjihuan:hystrix-MyGroup-1
yinjihuan:hystrix-MyGroup-1

可以看到只输出一次get data,缓存生效。

1.6 缓存清除

在上节学习了如何使用hystrix来实现数据缓存功能。有缓存必然就有清除缓存的动作,当数据发生变动时,必须将缓存中的数据也更新掉,不然就会产生脏数据的问题。同样,Hystrix也有清除缓存的功能。

增加一个支持清除缓存的类,如代码所示:

public class ClearCacheHystrixCommand extends HystrixCommand<String>{
    private final String name;
    private static final HystrixCommandKey GETTER_KEY=HystrixCommandKey.Factory.asKey("MyKey");
    public ClearCacheHystrixCommand(String name) {
        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.
                Factory.asKey("MyGroup")).andCommandKey(GETTER_KEY));
        this.name=name;
    }

    public static void flushCache(String name) {
        HystrixRequestCache.getInstance(
                GETTER_KEY, HystrixConcurrencyStrategyDefault.getInstance()).clear(name);
    }

    protected String getCacheKey() {
        return String.valueOf(this.name);
    }
    @Override
    protected String run() throws Exception {
        System.err.println("get data");
        return this.name+":"+Thread.currentThread().getName();
    }

    protected String getFallback() {
        return "失败了";
    }

}

flushCache方法就是清除缓存的方法,通过HystrixRequestCache来执行清除操作,根据getCacheKey返回的key来清除。

修改调用代码来验证清除是否有效果,如代码所示:

HystrixRequestContext context=HystrixRequestContext.initializeContext();
        String result=new ClearCacheHystrixCommand("yinjihuan").execute();
        System.out.println(result);
        ClearCacheHystrixCommand.flushCache("yinjihuan");
        Future<String> future=new ClearCacheHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());

执行两次相同的key,在第二次执行之前调用缓存清除的方法,也就是说第二次用不到缓存,输出结果如下:

get data
yinjihuan:hystrix-MyGroup-1
get data
yinjihuan:hystrix-MyGroup-2

1.7 合并请求

Hystrix支持将多个请求自动合并为一个请求,利用这个功能可以节省网络开销,比如每个请求都有通过网络访问远程资源。如果把多个请求合并为一个一起执行,将多次网络交互变成一次,则会极大节省开销。如代码所示:

public class MyHystrixCollapser extends HystrixCollapser<List<String>,String,String>{

    private final String name;
    public MyHystrixCollapser(String name) {
        this.name=name;
    }
    @Override
    public String getRequestArgument() {
        // TODO Auto-generated method stub
        return name;
    }

    @Override
    protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, String>> requests) {
        // TODO Auto-generated method stub
        return new BatchCommand(requests);
    }

    @Override
    protected void mapResponseToRequests(List<String> batchResponse,
            Collection<CollapsedRequest<String, String>> requests) {
        int count=0;
        for(CollapsedRequest<String, String>request:requests) {
            request.setResponse(batchResponse.get(count++));
        }
    }

    private static final class BatchCommand extends HystrixCommand<List<String>>{
        private final Collection<CollapsedRequest<String,String>> requests;
        private BatchCommand(Collection<CollapsedRequest<String, String>>requests) {
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey")));
                this.requests=requests;
        }
        @Override
        protected List<String> run() throws Exception {
            System.out.println("真正执行请求......");
            ArrayList<String> response=new ArrayList<String>();
            for(CollapsedRequest<String, String>request:requests) {
                response.add("返回结果:"+request.getArgument());
            }
            return response;
        }
    }

}

接下来编写测试代码,如代码所示:

HystrixRequestContext context=HystrixRequestContext.initializeContext();
        Future<String> f1=new MyHystrixCollapser("yinjihuan").queue();
        Future<String> f2=new MyHystrixCollapser("yinjihuan333").queue();
        System.out.println(f1.get()+"="+f2.get());
        context.shutdown();

通过

MyHystrixCollapser创建两个执行任务,按照正常的逻辑肯定是分别执行这两个任务,通过HystrixCollapser可以将多个任务合并到一起执行。从输出结果可以看出,任务的执行是在run方法中去做的,输出结果如下:

真正执行请求......
返回结果:yinjihuan=返回结果:yinjihuan333

2.在Spring Cloud中使用Hystrix

2.1 简单使用

在fsh-substitution服务中增加Hystrix的依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>

在启动类上添加@EnableHystrix或者@EnableCircuitBreaker。注意,@EnableHystrix中包含了@EnableCircuitBreaker。

然后改造之前的callHello方法,在上面增加一个@HystrixCommand注解,用于指定依赖服务调用延迟或失败时调用的方法,如代码所示:

@GetMapping("/callHello")
    @HystrixCommand(fallbackMethod="defaultCallHello")
    public String callHello() {

String result=restTemplate.getForObject("http://fsh-house/house/hello", String.class);

        return result;
    }

调用失败触发熔断时会用defaultCallHello方法来回退具体的内容,定义defaultCallHello方法的代码如下所示:

public String defaultCallHello() {
        return "fail";
    }

只启动fsh-substitution服务而不启动fsh-house服务, 调用/callHello接口, 可以看到返回的内容是“fail”。

将启动类上的@EnableHystrix去掉, 重启服务, 再次调用/callHello接口可以看到返
回的是500错误信息,这个时候就没有用到回退功能了。

2.2 配置详解

HystrixCommand中除了fallbackMethod还有很多的配置,下面来看看这些配置:

官方的配置信息文档请参考:https://github.com/Netflix/Hystrix/wiki/Configuration

2.3 Feign整合Hystrix服务容错

首先需要执行2.1节中的集成步骤,然后在属性文件中开启Feign对Hystrix的支持:

feign.hystrix.enabled=true

1.Fallback方式

在Feign的客户端类上的@FeignClient注解中指定fallback进行回退,改造fsh-housed客户端类HouseRemoteClient,为其配置fallback。代码如下:

@FeignClient(value = "fsh-house", path = "/house", configuration = FeignConfiguration.class
        , fallback = HouseRemoteClientHystrix.class)
public interface HouseRemoteClient {

    @GetMapping("/{houseId}")
    HouseInfoDto [email protected]("houseId")Long houseId);
}

HouseRemoteClientHystrix类需要实现HouseRemoteClient类中所有的方法,返回回退时的内容,如代码所示:

@Component
public class HouseRemoteClientHystrix implements HouseRemoteClient{

    public HouseInfoDto houseInfo(Long houseId){
        return new HouseInfoDto();
}

}

启动fsh-substitution服务,停掉所有fsh-house服务,然后访问http://localhost:8082/substitution/1接口,这时候fsh-house服务是不可用的,必然触发回退,返回的内容是正常的格式,只是house对象是空的,这证明回退生效了。在这种情况下,如果你的接口调用了多个服务的接口,那么只有fsh-house服务会没数据,不会影响别的服务,如果不用Hystrix回退处理,整个请求都将失败。

2.FallbackFactory方式

通过fallback已经可以实现服务不可用时回退的功能,如果你想知道触发回退的原因,可以使用FallbackFactory来实现回退功能,如代码所示:

@Component
public class HouseRemoteClientFallbackFactory implements FallbackFactory<HouseRemoteClient> {
    @Override
    public HouseRemoteClient create(final Throwable cause) {
        return new HouseRemoteClient() {

            @Override
            public HouseInfoDto hosueInfo(Long houseId) {
                HouseInfoDto info = new HouseInfoDto();
                info.setData(new HouseInfo(1L, "", "", cause.getMessage()));
                return info;
            }
};
}
}

FallbackFactory的使用就是在@FeignClient中用fallbackFactory指定回退处理类,如代码所示:

@FeignClient(value = "fsh-house", path = "/house", configuration = FeignConfiguration.class
        , fallbackFactory = HouseRemoteClientFallbackFactory.class)

在这个回退处理的时候,将异常信息设置到HouseInfo中的name属性中了。我们重启fsh-substitution,调用接口,可以看到异常信息返回在结果里面了,FallbackFactory和Fallback唯一的区别就在这里。

2.4 Feign中禁用Hystrix

禁用Hystrix还是比较简单的,目前有两种方式可以禁用,其一是在属性文件中进行全部禁用:

feign.hystrix.enabled=false

另一种是通过代码的方式禁用某个客户端,在Feign的配置类中增加代码如下所示:

@Configuration
public class FeignConfiguration{

    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder(){
        return Feign.builder();
    }
}

3.Hystrix监控

在微服务架构中,Hystrix除了实现容错外,还提供了实时监控功能。在服务调用时,Hystrix会实时累积关于HystrixCommand的执行信息,比如每秒的请求数、成功数等。更多的指标信息请查看官方文档地址:

https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring.

Hystrix监控需要两个必备条件:

(1)必须有Actuator的依赖,如代码所示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artigactId>
</dependency>

(2)必须有Hystrix的依赖,Spring Cloud中必须在启动类中添加@EnableHystrix开启Hystrix,如代码所示:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

我们访问fsh-substitution中的地址(http://localhost:8082/hystrix.sream)中可以看到一直在输出“ping:”,这种情况是因为还没有数据,等到HystrixCommand执行了之后就可以看到具体数据了。

4.整合Dashboard查看监控数据

上面讲了通过hystrix.stream端点来获取监控数据,但是这些数据是以字符串的形式展现的,实际使用中不方便查看,我们可以借助Hystrix-dashboard对监控进行图形化展示。

源码参考:https://github.com/yinjihuan/spring-cloud/tree/master/fangjia-hystrix-dashboard

创建一个maven项目,在pom.xml中添加dashboard的依赖,如代码:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        </dependency>

创建启动类,在启动类上添加@EnableHystrixDashboard注解,如代码所示:

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication{
    public static void main(String[] args){
        SpringApplication.run(HystrixDashboardApplication.class,args);
}
}

在属性配置文件中只需要配置服务名称和服务端口:

spring.application.name=fangjia-hystrix-dashboard
server.port=9011

5.Turbine聚合集群数据

5.1 Turbine使用

Turbine是聚合服务器发送事件流数据的一个工具。Hystrix只能监控单个节点, 然后通
过dashboard进行展示。实际生产中都为集群, 这个时候我们可以通过Turbine来监控集群
下Hystrix的metrics情况, 通过Eureka来发现Hystrix服务。

首先增加turbine的依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-turbine</artifactId>
        </dependency>

在启动上增加@EnableTurbine和@EnableDiscoveryClient。在属性文件中配置如下:

eureka.client.serviceUrl.defaultZone=http://yinjihuan:[email protected]:8761/eureka/
#配置需要聚合的服务名称
turbine.appConfig=fsh-substitution,fsh-house
#Turbine需要聚合的集群名称
turbine.aggregator.clusterConfig=default
#集群名表达式
turbine.clusterNameExpression=new String("default")

重启服务,就可以使用http://localhost:9011/turbine.stream来访问集群的监控数据了。

5.2 context-path导致监控失败

如果被监控的服务中设置了context-path,则会导致Turbine无法获取监控数据。这时需要在Turbine中指定turbine.instanceUrlSuffix来解决这个问题:turbine.instanceUrlSuffix=/sub/hystrix.stream

sub用于监控服务的context-path。这种方式是全局配置,如果每个服务的context-path都不一样,这个时候有一些就会出问题,那么就需要对每个服务做一个集群,然后配置集群对应的context-path:

turbine.instanceUrlSuffix.集群名称=/sub/hystrix.stream

更多内容可参见官方文档:https://github.com/Netflix/Turbine/wiki/Configuration-(1.x).

原文地址:https://www.cnblogs.com/xc-xinxue/p/12459861.html

时间: 2024-10-17 10:46:39

Hystrix服务容错处理(一)的相关文章

Spring Cloud Hystrix - 服务容错

服务容错和Hystrix 在微服务架构中,由于某个服务的不可用导致一系列的服务崩溃,被称之为雪崩效应.所以防御服务的雪崩效应是必不可少的,在Spring Cloud中防雪崩的利器就是Hystrix,Spring Cloud Hystri是基于Netflix Hystrix实现的.Hystrix的目标在于通过控制那些访问远程系统.服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力.Hystrix 具备服务降级.服务容错.服务熔断.线程和信号隔离.请求缓存.请求合并以及服务监控等强大功能.

SpringCloud+Hystrix服务容错

Netflix Hystrix - 应对复杂分布式系统中的延时和故障容错 +应用场景 分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而Hystrix的出现,给我们提供了另一种选择 Hystrix的内部处理逻辑构建Hystrix的Command对象, 调用执行方法. Hystrix检查当前服务的熔断器开关是否开启, 若开启, 则执行降级服务getFallback方法. 若熔断器开关关闭, 则Hy

笔记:Spring Cloud Hystrix 服务容错保护

由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪. 在微服务架构中,存在着大量的服务单元,若一个单元出现故障,就很容易因依赖关系而引发故障的蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构更加不稳定,为了解决这样的问题,产生了断路器等一系列的服务保护机制. 在分布式架构中

Hystrix 服务容错

第一步: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency> 第二步: @EnableCircuitBreaker 第三步: package com.example.order.controller; import com.netflix.h

服务容错保护断路器Hystrix之五:配置

接着<服务容错保护断路器Hystrix之二:Hystrix工作流程解析>中的<2.8.关于配置>再列举重要的配置如下 一.hystrix在生产中的建议 1.保持timeout的默认值(1000ms),除非需要修改(其实通常会修改) 2.保持threadpool的的线程数为10个,除非需要更多 3.依赖标准的报警和监控系统来捕获问题 4.通过dashboards的实时监控来动态修改配置,直到满意为止 二.配置信息(default或HystrixCommandKey)最常用的几项 超时

Spring Cloud 八:服务容错保护(Hystrix断路器)【Dalston版】

断路器 断路器模式源于Martin Fowler的Circuit Breaker一文."断路器"本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,"断路器"能够及时的切断故障电路,防止发生过载.发热.甚至起火等严重后果. 在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),直接切断原来的主逻辑调用.但是,在Hystrix中的断路器除了切断主逻辑的功能之外,还有更复

Spring Cloud(四):服务容错保护 Hystrix【Finchley 版】

Spring Cloud(四):服务容错保护 Hystrix[Finchley 版] 发表于 2018-04-15 |  更新于 2018-05-07 | 分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况,这种现象被称为服务雪崩效应.为了应对服务雪崩,一种常见的做法是手动服务降级.而 Hystrix 的出现,给我们提供了另一种选择. Hystrix [h?st'r?ks] 的中文含义是 "豪猪",豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制,这与 Hy

Spring Cloud构建微服务架构 服务容错保护(Hystrix断路器)【Dalston版】

前言 在前两篇<Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)>和<Spring Cloud构建微服务架构:服务容错保护(Hystrix依赖隔离)>中,我们对Hystrix提供的服务降级和依赖隔离有了基本的认识.下面我们将继续说说Hystrix的另外一个重要元件:断路器. 断路器 断路器模式源于Martin Fowler的Circuit Breaker一文."断路器"本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生

SpringCould-------使用Hystrix 实现断路器进行服务容错保护

消费: <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"> <modelV