Spring Cloud Hystrix: Service fault-tolerant protection

SpringBoot actual electricity supplier item mall (20k + star) Address: github.com/macrozheng/...

Summary

Spring Cloud Hystrix Spring Cloud Netflix is ​​one of the core components of subprojects, with service and fault tolerance thread isolation and a series of service protection, this article will detail its usage.

Hystrix Profile

In the micro-service architecture, between service and service calls by way of remote communication, once a service is called a failure has occurred, it depends on the service failure can occur, then spread failure occurs, eventually leading to system paralysis. Hystrix realized breaker mode, when a service fails, by monitoring the circuit breaker, an error is returned to the caller response, rather than waiting for a long time, so it will not make the caller due to prolonged lack of response the thread occupied to prevent the spread of the fault. Hystrix have service degradation, service fuse, thread isolation, request caching, request merging and service monitoring and other powerful features.

Create a hystrix-service module

Here we create a hystrix-service module to demonstrate common features of hystrix.

Add its dependencies in 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>
复制代码

Configured in application.yml

The main port is the call path, registry address and user-service configured.

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
复制代码

Add @EnableCircuitBreaker at the start of the class to open the circuit breaker function Hystrix

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixServiceApplication {

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

UserHystrixController create an interface for invoking user-service service

Service downgraded demo

  • In UserHystrixController added for testing service degradation interface:
@GetMapping("/testFallback/{id}")
public CommonResult testFallback(@PathVariable Long id) {
    return userService.getUser(id);
}
复制代码
  • Add call methods and methods UserService service degradation, it is necessary to add annotations on @HystrixCommand method:
@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);
}
复制代码
  • Start eureka-server, user-service, hystrix-service services;

  • Closed user-service service re-test of the interface and found the service degradation has occurred:

Detailed @HystrixCommand

The common parameters @HystrixCommand

  • fallbackMethod: downgrade method specified service;
  • ignoreExceptions: ignore certain exceptions, service degradation does not occur;
  • commandKey: command name used to distinguish the command;
  • groupKey: group name, Hystrix statistics will be alert and dashboard command information based on different grouping;
  • threadPoolKey: Thread pool name for dividing the thread pool.

Set command group and the name of the thread pool

  • Add in UserHystrixController the test interface:
@GetMapping("/testCommand/{id}")
public CommonResult testCommand(@PathVariable Long id) {
    return userService.getUserCommand(id);
}
复制代码
  • Adding way to achieve functionality in UserService in:
 @HystrixCommand(fallbackMethod = "getDefaultUser",
    commandKey = "getUserCommand",
    groupKey = "getUserGroup",
    threadPoolKey = "getUserThreadPool")
public CommonResult getUserCommand(@PathVariable Long id) {
    return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
 }
复制代码

Use ignoreExceptions ignore certain abnormal downgrade

  • Add in UserHystrixController the test interface:
@GetMapping("/testException/{id}")
public CommonResult testException(@PathVariable Long id) {
    return userService.getUserException(id);
}
复制代码
  • Add implementation in UserService, the neglected here NullPointerException, IndexOutOfBoundsException is thrown when id 1, id throw NullPointerException is 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);
}
复制代码

The request buffer Hystrix

When the system is complicated by increasing the amount of, we need to use caching to optimize the system, to reduce the effect of concurrent requests the number of threads, providing a response speed.

Related Notes

  • @CacheResult: open the cache, all the parameters as default cache key, cacheKeyMethod key can be specified by a method return type String;
  • @CacheKey: Specifies the cache key, can specify parameters specified parameter or attribute value in the cache key, cacheKeyMethod can also specify the type of process by returning String;
  • @CacheRemove: remove the cache, you need to specify commandKey.

Test using the cache

  • Adding a cache in UserHystrixController test interface, the direct method called three times getUserCache:
@GetMapping("/testCache/{id}")
public CommonResult testCache(@PathVariable Long id) {
    userService.getUserCache(id);
    userService.getUserCache(id);
    userService.getUserCache(id);
    return new CommonResult("操作成功", 200);
}
复制代码
  • Add getUserCache method has the cache function in UserService in:
@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);
}
复制代码

Remove the cache test

  • Add remove cached in UserHystrixController test interface, the method is called once removeCache:
@GetMapping("/testRemoveCache/{id}")
public CommonResult testRemoveCache(@PathVariable Long id) {
    userService.getUserCache(id);
    userService.removeCache(id);
    userService.getUserCache(id);
    return new CommonResult("操作成功", 200);
}
复制代码
  • In UserService has added removeCache method to remove the cache function:
@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);
}
复制代码

Problems cache during use

  • In the course of the cache, we need to be initialized and HystrixRequestContext closed before and after each use cached request, otherwise there will be the following exception:
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]
复制代码
  • Here we have to solve this problem by using a filter, initialize before and after each request and closed 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();
        }
    }
}
复制代码

The request to join

Micro-service inter-service communication system, need to be achieved through long-distance calls, as more and more number of calls, more and more will take up the thread resources. Hystrix provided for @HystrixCollapser merge request, so as to reduce the communication cost and the effect of the number of threads.

@HystrixCollapser common attributes

  • batchMethod: combined method for setting request;
  • collapserProperties: the binder attribute request, for example to control properties, there are many;
  • timerDelayInMilliseconds: collapserProperties attributes used to control how much time merging every single request;

Demo

  • Add UserHystrixController in testCollapser method, we first performed here two service calls, service calls a third time later interval 200ms:
@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);
}
复制代码
  • Use @HystrixCollapser fulfill the request merge, all of getUserFuture of multiple calls will be transformed into a single call to getUserByIds of:
@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();
}
复制代码

Hystrix common configuration

Global Configuration

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不能动态改版大小,使用时需要用该参数来控制线程数
复制代码

Examples of configuration

After the default configuration examples only need to replace the corresponding global key to.

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

Description of the configuration file related to key

  • HystrixComandKey of the corresponding property commandKey @HystrixCommand;
  • HystrixCollapserKey collapserKey attribute corresponding to the annotation @HystrixCollapser;
  • HystrixThreadPoolKey @HystrixCommand properties of the corresponding threadPoolKey.

To use the module

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

Project Source Address

github.com/macrozheng/…

No public

mall project a full tutorial serialized in public concern number the first time to obtain.

No public picture

Guess you like

Origin juejin.im/post/5d822d27e51d45621479ad92