服务雪崩  

  微服务架构中,系统通常包括多个服务层。微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,服务间的依赖就难免会存在。而每个微服务都不能保证自己时时刻刻百分百可用。当一个微服务挂掉之后,其他微服务调用这个挂掉微服务的都不可用了。把这种基础服务故障导致级联故障的现象称为雪崩效应。

  简单点来说就是服务A 调用服务B ,而服务B又去调用服务C,服务D(这样的调用过程就是服务扇出)。而在某条扇出的服务调用链路中有一个服务,由于响应时间过程或者抛出异常,导致服务调用者被占用越来越多资源,从而导致整个系统奔溃。

解决方案

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

为网络请求设置超时:为每个网络请求设置超时时间,超时不再等待,让资源尽快释放。

使用断路器模式:断路器就相当于家里的自动跳闸的。当请求错误失败数较多,或者有大量超时的请求,就会自动停止对该微服务的请求。

 

Hystrix简介

  Hystrix是一个容错管理工具,设计目的是将应用中的远程系统访问、服务调用、第三方依赖包的调用入口,通过资源控制的方式隔离开,避免了在分布式系统中失败的级联塌方式传递,提升系统的弹性和健壮性。

Hystrix 容错机制

包裹请求: 使用HystrixCommand注解包裹对依赖的调用逻辑,每个命令都在独立的线程中执行(命令模式)。
跳闸机制: 当某服务的错误率超过了一定的阈值,Hystrix可以自动或者手动跳闸,停止对该服务的访问。
资源隔离: Hystrix为每个历来都维护了一个小型的线程池。如果该线程池已满,发往该依赖的请求就会被拒绝,而不是排队等候。从而加速失败的判定。
监控:   Hystrix可以实时的监控运行指标和配置的变化。
回退机制: 当请求失败、超时、被拒绝时,或者当断路器被打开时执行回退的逻辑,回退的逻辑可以自己提供。
自我修复: 断路器打开一段时间后,会自动进入半开状态。

Hystrix入门

<!--1. 配置pom文件,引入spring-cloud-starter-netflix-hystrix包-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

<!- 2. 在服务消费方继承HystrixCommand,实现Hystrix的服务降级->
public class OrderHystrixCommand extends HystrixCommand<List<OrderVo>> {

    private String userId;

    private RestTemplate restTemplate;

    public OrderHystrixCommand(String commandGroupKey, RestTemplate restTemplate, String userId) {
        super(HystrixCommandGroupKey.Factory.asKey(commandGroupKey));
        this.restTemplate = restTemplate;
        this.userId = userId;
    }

    @Override
    protected List<OrderVo> run() throws Exception {
        ResponseEntity<List> responseEntity  =  restTemplate.getForEntity("http://MS-PROVIDER-ORDER/order/queryOrderByUserId/"+userId, List.class);
        List<OrderVo> orderVoList = responseEntity.getBody();
        return orderVoList;
    }

    @Override
    protected List<OrderVo> getFallback() {
        OrderVo orderVo = new OrderVo();
        orderVo.setOrderId(-1);
        orderVo.setUserId(-1);
        orderVo.setOrderMoney(new BigDecimal(0));

        List<OrderVo> orderVos = new ArrayList<>();
        orderVos.add(orderVo);
        return orderVos;
    }
}


<!- 3. 在服务消费方调用 ->
OrderHystrixCommand ohc =  new OrderHystrixCommand("orderGroupKey", restTemplate, userId);
List<OrderVo> orderVoList = ohc.execute();

 备注:我们可以做几个简单的测试,来对Hystrix有的服务降级有一个初步的了解:

  宕机跳闸:关闭一个MS-PROVIDER-ORDER服务提供者,尝试看会出现什么结果,能不能调用我们的getFallback方法;

  超时跳闸:在服务提供方的被调用方法中设置线程睡眠时间,同时我们通过ribbon参数将服务消费者的超时,让超时时间小于睡眠时间,尝试看会出现什么结果。

  异常跳闸:查询一个不存在的订单模拟抛出异常,尝试看会出现什么结果。

超时跳闸时一些常用配置

#设置全局的超时时间
ribbon.ReadTimeout=1000
ribbon.ConnectTimeout=1000

#出现异常的时候 ,当前实例进行重试
ribbon.MaxAutoRetries=1
#切换实例重试的次数
ribbon.MaxAutoRetriesNextServer=1

#对有所的的操作进行重试(all是对有所方法进行重试,true对有所的重试,false对get重试)
#如果为True需要保证接口的幂等性
ribbon.OkToRetryOnAllOperations=true

#开启重试机制(默认是开启的)
spring.cloud.loadbalancer.retry.enabled=true

#局部配置
微服务实例名称.ribbon.ConnectTimeout=1000
微服务实例名称.ribbon.ReadTimeout=1000
微服务实例名称.ribbon.OkToRetryOnAllOperations=true
微服务实例名称.ribbon.MaxAutoRetriesNextServer=1
微服务实例名称.ribbon.MaxAutoRetries=1

通过 @HystrixCommand实现服务降级

fallbackMethod方法需要和主方法有一样的入参,不然会报错。一般来说只对调用频率高的接口配置服务降级。
@HystrixCommand 虽然比继承HystrixCommand要方便,但在实际中用的并不多,因为配置的过多会带来难以维护的问题。

@HystrixCommand(fallbackMethod="queryOrderFallBack")
@RequestMapping("testFallback/{userId}")
public EmployeeInfo testHystrixFallback(@PathVariable("userId") String userId){
    Employee employee = employeeService.findEmployeeById(userId);

    ResponseEntity<List> responseEntity;
    responseEntity = restTemplate.getForEntity("http://MS-PROVIDER-ORDER/order/queryOrderbyUserId/" + userId, List.class);

    List<OrderVo> orderVoList = responseEntity.getBody();

    EmployeeInfo employeeInfo = new EmployeeInfo();
    employeeInfo.setEmployeeName(employee.getName());
    employeeInfo.setOrderVoList(orderVoList);
    return employeeInfo;
}

protected EmployeeInfo queryOrderFallBack(String userId) {
    EmployeeInfo employeeInfo = new EmployeeInfo();
    employeeInfo.setEmployeeName("FallBack");
    employeeInfo.setOrderVoList(null);
    return employeeInfo;
}

Hystrix整合Feign

<!--1. 配置pom文件,引入jar包 -->
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

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

<!-- 2. 配置属性文件 -->
#开启feign对hystrix的支持,默认是关闭的。
feign.hystrix.enabled=true


<!-- 3. 在Spring的启动入口添加@EnableFeignClients注解 -->
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class HystrixProvider8006Application {

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

<!-- 4. 定义Feign的接口类,通过FeignClient中的fallback属性实现Hystrix的服务降级 -->
@FeignClient(name = "ms-provider-order", fallback = CustomFeignOrderApiFallBack.class, path = "/order")
public interface CustomFeignOrderApi {
    @RequestMapping("/queryOrdersByUserId/{userId}")
    List<OrderVo> queryOrdersByUserId(@PathVariable("userId") String userId);

    @RequestMapping("/getRegisterInfo")
    String getRegisterInfo();
}

@Component
public class CustomFeignOrderApiFallBack implements MsCustomFeignOrderApi {
    @Override
    public List<OrderVo> queryOrdersByUserId(String userId) {
        List<OrderVo> orderVoList = new ArrayList<>();
        OrderVo orderVo = new OrderVo();
        orderVo.setUserId(userId);
        orderVo.setOrderId(-1);

        orderVoList.add(orderVo);
        return orderVoList;
    }

    @Override
    public String getRegisterInfo() {
        return "服务异常";
    }
}

备注:我们可以同上实验一下

 

 Hystrix的一些常用配置

1. 关闭Hystrix对feign的支持
全局关闭:feign.hystrix.enabled=false(默认是关闭的)
局部关闭:
写一个配置类,在配置类中写一个FeignBulider的类:
public class CustomeFeignApiWithoutHystrixConfg {
    @Scope("prototype")
    @Bean
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
}

在FeignClient中指配置文件
@FeignClient(name = "MS-PROVIDER-ORDER",configuration = MsCustomeFeignApiWithoutHystrixConfg.class, path = "/order")

2. 关闭熔断
全局关闭:hystrix.command.default.circuitBreaker.enabled=false
局部关闭:hystrix.command.<HystrixCommandKey>.circuitBreaker.enabled: false
        其中的HystrixCommandKey是个变量,可以打开服务的hystrix.stream端点即可看到,也可在Hystrix Dashboard中查看。

3. 设置超时
全局超时:hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 1000
局部关闭:hystrix.command.<HystrixCommandKey>.execution.isolation.thread.timeoutInMilliseconds: 1000

4. 关闭超时
全局关闭:hystrix.command.default.execution.timeout.enabled: false
局部关闭:hystrix.command.<HystrixCommandKey>.execution.timeout.enabled: false

 

posted on 2020-03-02 10:52  愚蠢的猴子  阅读(447)  评论(0编辑  收藏  举报