你还不会用hystrix实现服务熔断降级吗?

一:需求分析

如果一个服务不可用,例如网络延迟或者流量激增,则会影响依赖于这个服务的其他服务,导致雪崩效应。为了解决这种雪崩效应,使用熔断器Hystrix, 实现服务降级,服务限流的功能,并且为client提供健康的页面状态。

二:解决方案

使用熔断器或者服务降级,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。

fallback和fallbackFactory区别

需要得到导致回退触发的原因需要使用fallbackFactory

配置中心开启Hystrix功能

在application.yml 或者Apollo中开启Hystrix功能

ribbon:
  OkToRetryOnAllOperations: false #对所有操作请求都进行重试,默认false
  ReadTimeout: 5000   #负载均衡超时时间,默认值5000
  ConnectTimeout: 2000 #ribbon请求连接的超时时间,默认值2000
  MaxAutoRetries: 0     #对当前实例的重试次数,默认0
  MaxAutoRetriesNextServer: 1 #对切换实例的重试次数,默认1


hystrix:
  command:
    default: #default全局有效,service id指定应用有效
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制,true,则超时作为熔断根据
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 1000 #断路器超时时间,默认1000ms

feign:
  hystrix:
    enabled: true
  • 如果hystrix.command.default.execution.timeout.enabled为true,则会有两个执行方法超时的配置,一个就是ribbon的ReadTimeout,一个就是熔断器hystrix的timeoutInMilliseconds, 此时谁的值小谁生效
  • 如果hystrix.command.default.execution.timeout.enabled为false,则熔断器不进行超时熔断,而是根据ribbon的ReadTimeout抛出的异常而熔断,也就是取决于ribbon
  • ribbon的ConnectTimeout,配置的是请求服务的超时时间,除非服务找不到,或者网络原因,这个时间才会生效
  • ribbon还有MaxAutoRetries对当前实例的重试次数,MaxAutoRetriesNextServer对切换实例的重试次数, 如果ribbon的ReadTimeout超时,或者ConnectTimeout连接超时,会进行重试操作
  • 由于ribbon的重试机制,通常熔断的超时时间需要配置的比ReadTimeout长,ReadTimeout比ConnectTimeout长,否则还未重试,就熔断了
  • 为了确保重试机制的正常运作,理论上(以实际情况为准)建议hystrix的超时时间为:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout
  • (以上太长可不看,如果按ribbon默认的推荐设置看,算出来是10s,但不使用ribbon重试机制的话则为5s,但hystrix默认为1s,因此建议测试环境5s,生产环境机器性能各方面相对较高可设置为3s左右)

添加fallback属性

在接口中的@FeignClient注解上添加fallback属性来配置指定的处理类。

在热点服务上添加指定fallback类,返回指定内容:

在RPC调用的类上添加指定fallback类,在服务熔断的时候返回fallback类中的内容。

@FeignClient(value = "CIM-BASE-ZUUL", url = "${feign.url.cim-base-zuul}", fallbackFactory = CimBaseZuulServiceFallback.class)
public interface CimBaseZuulService {
    /**
     * 获取所有设备
     */
    @GetMapping("/cim/api-service/find/deviceTypes")
    CimBaseZuulResponse<List<DeviceBaseInfoBO>> getAllDeviceInfo();

}

创建回调类

热点服务的回掉类继承FallbackFactory:

创建UgmdpServiceFallback类继承于FallbackFactory实现回调的方法

@Slf4j
@Component
public class CimBaseZuulServiceFallback implements FallbackFactory<CimBaseZuulService> {

    private static final String SYSTEM_CIM = "cim";
    private static final String GET_ALL_DEVICE_INFO = "/cim/api-service/find/deviceTypes";

    @Override
    public CimBaseZuulService create(Throwable e) {
        return new CimBaseZuulService() {
            @Override
            public CimBaseZuulResponse<List<DeviceBaseInfoBO>> getAllDeviceInfo() {
                if (e instanceof HystrixTimeoutException) {
                    throw new TimeoutException(ResponseCode.FEIGN_TIMEOUT, SYSTEM_CIM, GET_ALL_DEVICE_INFO);
                } else {
                    throw new TimeoutException(ResponseCode.DEGRADATION);
                }
            }
        };
    }
}

自定义异常

@Data
@NoArgsConstructor
public class TimeoutException extends RuntimeException{
    private String code;
    private String message;

    public TimeoutException(String code, String message) {
        super(message);
        this.code = code;
        this.message = message;
    }

    public TimeoutException(ResponseCode exception) {
        super(exception.getMsg());
        this.code = exception.getCode();
        this.message = exception.getMsg();
    }

    public TimeoutException(ResponseCode exception,String... str) {
        super(exception.getMsg());
        this.code = exception.getCode();
        this.message = String.format(exception.getMsg(),str);
    }
}

全局异常捕获

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler({HystrixRuntimeException.class})
    @ResponseBody
    public RdfaResult<Serializable> timeoutException(HystrixRuntimeException exception) {
        Exception cause = (Exception) exception.getFallbackException().getCause().getCause();
        log.warn("服务调用超时", cause);
        if (cause instanceof TimeoutException){
            TimeoutException e = (TimeoutException)cause;
            return RdfaResult.fail(e.getCode(), e.getMessage());
        }
        return RdfaResult.fail(ResponseCode.FAILURE.getCode(), exception.getMessage());
    }
}

三:测试熔断

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值