Spring Cloud Hystrix: отказоустойчивая защита сервисов

Java
Spring Cloud Hystrix: отказоустойчивая защита сервисов

Адрес фактического проекта электронной коммерции SpringBoot (20k+star):GitHub.com/macro-positive/…

Резюме

Spring Cloud Hystrix является одним из основных компонентов подпроекта Spring Cloud Netflix.Он имеет ряд функций защиты службы, таких как отказоустойчивость службы и изоляция потоков.В этой статье будет подробно рассказано о его использовании.

Введение в Хайстрикс

В микросервисной архитектуре сервисы взаимодействуют друг с другом через удаленные вызовы.В случае сбоя вызываемого сервиса, его зависимые сервисы также будут давать сбой.В это время произойдет распространение сбоя, что в конечном итоге приведет к параличу системы. Hystrix реализует режим автоматического выключателя: при сбое службы он возвращает вызывающей стороне ответ об ошибке через мониторинг автоматического выключателя вместо длительного ожидания, так что вызывающая сторона не получит ответа в течение длительного времени. занимают поток, тем самым предотвращая распространение сбоя. Hystrix обладает мощными функциями, такими как снижение качества обслуживания, предохранитель обслуживания, изоляция потоков, кэширование запросов, слияние запросов и мониторинг обслуживания.

Создать модуль hystrix-service

Здесь мы создаем модуль hystrix-service, чтобы продемонстрировать общие функции hystrix.

Добавьте связанные зависимости в pom.xml

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Настроить в application.yml

В основном настройте порт, адрес реестра и путь вызова пользовательской службы.

server:
  port: 8401
spring:
  application:
    name: hystrix-service
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8001/eureka/
service-url:
  user-service: http://user-service

Добавьте @EnableCircuitBreaker в класс запуска, чтобы включить функцию автоматического выключателя Hystrix.

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixServiceApplication {

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

Создайте интерфейс UserHystrixController для вызова службы обслуживания пользователей.

Демонстрация деградации службы

  • Добавьте интерфейс для тестирования даунгрейда сервиса в UserHystrixController:
@GetMapping("/testFallback/{id}")
public CommonResult testFallback(@PathVariable Long id) {
    return userService.getUser(id);
}
  • Добавьте вызывающий метод и метод перехода на более раннюю версию службы в UserService, и к методу необходимо добавить аннотацию @HystrixCommand:
@HystrixCommand(fallbackMethod = "getDefaultUser")
public CommonResult getUser(Long id) {
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}

public CommonResult getDefaultUser(@PathVariable Long id) {
    User defaultUser = new User(-1L, "defaultUser", "123456");
    return new CommonResult<>(defaultUser);
}
  • Запустите службы eureka-server, user-service, hystrix-service;

  • Завершите работу службы пользовательской службы и повторно протестируйте интерфейс, обнаружив, что произошла деградация службы:

Подробное объяснение @HystrixCommand

Общие параметры в @HystrixCommand

  • fallbackMethod: указывает метод обработки перехода на более раннюю версию службы;
  • ignoreExceptions: игнорировать некоторые исключения, понижение службы не происходит;
  • commandKey: имя команды, используемое для различения разных команд;
  • groupKey: имя группы, Hystrix будет подсчитывать аварийные сигналы команд и информацию на приборной панели в соответствии с различными группами;
  • threadPoolKey: имя пула потоков, используемое для разделения пула потоков.

Задайте имя команды, группы и пула потоков

  • Добавьте тестовый интерфейс в UserHystrixController:
@GetMapping("/testCommand/{id}")
public CommonResult testCommand(@PathVariable Long id) {
    return userService.getUserCommand(id);
}
  • Добавьте способ реализации функции в UserService:
 @HystrixCommand(fallbackMethod = "getDefaultUser",
    commandKey = "getUserCommand",
    groupKey = "getUserGroup",
    threadPoolKey = "getUserThreadPool")
public CommonResult getUserCommand(@PathVariable Long id) {
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
 }

Используйте ignoreExceptions, чтобы игнорировать определенные понижения исключения

  • Добавьте тестовый интерфейс в UserHystrixController:
@GetMapping("/testException/{id}")
public CommonResult testException(@PathVariable Long id) {
    return userService.getUserException(id);
}
  • Добавьте в UserService метод реализации, который игнорирует исключение NullPointerException, создает исключение IndexOutOfBoundsException, если идентификатор равен 1, и создает исключение NullPointerException, если идентификатор равен 2:
@HystrixCommand(fallbackMethod = "getDefaultUser2", ignoreExceptions = {NullPointerException.class})
public CommonResult getUserException(Long id) {
    if (id == 1) {
        throw new IndexOutOfBoundsException();
    } else if (id == 2) {
        throw new NullPointerException();
    }
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}

public CommonResult getDefaultUser2(@PathVariable Long id, Throwable e) {
    LOGGER.error("getDefaultUser2 id:{},throwable class:{}", id, e.getClass());
    User defaultUser = new User(-2L, "defaultUser2", "123456");
    return new CommonResult<>(defaultUser);
}

Кэш запросов Hystrix

Когда объем параллелизма системы увеличивается, нам необходимо использовать кеш для оптимизации системы, чтобы уменьшить количество одновременных потоков запросов и обеспечить эффект скорости ответа.

Связанные комментарии

  • @CacheResult: включить кеш, все параметры используются в качестве ключа кеша по умолчанию, а cacheKeyMethod может указать ключ, возвращая метод типа String;
  • @CacheKey: укажите ключ кеша, вы можете указать параметр или значение атрибута в указанном параметре — это ключ кеша, а cacheKeyMethod также можно указать, возвращая метод типа String;
  • @CacheRemove: чтобы удалить кеш, нужно указать commandKey.

Тест с использованием кеша

  • Добавьте тестовый интерфейс для использования кеша в UserHystrixController и трижды напрямую вызовите метод getUserCache:
@GetMapping("/testCache/{id}")
public CommonResult testCache(@PathVariable Long id) {
    userService.getUserCache(id);
    userService.getUserCache(id);
    userService.getUserCache(id);
    return new CommonResult("操作成功", 200);
}
  • Добавьте метод getUserCache с функцией кэширования в UserService:
@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(fallbackMethod = "getDefaultUser", commandKey = "getUserCache")
    public CommonResult getUserCache(Long id) {
    LOGGER.info("getUserCache id:{}", id);
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}

/**
 * 为缓存生成key的方法
 */
public String getCacheKey(Long id) {
    return String.valueOf(id);
}
  • тест интерфейса вызоваhttp://localhost:8401/user/testCache/1, метод getUserCache вызывается в этом интерфейсе трижды, но печатается только один лог, указывающий на то, что кеш используется дважды:

Тест удаления кеша

  • Добавьте тестовый интерфейс для удаления кеша в UserHystrixController и вызовите метод removeCache один раз:
@GetMapping("/testRemoveCache/{id}")
public CommonResult testRemoveCache(@PathVariable Long id) {
    userService.getUserCache(id);
    userService.removeCache(id);
    userService.getUserCache(id);
    return new CommonResult("操作成功", 200);
}
  • Добавьте метод removeCache с функцией удаления кеша в UserService:
@CacheRemove(commandKey = "getUserCache", cacheKeyMethod = "getCacheKey")
@HystrixCommand
public CommonResult removeCache(Long id) {
    LOGGER.info("removeCache id:{}", id);
    return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id);
}

Проблемы с использованием кеша

  • В процессе использования кеша нам нужно инициализировать и закрывать HystrixRequestContext до и после каждого кэшированного запроса, иначе возникнет следующее исключение:
java.lang.IllegalStateException: Request caching is not available. Maybe you need to initialize the HystrixRequestContext?
	at com.netflix.hystrix.HystrixRequestCache.get(HystrixRequestCache.java:104) ~[hystrix-core-1.5.18.jar:1.5.18]
	at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:478) ~[hystrix-core-1.5.18.jar:1.5.18]
	at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:454) ~[hystrix-core-1.5.18.jar:1.5.18]
  • Здесь мы решаем проблему, используя фильтр для инициализации и закрытия HystrixRequestContext до и после каждого запроса:
/**
 * Created by macro on 2019/9/4.
 */
@Component
@WebFilter(urlPatterns = "/*",asyncSupported = true)
public class HystrixRequestContextFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            context.close();
        }
    }
}

запрос на вытягивание

Межсервисное взаимодействие в микросервисной системе необходимо реализовывать через удаленные вызовы, поскольку с увеличением количества вызовов занято все больше и больше ресурсов потоков. Hystrix предоставляет @HystrixCollaper для запросов на слияние, тем самым уменьшая потребление связи и количество потоков.

Общие свойства @HystrixCollaper

  • batchMethod: метод, используемый для установки слияния запросов;
  • коллапспропертиес: свойства слияния запроса, используемые для управления свойствами экземпляра, их много;
  • timerDelayInMilliseconds: свойство вcolprProperties для управления частотой объединения запросов;

Демо

  • Добавьте метод testCollaper в UserHystrixController.Здесь мы сначала делаем два вызова службы, а затем делаем третий вызов службы с интервалом в 200 мс:
@GetMapping("/testCollapser")
public CommonResult testCollapser() throws ExecutionException, InterruptedException {
    Future<User> future1 = userService.getUserFuture(1L);
    Future<User> future2 = userService.getUserFuture(2L);
    future1.get();
    future2.get();
    ThreadUtil.safeSleep(200);
    Future<User> future3 = userService.getUserFuture(3L);
    future3.get();
    return new CommonResult("操作成功", 200);
}
  • Используя @HystrixCollapser для слияния запросов, все множественные вызовы getUserFuture преобразуются в один вызов getUserByIds:
@HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {
    @HystrixProperty(name = "timerDelayInMilliseconds", value = "100")
})
public Future<User> getUserFuture(Long id) {
    return new AsyncResult<User>(){
    @Override
    public User invoke() {
        CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
        Map data = (Map) commonResult.getData();
        User user = BeanUtil.mapToBean(data,User.class,true);
        LOGGER.info("getUserById username:{}", user.getUsername());
        return user;
        }
    };
}

@HystrixCommand
public List<User> getUserByIds(List<Long> ids) {
    LOGGER.info("getUserByIds:{}", ids);
    CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/getUserByIds?ids={1}", CommonResult.class, CollUtil.join(ids,","));
    return (List<User>) commonResult.getData();
}
  • тест интерфейса доступаhttp://localhost:8401/user/testCollapser, так как мы установили 100 миллисекунд для слияния запроса, первые два слились, а последний раз слился сам по себе.

Общая конфигурация Hystrix

Глобальная конфигурация

hystrix:
  command: #用于控制HystrixCommand的行为
    default:
      execution:
        isolation:
          strategy: THREAD #控制HystrixCommand的隔离策略,THREAD->线程池隔离策略(默认),SEMAPHORE->信号量隔离策略
          thread:
            timeoutInMilliseconds: 1000 #配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理
            interruptOnTimeout: true #配置HystrixCommand执行超时的时候是否要中断
            interruptOnCancel: true #配置HystrixCommand执行被取消的时候是否要中断
          timeout:
            enabled: true #配置HystrixCommand的执行是否启用超时时间
          semaphore:
            maxConcurrentRequests: 10 #当使用信号量隔离策略时,用来控制并发量的大小,超过该并发量的请求会被拒绝
      fallback:
        enabled: true #用于控制是否启用服务降级
      circuitBreaker: #用于控制HystrixCircuitBreaker的行为
        enabled: true #用于控制断路器是否跟踪健康状况以及熔断请求
        requestVolumeThreshold: 20 #超过该请求数的请求会被拒绝
        forceOpen: false #强制打开断路器,拒绝所有请求
        forceClosed: false #强制关闭断路器,接收所有请求
      requestCache:
        enabled: true #用于控制是否开启请求缓存
  collapser: #用于控制HystrixCollapser的执行行为
    default:
      maxRequestsInBatch: 100 #控制一次合并请求合并的最大请求数
      timerDelayinMilliseconds: 10 #控制多少毫秒内的请求会被合并成一个
      requestCache:
        enabled: true #控制合并请求是否开启缓存
  threadpool: #用于控制HystrixCommand执行所在线程池的行为
    default:
      coreSize: 10 #线程池的核心线程数
      maximumSize: 10 #线程池的最大线程数,超过该线程数的请求会被拒绝
      maxQueueSize: -1 #用于设置线程池的最大队列大小,-1采用SynchronousQueue,其他正数采用LinkedBlockingQueue
      queueSizeRejectionThreshold: 5 #用于设置线程池队列的拒绝阀值,由于LinkedBlockingQueue不能动态改版大小,使用时需要用该参数来控制线程数

конфигурация экземпляра

Конфигурация экземпляра должна только заменить значение по умолчанию в глобальной конфигурации соответствующим ключом.

hystrix:
  command:
    HystrixComandKey: #将default换成HystrixComrnandKey
      execution:
        isolation:
          strategy: THREAD
  collapser:
    HystrixCollapserKey: #将default换成HystrixCollapserKey
      maxRequestsInBatch: 100
  threadpool:
    HystrixThreadPoolKey: #将default换成HystrixThreadPoolKey
      coreSize: 10

Описание соответствующего ключа в файле конфигурации

  • HystrixComandKey соответствует свойству commandKey в @HystrixCommand;
  • HystrixCollaperKey соответствует свойству collaprKey в аннотации @HystrixCollapser;
  • HystrixThreadPoolKey соответствует свойству threadPoolKey в @HystrixCommand.

используемые модули

springcloud-learning
├── eureka-server -- eureka注册中心
├── user-service -- 提供User对象CRUD接口的服务
└── hystrix-service -- hystrix服务调用测试服务

Адрес исходного кода проекта

GitHub.com/macro-positive/…

публика

проект торгового центраПолный набор учебных пособий сериализуется,Обратите внимание на публичный аккаунтПолучите это прямо сейчас.

公众号图片