【Spring Cloud 基础设施搭建系列】Spring Cloud Demo项目 使用Hystrix实现微服务的容错处理

实现容错的手段

如果服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载场景下,如果不作任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃。

雪崩效应

微服务架构的应用系统通常包含多个服务层。微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,微服务之间难免存在依赖关系。我们知道,任何微服务都并非100%可用,网络往往也很脆弱,因此难免有些请求会失败。
我们常把“基础服务故障”导致“级联故障”的现象称为雪崩效应。雪崩效应描述的是提供者不可用导致消费者不可用,并将不可用逐渐放大的过程。

如何容错

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

  • 为网络请求设置超时
    必须为网络请求设置超时。正常情况下,一个远程调用一般在几十毫秒内就能得到响应了。如果依赖的服务不可用或者网络有问题,那么响应时间就会变得很长(几十秒)。
    通常情况下,一次远程调用对应着一个线程/进程。如果响应太慢,这个线程/进程就得不到释放。而线程/进程又对应着系统资源,如果得不到释放的线程/进程越积越多,资源就会逐渐被耗尽,最终导致服务的不可用。
    因此,必须为每个网络请求设置超时,让资源尽快释放。
  • 使用断路器模式
    试想一下,如果家里没有断路器,当电流过载时(例如功率过大、短路等),电路不断开,电路就会升温,甚至可能烧断电路、引发火灾。使用断路器,电路一旦过载就会跳闸,从而可以保护电路的安全。在电路超载的问题被解决后,只须关闭断路器,电路就可以恢复正常。
    同理,如果对某个微服务的请求有大量超时(常常说明该微服务不可用),再去让新的请求访问该服务已经没有任何意义,只会无谓消耗资源。例如,设置了超时时间为1秒,如果短时间内有大量的请求无法在1秒内得到响应,就没有必要再去请求依赖的服务了。
    断路器可理解为对容易导致错误的操作的代理。这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接返回。
    断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不再请求所依赖的服务。这样,应用程序就无须再浪费CPU时间去等待长时间的超时。
    断路器也可自动诊断依赖的服务是否已经恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。使用这种方式,就可以实现微服务的“自我修复”——当依赖的服务不正常时打开断路器时快速失败,从而防止雪崩效应;当发现依赖的服务恢复正常时,又会恢复请求。

在这里插入图片描述

  • 正常情况下,断路器关闭,可正常请求依赖的服务。
  • 当一段时间内,请求失败率达到一定阈值(例如错误率达到50%,或100次/分钟等),断路器就会打开。此时,不会再去请求依赖的服务。
  • 断路器打开一段时间后,会自动进入“半开”状态。此时,断路器可允许一个请求访问依赖的服务。如果该请求能够调用成功,则关闭断路器;否则继续保持打开状态。

综上,我们可通过以上两点机制保护应用,从而防止雪崩效应并提升应用的可用性。

使用Hystrix实现容错

Hystrix是一个实现了超时机制和断路器模式的工具类库。

Hystrix简介

Hystrix是由Netflix 开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

  • 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用到了设计模式中的“命令模式”。
  • 跳闸机制:当某服务的错误率超过一定阀值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
  • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。
  • 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
  • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。
  • 自我修复:断路器打开一段时间后,会自动进入“半开”状态。断路器打开、关闭、半开的逻辑转换。

整合Hystrix

由于Springboot2.1.1 Feign 已经集成了hystrix服务,所以我们这里就不需要添加新的依赖。但是如果你项目中使用的不是Feign而是使用Ribbon+RestTemplate,那么就需要在pom文件中新加入依赖

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

<artifactId>spring-cloud-starter-hystrix</artifactId>已经过期,推荐使用<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>

@FeignClient注解中增加fallback

  • 改造cloud-service-order
package com.cc.cloud.order.feign;

import com.cc.cloud.order.feign.fallback.MemberFeignFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(value = "cloud-service-member", fallback = MemberFeignFallback.class)
public interface MemberFeign {
    @RequestMapping("/member/members")
    List<String> getAllMemberList();
}
package com.cc.cloud.order.feign.fallback;


import com.cc.cloud.order.feign.MemberFeign;
import com.google.common.collect.Lists;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class MemberFeignFallback implements MemberFeign {

    //服务降级处理
    @Override
    public List<String> getAllMemberList() {
        List<String> memberList = Lists.newArrayList();
        memberList.add("not member list");
        return memberList;
    }
}

  • 改造cloud-service-member
package com.cc.cloud.member.feign;

import com.cc.cloud.member.feign.fallback.OrderFeignFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(value = "cloud-service-order",fallback = OrderFeignFallback.class)
public interface OrderFeign {

    @RequestMapping("/order/orders")
    List<String> getAllOrderList();
}

package com.cc.cloud.member.feign.fallback;

import com.cc.cloud.member.feign.OrderFeign;
import com.google.common.collect.Lists;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class OrderFeignFallback implements OrderFeign {

    //服务降级处理
    @Override
    public List<String> getAllOrderList() {
        List<String> listUser = Lists.newArrayList();
        listUser.add("not order list");
        return listUser;
    }
}

Feign默认为开启hystrix服务,各个不同版本可能不一致,所以为了保险起见,在配置文件中开启hystrix服务,其中feign.hystrix.enabled=true代表hystrix服务开启,false关闭hystrix服务。

所以我们给cloud-service-member和cloud-service-order加上配置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka/
server:
  port: 8762
spring:
  application:
    name: cloud-service-member
feign:
  hystrix:
    enabled: true
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8888/eureka/
server:
  port: 8765
spring:
  application:
    name: cloud-service-order
feign:
  hystrix:
    enabled: true

现在我们可以启动我们的服务。

我们现在是可以正常访问两个服务。
在这里插入图片描述
在这里插入图片描述

然后我们关掉order服务之后访问member服务。
在这里插入图片描述
同理,我们关掉member服务,访问order服务。
在这里插入图片描述
至此断路器已经启作用了

参考

SpringCloud与Docker微服务架构实战-完整版.pdf

springcloud(四):熔断器Hystrix

【SpringCloud Greenwich版本】第五章:断路器(hystrix)

五、SpringCloud断路器Hystrix的使用

源代码

https://gitee.com/cckevincyh/spring-cloud-demo/tree/hystrix

发布了647 篇原创文章 · 获赞 816 · 访问量 98万+

猜你喜欢

转载自blog.csdn.net/cckevincyh/article/details/100904569