1 Star 0 Fork 1

wow / java-notes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
使用SpringCloudCircuitBreaker实现服务容错.md 9.87 KB
一键复制 编辑 原始数据 按行查看 历史
wow 提交于 2021-03-26 15:26 . 分布式事务

使用Hystrix进行服务容错

我们这里使用 Spring Cloud Circuit Breaker 框架 支持的 Hystrix进行服务容错。

引入Hystrix依赖

  1. 引入Hystrix依赖

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

    在引入其他所需依赖如 eureka、web、test等

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- 略...... -->
  2. 在启动类上使用 @SpringCloudApplication 注解

    @SpringCloudApplication 注解定义如下

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootApplication//spring boot 启动
    @EnableDiscoveryClient//启动服务发现
    @EnableCircuitBreaker//启动断路器
    public @interface SpringCloudApplication {
    }

使用 Hystrix 实现服务隔离

在编写代码前首先我们来看一下 Hystrix 的核心类

HystrixCommand 类是一个抽象类值包含了一个抽象方法

protected abstract R run() throws Exception;

该方法是留给开发人员实现服务容错所需处理的业务逻辑的。

还有另外一个核心方法 getFallback(),作为服务回退函数的实现

protected R getFallback() {
 throw new UnsupportedOperationException("No fallback available.");
}

Hystrix 组件在支持以下两种隔离方式

  • 线程池隔离

  • 信号量隔离

我们使用线程池隔离来编码

编码方式


public class GetUserCommand extends HystrixCommand<User> {

    //远程调用 user-service 的客户端工具类
    @Autowired
    private UserServiceClient userServiceClient;

    public GetUserCommand() {
        super(Setter.withGroupKey(
                //设置命令组
                HystrixCommandGroupKey.Factory.asKey("springHealthGroup"))
                //设置命令键
                .andCommandKey(HystrixCommandKey.Factory.asKey("interventionKey"))
                //设置线程池键
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("aaa"))
                //设置命令属性
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withCircuitBreakerRequestVolumeThreshold(10)//至少有10个请求,熔断器才进行错误率的计算
                                .withCircuitBreakerSleepWindowInMilliseconds(5000)//熔断器中断请求5秒后会进入半打开状态,放部分流量过去重试
                                .withCircuitBreakerErrorThresholdPercentage(50)//错误率达到50开启熔断保护
                                .withExecutionTimeoutEnabled(true)//开启执行超时
                                .withExecutionTimeoutInMilliseconds(5000))//超时时间
                //设置线程池属性
                .andThreadPoolPropertiesDefaults(
                        HystrixThreadPoolProperties.Setter()
                                .withMaxQueueSize(10)//最大队列
                                .withCoreSize(2))
        );

        System.out.println("GetUserCommand 启动");
    }

    //执行的业务方法
    @Override
    protected User run() {
        User user = userServiceClient.getUser();
        return user;
    }

    //服务回退方法
    @Override
    protected User getFallback() {
        return new User(1L, "user1", "fail");
    }
}

使用方式

User user = new GetUserCommand().execute();

注解方式

上面的代码我们使用起来过于繁琐,Hystrix 为我们提供了一个 @HystrixCommand 注解,我们来看一下它的定义

public @interface HystrixCommand {

    //命令组
    String groupKey() default "";
    //命令键
    String commandKey() default "";
    //设置线程池键
    String threadPoolKey() default "";
    //回退方法
    String fallbackMethod() default "";
    //设置命令属性
    HystrixProperty[] commandProperties() default {};
    //设置线程池属性
    HystrixProperty[] threadPoolProperties() default {};

    Class<? extends Throwable>[] ignoreExceptions() default {};

    ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

    HystrixException[] raiseHystrixExceptions() default {};

    String defaultFallback() default "";
}

我们对上面的代码重构

@HystrixCommand
public User getUser() {
    ......
}

也可以对部分参数进行精确的控制

@HystrixCommand(threadPoolKey = "springHealthGroup",
    threadPoolProperties =
     {
         @HystrixProperty(name="coreSize",value="2"),//设置核心线程
         @HystrixProperty(name="maxQueueSize",value="10")//设置最大队列
     }
)

使用 Hystrix 实现服务熔断

我们知道熔断器有三个状态,其中打开半打开状态会导致触发熔断机制。

我们在生产者项目 user-service 在接口中添加 Threadl.sleep(5000) 即可模拟请求超时的结果

我们可以看到控制台有如下错误

{
    "timestamp":"1601881721343",
    "status":500,
    "error":"Internal Server Error",
    "exception":"com.netflix.hystrix.exception.HystrixRuntimeException",
    "message":"generate Intervention time-out and fallback failed.",
    "path":"/interventions/springhealth_user1/device_blood"
 }

在这里,我们发现 HTTP 响应状态为 500,而抛出的异常为 HystrixRuntimeException,从异常信息上可以看出引起该异常的原因是超时。事实上,默认情况下,添加了 @HystrixCommand 注解的方法调用超过了 1000 毫秒就会触发超时异常,显然上例中设置的 2000 毫秒满足触发条件。

和设置线程池属性一样,在 HystrixCommand 中我们也可以对熔断的超时时间、失败率等各项阈值进行设置。例如我们可以在 getDevice() 方法上添加如下配置项以改变 Hystrix 的默认行为:

@HystrixCommand(
    commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
private DeviceMapper getDevice(String deviceCode)

所有项还可以如下配置

@HystrixCommand(
        groupKey = "UserServiceClient-1",
        commandKey = "getUser-1",
        threadPoolKey = "user-1",
        threadPoolProperties =
                {
                        @HystrixProperty(name = "coreSize", value = "2"),
                        @HystrixProperty(name = "maxQueueSize", value = "10")
                },
        commandProperties = {
                //熔断超时时间
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "12000"),
                //一个滑动窗口内最小的请求数
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
                //错误比率阈值
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "75"),
                //触发熔断的时间值
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "7000"),
                //一个滑动窗口的时间长度
                @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "15000"),
                //一个滑动窗口被划分的数量
                @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "5")},
        fallbackMethod = "getUserFallback"
)

使用 Hystrix 实现服务回退

Hystrix 在服务调用失败时都可以执行服务回退逻辑。在开发过程上,我们只需要提供一个 Fallback 方法实现并进行配置即可。例如,在 SpringHealth 案例系统中,对于 intervention-service 中访问 user-service 和 device-service 这两个远程调用场景,我们都可以实现 Fallback 方法。回退方法的实现也非常方便,唯一需要注意的就是 Fallback 方法的参数和返回值必须与真实的方法完全一致。如下所示的就是 Fallback 方法的一个示例:

//示例
private UserMapper getUserFallback(String userName) {
    UserMapper fallbackUser = new UserMapper(0L,"no_user","not_existed_user");
    return fallbackUser;
}

} 我们通过构建一个不存在的 User 信息来返回 Fallback 结果。有了这个 Fallback 方法,剩下来要做的就是在 @HystrixCommand 注解中设置“fallbackMethod”配置项。重构后的 getUser 方法如下所示:

@HystrixCommand(threadPoolKey = "springHealthGroup",
    threadPoolProperties ={
        @HystrixProperty(name="coreSize",value="2"),
        @HystrixProperty(name="maxQueueSize",value="10")
    },
    fallbackMethod = "getUserFallback"
)
private UserMapper getUser(String userName) {
    return userClient.getUserByUserName(userName);
}

与zuul网关集成

feign:
  hystrix:
    # 开启熔断支持
    enabled: true
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            # 熔断器超时时间
            timeoutInMilliseconds: 600000
ribbon:
  # 处理请求的超时时间,默认为1000ms = 1s ,这里需要参考熔断器超时时间
  ReadTimeout: 20000
  # 建立连接的时长默认1000ms
  ConnectTimeout: 10000
  # 同一台实例的最大重试次数,但是不包括首次调用,默认为1次
  MaxAutoRetries: 1
  # 重试负载均衡其他实例的最大重试次数,不包括首次调用,默认为0次
  MaxAutoRetriesNextServer: 0
  # 是否对所有操作都重试,默认false
  OkToRetryOnAllOperations: false
其他
1
https://gitee.com/superwow/java-notes.git
git@gitee.com:superwow/java-notes.git
superwow
java-notes
java-notes
master

搜索帮助