使用Hystrix实现微服务的容错处理(九幽金祖火)

1.实现容错的手段

如果服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供这响应超时。在高负载的场景下,如果不错处理容易导致雪崩效应

1.1:雪崩效应

我们常把"基础服务故障"导致"级联故障"的现象称为雪崩效应。雪崩小樱的描述的 是提供者不可用导致消费者不可用,并将不可用逐渐放大的过程。

1.2:如何容错

想要防止雪崩效应,必须有一个强大的容错机制。该机制需要实现以下两点。

*为网络请求设置超时

*使用断路器模式

1.3:断路器转换的逻辑图

正常情况下,断路器关闭,可正常请求依赖的服务

当一段时间内,请求失败率达到一定阈值,断路器就会打开,此时,不会再请求依赖的服务

断路器打开一段时间后,会自动进入半开状态。此时,断路器可允许一个请求访问依赖的服务,如果请求能够调用成功,则关闭断路器,否则就继续保持打开状态

2.使用Hystrix实现容错

2.1:通用方式整合Hystrix

2.1.1:复制microservice-consumer-movie-ribbon,将ArtifactId修改为microservice-consumer-movie-ribbon-hystrix

2.1.2:添加依赖

<?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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.itmuch.cloud</groupId>
    <artifactId>microservice-consumer-movie-ribbon-hystrix</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>microservice-consumer-movie-ribbon-hystrix</name>
    <description>Demo project for Spring Boot</description>


    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

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

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <!--引入spring cloud依赖-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

</project>

在启动类上添加注解@EnableHystrix,启动断路器支持

package com.itmuch.cloud.microserviceconsumermovieribbon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class MicroserviceConsumerMovieRibbonHystrixApplication {

    /**
     * 实例化一个Bean并使用该方法的名字命名
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

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

}

修改MovieController,让其中的findById方法具备容错能力

package com.itmuch.cloud.microserviceconsumermovieribbon.controller;

import com.itmuch.cloud.microserviceconsumermovieribbon.entity.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

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

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    private static final Logger logger = LoggerFactory.getLogger(MovieController.class);

    /**
     * microservice-provider-user:虚拟主机名
     * @param id
     * @return
     */
    @HystrixCommand(fallbackMethod = "findByIdFallback",
                    commandProperties = {
                        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000"),
                        @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "10000")
                    },
                    threadPoolProperties = {
                        @HystrixProperty(name = "coreSize",value = "1"),
                        @HystrixProperty(name = "maxQueueSize",value = "10")
                    })
    @GetMapping("/user/{id}")
    public User findById(@PathVariable Long id){
        return this.restTemplate.getForObject("http://microservice-provider-user/" + id,User.class);
    }

    public User findByIdFallback(Long id){
        User user = new User();
        user.setId(-1L);
        user.setName("默认用户");
        return user;
    }

    @GetMapping("/log-instance")
    public void logUserInstance(){
        ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
        //打印当前选择的那个节点
        MovieController.logger.info("{}:{}:{}",serviceInstance.getServiceId(),serviceInstance.getHost(),serviceInstance.getPort());
    }
}

里面添加了回退方法

启动测试

正常状况下

我们关闭服务的提供者

hystrix:使用hystrix入门

附录:各种策略配置

  • HystrixCommandProperties
/* --------------统计相关------------------*/ 
// 统计滚动的时间窗口,默认:5000毫秒(取自circuitBreakerSleepWindowInMilliseconds)   
private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds;   
// 统计窗口的Buckets的数量,默认:10个,每秒一个Buckets统计   
private final HystrixProperty metricsRollingStatisticalWindowBuckets; // number of buckets in the statisticalWindow   
// 是否开启监控统计功能,默认:true   
private final HystrixProperty metricsRollingPercentileEnabled;   
/* --------------熔断器相关------------------*/ 
// 熔断器在整个统计时间内是否开启的阀值,默认20。也就是在metricsRollingStatisticalWindowInMilliseconds(默认10s)内至少请求20次,熔断器才发挥起作用   
private final HystrixProperty circuitBreakerRequestVolumeThreshold;   
// 熔断时间窗口,默认:5秒.熔断器中断请求5秒后会进入半打开状态,放下一个请求进来重试,如果该请求成功就关闭熔断器,否则继续等待一个熔断时间窗口
private final HystrixProperty circuitBreakerSleepWindowInMilliseconds;   
//是否启用熔断器,默认true. 启动   
private final HystrixProperty circuitBreakerEnabled;   
//默认:50%。当出错率超过50%后熔断器启动
private final HystrixProperty circuitBreakerErrorThresholdPercentage;  
//是否强制开启熔断器阻断所有请求,默认:false,不开启。置为true时,所有请求都将被拒绝,直接到fallback 
private final HystrixProperty circuitBreakerForceOpen;   
//是否允许熔断器忽略错误,默认false, 不开启   
private final HystrixProperty circuitBreakerForceClosed; 
/* --------------信号量相关------------------*/ 
//使用信号量隔离时,命令调用最大的并发数,默认:10   
private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests;   
//使用信号量隔离时,命令fallback(降级)调用最大的并发数,默认:10   
private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests; 
/* --------------其他------------------*/ 
//使用命令调用隔离方式,默认:采用线程隔离,ExecutionIsolationStrategy.THREAD   
private final HystrixProperty executionIsolationStrategy;   
//使用线程隔离时,调用超时时间,默认:1秒   
private final HystrixProperty executionIsolationThreadTimeoutInMilliseconds;   
//线程池的key,用于决定命令在哪个线程池执行   
private final HystrixProperty executionIsolationThreadPoolKeyOverride;   
//是否开启fallback降级策略 默认:true   
private final HystrixProperty fallbackEnabled;   
// 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作.默认:true   
private final HystrixProperty executionIsolationThreadInterruptOnTimeout; 
// 是否开启请求日志,默认:true   
private final HystrixProperty requestLogEnabled;   
//是否开启请求缓存,默认:true   
private final HystrixProperty requestCacheEnabled; // Whether request caching is enabled. 
  • HystrixCollapserProperties
//请求合并是允许的最大请求数,默认: Integer.MAX_VALUE   
private final HystrixProperty maxRequestsInBatch;   
//批处理过程中每个命令延迟的时间,默认:10毫秒   
private final HystrixProperty timerDelayInMilliseconds;   
//批处理过程中是否开启请求缓存,默认:开启   
private final HystrixProperty requestCacheEnabled; 
  • HystrixThreadPoolProperties
/* 配置线程池大小,默认值10个 */ 
private final HystrixProperty corePoolSize; 
/* 配置线程值等待队列长度,默认值:-1 建议值:-1表示不等待直接拒绝,测试表明线程池使用直接决绝策略+ 合适大小的非回缩线程池效率最高.所以不建议修改此值。 当使用非回缩线程池时,queueSizeRejectionThreshold,keepAliveTimeMinutes 参数无效 */
private final HystrixProperty maxQueueSize; 

2.2:Hystrix线程隔离策略与传播上下文

Hystrix的隔离策略有两种:分别是线程隔离和信号量隔离

THREAD(线程隔离):使用方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中的线程数量的限制

SEMAPHORE(信号量隔离):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受到信号量个数的限制

Hystrix中默认使用并推荐使用线程隔离,因为这种方式有一个除网络超时以外的额外保护层

2.3:Feign使用Hystrix

Spring Cloud默认已为Feign整合了Hystrix,只要Hystrix在项目中的classpath中,Feign默认就会使用断路器包裹所有方法

2.3.1:为Feign添加回退

复制项目microservice-consumer-movie-feign,将ArtifactId修改为microservice-consumer-movie-feign-hystrix-fallback

编写Feign接口

package com.itmuch.cloud.microserviceconsumermovie.client;

import com.itmuch.cloud.microserviceconsumermovie.entity.FeignClientFallback;
import com.itmuch.cloud.microserviceconsumermovie.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @FeignClient注解中的microservice-provider-user是一个任意的客户端名称,用于创建Ribbon负载均衡器
 * 还可以使用url属性指定请求的URL(URL可以是完整的URL或者主机名)例如
 * @FeignClient(name = "microservice-provider-user",url="http://localhost:8000/")
 * fallback指定回退类
 */
@FeignClient(name = "microservice-provider-user",fallback = FeignClientFallback.class)
public interface UserFeignClient {
    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public User findById(@PathVariable("id")Long id);
}

回退类

package com.itmuch.cloud.microserviceconsumermovie.entity;

import com.itmuch.cloud.microserviceconsumermovie.client.UserFeignClient;
import org.springframework.stereotype.Component;

@Component
public class FeignClientFallback implements UserFeignClient {
    @Override
    public User findById(Long id) {
        User user = new User();
        user.setId(-1L);
        user.setUsername("默认用户");
        return user;
    }
}

启动测试

正常


关闭提供者

2.3.3:通过Fallback Factory检查回退原因

1.复制项目microservice-consumer-movie-feign,将ArtifactId修改为microservice-consumer-movie-feign-hystrix-fallback-factory

2.UserFeignClient改为如下内容

package com.itmuch.cloud.microserviceconsumermovie.client;

import com.itmuch.cloud.microserviceconsumermovie.entity.FeignClientFallbackFactory;
import com.itmuch.cloud.microserviceconsumermovie.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @FeignClient注解中的microservice-provider-user是一个任意的客户端名称,用于创建Ribbon负载均衡器
 * 还可以使用url属性指定请求的URL(URL可以是完整的URL或者主机名)例如
 * @FeignClient(name = "microservice-provider-user",url="http://localhost:8000/")
 */
@FeignClient(name = "microservice-provider-user",fallbackFactory = FeignClientFallbackFactory.class)
public interface UserFeignClient {
    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public User findById(@PathVariable("id")Long id);
}
package com.itmuch.cloud.microserviceconsumermovie.entity;

import com.itmuch.cloud.microserviceconsumermovie.client.UserFeignClient;
import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class FeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
    private static final Logger logger = LoggerFactory.getLogger(FeignClientFallbackFactory.class);

    @Override
    public UserFeignClient create(Throwable cause) {
        return new UserFeignClient() {
            @Override
            public User findById(Long id) {
                //日志最好放在各个fallback方法中。而不要直接放在create方法中
                //否则在引起启东时,就会打印该日志
                FeignClientFallbackFactory.logger.info("fallback;reason was:",cause);
                User user = new User();
                user.setId(-1L);
                user.setUsername("默认用户");
                return user;
            }
        };
    }
}

启动测试:

不知道为什么,没测出想要的结果

2019-10-28 15:18:38.658  INFO 19832 --- [ HystrixTimer-3] c.i.c.m.e.FeignClientFallbackFactory     : fallback;reason was:

2.3.4:为Feign禁用Hystrix

在Spring cloud中,只要Hystrix在项目的classpath中,Feign就会使用断路器包裹Feign客户端的所有方法,这样虽然方便,但很多场场景下并不需要,所以如何让Feign禁用Hystrix

为指定Feign客户端禁用Hystrix

借助Feign的自定义配置,可轻松为指定名称的Feign客户端禁用Hystrix

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

想要禁用Hystrix的@FeignClient引用该配置类即可

@FeignClient(name = "user",configuration = FeignDisableHystrixConfiguration.class)
public interface UserFeignClient {

}

全局禁用Hystrix

也可以为Feign全局禁用Hystrix.只需在application.yml中配置feign.hystrix.enabled = false即可

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值