SpringCloud Hystrix 容错保护

 

容错

 在一个分布式系统里,一个服务往往要调用多个服务,可能存在某个服务调用失败, 比如超时、异常等,

 要使用容错框架保证在某些服务调用出问题时,不会拖垮整个调用链路,系统依然可用。

 

 

Hystrix

Hystrix是一个容错框架,提供了隔离、熔断、服务降级、监控、cache等功能,可以有效防止被调服务故障造成的级联故障。

和eureka、ribbon、feign一样,hystrix也是Netflix旗下的项目,都被SpringCloud集成了。

  

 

服务限流

当此消费者对某提供者请求个数较多时,消费者可以限制自身对提供者发起的请求个数,请求个数超过指定的值时,使请求快速失败。

Hystrix的限流方式有2种:线程池、信号量。

 

 

服务监控

监控请求的失败率(一定时间内,请求失败个数)达到阈值,就打开断路器,熔断链路,使后续的请求快速失败。

 

 

熔断

熔断服务,包含被调用者、链路的下游服务,防止下游服务的故障影响到上游服务。

 

 

服务降级

服务调用失败默认会返回一堆英文的错误信息给用户,很不友好。

服务降级是在服务调用失败时自动执行预案,预案使用相同参数表、返回值类型,是备胎、次选,

预案可以只保留核心业务、抛弃不重要的业务,比如电商网站qps很大时,用户查询商品信息这一操作,标准业务是获取商品信息+评论+相似商品(商品推荐),预案只保留查询商品信息这一核心业务,去掉获取评论、推荐商品等非核心业务。

预案也可以连核心业务都不要,直接给出“太挤了,请稍后重试”之类的友好提示。

 

 

熔断和降级的异同点

相同点:从可用性和可靠性触发,防止系统崩溃,某些功能暂时不可用

不同点:服务熔断一般是下游服务故障导致的,而服务降级一般是从服务负载考虑,由调用方控制

 

 

自我修复

自我修复其实就是断路器状态的切换,断路器打开(熔断)后5min内的请求都快速失败;5min后断路器半开,放行部分请求,

如果这些请求(服务调用)都成功了,说明问题已修复,关闭断路器,链路恢复通行;否则继续下一个5min。

5min是窗口期,数值可以调。

 

 

 


 

 

 

使用Hystrix

Hystrix是在消费者(调用方)中使用的,一般要和Feign搭配使用

 

(1)在创建消费者时勾选Spring Cloud Circuit Breaker -> Hystrix [Maintenance]

也可以手动加hystrix的依赖:

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

 

 

(2)引导类上加 @EnableCircuitBreaker或@EnableHystrix

 @EnableHystrix中包含了@EnableCircuitBreaker这个注解

 

 

(3)在controller中设置回退方法

@Controller
@RequestMapping("/api/v1/user")
public class UserController {
    @Resource
    // private RestTemplate restTemplate;
    private OrderFeignService orderFeignService;

    @RequestMapping("order/{user_id}")
    @ResponseBody
    @HystrixCommand(fallbackMethod = "findOrdersByUserIdFail")  //指定回退方法
    public Map<String,Object> findOrdersByUserId(@PathVariable("user_id") Integer userId){
        //调用服务
        // List<Order> orders = restTemplate.getForObject("http://order-service/api/v1/order/list/" + userId, List.class);
        List<Order> orders = orderFeignService.findOrdersByUserId(userId);

        HashMap<String, Object> map = new HashMap<>();
        map.put("code", 0);
        map.put("data", orders);

        return map;
    }


    /**
     * 回退方法,回退方法命名一般是原方法名+Fail
     */
    public Map<String,Object> findOrdersByUserIdFail(Integer userId){
        //参数:远程服务接口,返回值类型(目标类型)
        HashMap<String, Object> map = new HashMap<>();
        map.put("code", -1);
        map.put("msg", "人太多了,你被挤出来了,请稍后重试");
        return map;
    }
    
}

Hystrix一般是在controller中设置的,也可以在其它地方进行设置。但服务调用要写在service层、然后在controller中调用service层,我这里图方便直接在controller中调用服务。

使用Feign、RestTemplate调用服务都可以。

 

返回的时候一般用map装载数据,code表示处理情况,前端或上游服务根据码值确定处理的情况,进行对应的数据展示。

比如code=xx表示处理成功了,如果是数据查询,用表格还是什么把获取的数据展示出来;如果code=xx表示处理失败,用提示框告诉用户失败了。

 当然回退方法可以直接返回null,如果返回null,前端、上游服务处理返回结果时需要先判断返回的数据是否为null。

 

 

@HystrixCommand(fallbackMethod = "findOrdersByUserIdFail")标注在某个方法上,只对该方法有效;

@DefaultProperties(defaultFallback = "findOrdersByUserIdFail")标注在类上,对类中所有的方法都有效,该类中的某个方法执行失败时,都会调用默认的回退方法来代替。

 

 

 


 

 

 

使用Feign自带的Hystrix

上面的方式使用的是单独的Hystrix,也可以使用Feign内置的Hystrix。

 

同样是在消费中使用的,创建消费者时不需要勾选Hystrix,不需要添加Hystrix的依赖,因为Feign的依赖中已经包含了Hystrix。

 

在引导类中不需要使用Hystrix的注解

@EnableFeignClients
// @EnableHystrix
// 或者@EnableCircuitBreaker

 

 

(1)在配置文件中开启feign自带的hystrix

feign:
  hystrix:
    enabled: true

默认是false,需要手动开启

 

 

(2)写一个类实现Feign接口,和Feign接口放在同一个包下

@Component  //放到spring容器中
public class OrderFeignServiceFallback implements OrderFeignService {

    //实现的方法就是接口中对应方法的回退方法
    @Override
    public List<Order> findOrdersByUserId(Integer user_id) {
        return null;
    }

}

 

 

(3)在Feign接口中指定对应的回退类

@FeignClient(name = "order-service", fallback = OrderFeignServiceFallback.class) 
public interface OrderFeignService {

    @GetMapping("/api/v1/order/list") 
    List<Order> findOrdersByUserId(@RequestHeader("user_id") Integer user_id);

}

 

使用时不需要使用 @HystrixCommand 指定回退方法,调用失败时会自动执行回退类中对应的回退方法。

 

 

 


 

 

 

2种方式的比较

  • 作用范围

第一种在哪里使用都行,可以给controller、service、dao或者其他地方的方法指定回退方法,哪个方法都可以加回退方法;

第二种只是给服务调用加回退方法,只作用于服务调用的语句。

 

  • 服务调用方式

第一种可以使用RestTemplate或者Feign,第二种只能使用Feign。

 

总之第二种的使用范围很有限,只能对Feign方式的服务调用起到容错保护;

第一种的使用范围十分广泛,不局限于服务调用,可对整个应用起到容错保护,功能更加强大。

 

 

 


 

 

 

报警通知

1、通知用户

有些服务需要一段时候后才会处理,比如订单处理,会先放到消息队列中,逐个处理,

如果处理失败(服务调用失败)需要及时通知用户,比xx分钟内到账、xx分钟内出票,如果服务调用失败,在回退方法中要通知用户。

如果是车票、电影票、充话费之类较为重要的,要在回退方法中记录日志,并调用短信接口及时告知用户出票失败、充值失败,退款预计xx小时内到账。

 

通知时,如果当前线程还要做一些操作,比如返回数据给上游服务,通知是一个单独的业务,可以新建一个线程来通知,不要阻塞当前线程。

 

 

2、通知系统管理员、运维

统计服务调用失败的次数,如果指定时间内服务调用失败次数达到指定值,或者指定时间内服务调用失败的比例达到指定值,就以短信或者email的方式通知运维、管理员。

 

可以写一个工具类,用一个静态变量记录服务调用总数,调用某服务一次就+1,再使用一个静态变量统计该服务调用失败的次数;

写一个springboot定时任务,比如每5min执行一次,检测调用总次数、服务调用失败比例,达到指定值就通知管理员、运维,

检测调用总次数是为了确认被调者是否可能出问题了,不能调用总数1、失败率100%就通知管理员,可能是用户自己的问题。

 

通知之后或者比例未达到指定值,将统计值清空,重新统计下一个5min。

需要做好日志记录,方便管理员找出问题。

 

比如说被调用的服务集群挂了,不能每隔5min就通知管理一次,人家都开始处理了,你还一直通知。

可以在管理面板中提供按钮,点击可以关闭、开启通知管理员的功能,管理员收到故障通知后在面板中关闭通知管理员的功能,开始排查问题,解决后再开启通知管理员的功能。

也可以通知管理员之前先检测redis上有没有某个键值对,有就说明通知过了,不再通知;没有就设置此键值对,并指定过期时间,再通知管理员。比如过期时间设置为2h,2h通知一次。

 

 

 


 

 

 

Hystrix参数设置

Hystrix的参数配置可以写在@HystrixCommand(标注在方法上,只对该方法有效)、@DefaultProperties(标注在类上,对类中所有的方法都有效)注解中,

但配置自然是写在配置文件中好些,官方也是写在配置文件中的。

  

 Hystrix的配置是写在消费者中的,常用配置如下:

hystrix:
  command:
    default:
      execution:
        #是否启用超时,默认true,一般都是设置为true
        #timeout:
          #enabled: true
        isolation:
          #有2种隔离策略:THREAD 线程池(默认)、SEMAPHORE 信号量
          strategy: THREAD
          thread:
            #超时时间,默认1000,ms
            timeoutInMilliseconds: 4000
          #semaphore:
            #设置信号量最大值,默认10
            #maxConcurrentRequests: 100

上面使用的是hystrix,参数前缀是hystrix ;如果使用feign自带的hystrix,参数前缀是 feign: hystrix  

 

 

超时时间指的是@HystrixCommand标注的方法执行的时间,如果使用的是Feign自带的Hystrix,指定的是服务调用的时间。

超时自动返回失败,如果将超时时间关闭,则一直等待服务调用完成,不会超时。一般都开启超时时间。

 

 

隔离策略即服务限流,消费者一般都会对提供者发起多个服务调用请求,消费者可以设置服务限流,限制本身对提供者的并发请求数。有2种策略

(1)THREAD  线程池,也叫做线程隔离,默认的隔离策略

 为每个被调用的服务都创建一个线程池,将每个服务调用请求都包装为一个线程,放到线程池中。

线程池中的服务调用请求都是正在执行的,若线程池满了,放在队列中排队等待,若队列也满了,则后续对该被调者的请求直接快速失败。

Hystrix使用的是jdk自带的线程池,不是tomcat的线程池。并发执行的服务调用数取决于线程池能容纳的线程数。

 

 

(2)SEMAPHORE  信号量

信号量即可同时处理的请求个数,默认值10。

调用一次该服务,将信号量-1,一个调用请求结束将信号量+1,信号量为0时不再接受对该服务的调用请求,直接快速失败。

信号量适用于高并发服务调用,比如每秒数千次服务调用,因为使用线程池会导致线程池中同时存在几千个线程,开销巨大。

不用将服务调用请求包装为单独的线程,速度更快,但信号量一般只用于非网络调用。

 

 快速失败是指不调用服务提供者,直接服务降级,执行回退方法。

 

 

如果要设置Hystrix的更多参数:

github上搜hystrix找到官方 -> WiKi -> 点击右侧的目录中的Configuration即可查看hystrix全部参数的介绍

直达车    https://github.com/Netflix/Hystrix/wiki/Configuration

 

 

 

  

posted @ 2020-02-16 19:49  chy_18883701161  阅读(501)  评论(1编辑  收藏  举报