This is the 9th day of my participation in Gwen Challenge. This article is participating in “Java Theme Month – Java Development in Action”, see the activity link for details.

We’ve seen Spring Cloud’s various ways of limiting flow to handle the pressure of frequent requests. As we all know, when invoking multiple microservices, it is assumed that microservice A calls microservice B and microservice C, and microservice B and microservice C call other microservices, which is called fan-out. If the request of A microservice on the fan-out link is too long or unavailable, Calls to microservice A take up more and more time and resources, causing A system avalanche, or “avalanche effect.”

At this point, there needs to be a mechanism to ensure that when a microservice fails (a request is slow or down), the whole process continues in a friendly manner. Instead of waiting for a long time or throwing an exception that the caller cannot handle, the caller’s thread can be guaranteed not to be held up for a long time, thus avoiding the spread and avalanche of failures in the distributed system. We call this mechanism, or this process, a fuse.

The circuit breaker mechanism is a micro-service link protection mechanism to cope with avalanche effect. When a micro-service on the whole link is abnormal, the service is degraded, and the invocation of the micro-service on the node is fused to quickly return “reasonable” response information. When the node microservice is detected to be normal, the call link is restored, which is realized by Hystrix in the Spring Cloud framework mechanism. Hystrix will monitor the call status of microservice. When the failed call reaches a threshold, the default is 20 failed calls within 5 seconds, the circuit breaker mechanism will be activated. The comment on the circuit breaker mechanism is @hystrixCommand.

I recently took a look at Spring Cloud’s circuit-breakers, sharing some code, and some pitfalls in the field.

In Spring Cloud, imagine several microservices: user management services, order services, authentication centers, logistics services, and so on. In order service, an interface requests the user to manage the service. In this case, if the circuit breaker mechanism is required, what should be done?

First, the order service introduces dependencies:


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

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

At this point, the order service startup class needs to reference the @enablecircuitbreaker annotation to make it work:

/** * @author Damon * @date January 13, 2020 3:23:06 ** / @enableoAuth2Sso@configuration @enableAutoConfiguration @ComponentScan(basePackages = {"com.damon"}) @EnableDiscoveryClient @EnableCircuitBreaker public class OrderApp { public  static void main(String[] args) { SpringApplication.run(OrderApp.class, args); }}Copy the code

Here, don’t forget to annotate @enableDiscoveryClient to expose services to each other.

Finally, we need to annotate @hystrixCommand in the function that calls the user management service:

@HystrixCommand(fallbackMethod = "admin_service_fallBack", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", Value = "5000")}) / / isolation strategy: execution. The isolation. The strategy = SEMAPHORE or THREAD (not configured by default) @ Override public Response < Object > getUserInfo(HttpServletRequest req, HttpServletResponse res) { ResponseEntity<String> forEntity = restTemplate.getForEntity(envConfig.getAdmin_web_url() + "/api/user/getUserInfo", String.class); HttpHeaders headers = new HttpHeaders(); MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8"); headers.setContentType(type); headers.add("Accept", MediaType.APPLICATION_JSON.toString()); headers.add("Authorization", "bearer " + StrUtil.subAfter(req.getHeader("Authorization"), "bearer ", false)); HttpEntity<String> formEntity = new HttpEntity<String>(null, headers); String body = ""; try { ResponseEntity<String> responseEntity = restTemplate.exchange("http://admin-web-service/api/user/getUserInfo", HttpMethod.GET, formEntity, String.class); if (responseEntity.getStatusCodeValue() == 200) { logger.debug(String.format("request getUserInfo return: {}", JSON.toJSON(responseEntity.getBody()))); return Response.ok(responseEntity.getStatusCodeValue(), 0, "success", JSON.toJSON(responseEntity.getBody())); } } catch (Exception e) { logger.error("loadJobDetail error"); logger.error(e.getMessage(), e); } return null; } private Response<Object> admin_service_fallBack(HttpServletRequest req, HttpServletResponse res) { String token = StrUtil.subAfter(req.getHeader("Authorization"), "bearer ", false); logger.info("admin_service_fallBack token: {}", token); Return response. ok(200, -2, "User service is down!" , null); }Copy the code

FallbackMethod = fallbackMethod (); fallbackMethod (); fallbackMethod (); fallbackMethod ();

com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException:fallback method wasn't found.
Copy the code

Finally, configure Hystrix related parameters to configure YAML:

hystrix.command.BackendCall.execution.isolation.thread.timeoutInMilliseconds: 5000
hystrix.threadpool.BackendCallThread.coreSize: 5
Copy the code

The first configuration can also be configured in the calling function:

@HystrixCommand(fallbackMethod = "admin_service_fallBack", commandProperties = {
      @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") })
Copy the code

After the 3000 ms parameter takes effect, it will be overwritten if it is also configured in the configuration file.

Without the @hystrixCommand commandProperties= @hystrixProperty annotation, the following FallBack function admin_service_fallBack() is a thread; HystrixCommand() is a quarantined thread. Merge two threads into one thread with a commandProperties= @hystrixProperty annotation configuration.

At this point, the caller is finished with the configuration. Here http://admin-web-service is the service being invoked, so the @enableDiscoveryClient annotation is required in its service startup class:

@EnableOAuth2Sso @Configuration @EnableAutoConfiguration @ComponentScan(basePackages = {"com.damon"}) @EnableConfigurationProperties(EnvConfig.class) @EnableDiscoveryClient public class AdminApp { public static void main(String[] args) { SpringApplication.run(AdminApp.class, args); }}Copy the code

In addition, to configure the RestTemplate Bean with the @loadBalanced annotation, you need to perform LB to find one of the corresponding services according to the LB rules.

@Configuration public class BeansConfig { @Resource private Environment env; @ LoadBalanced / / will not be able to use IP and other forms to request other service @ Bean public RestTemplate RestTemplate () {SimpleClientHttpRequestFactory requestFactory  = new SimpleClientHttpRequestFactory(); requestFactory.setReadTimeout(env.getProperty("client.http.request.readTimeout", Integer.class, 15000)); requestFactory.setConnectTimeout(env.getProperty("client.http.request.connectTimeout", Integer.class, 3000)); RestTemplate rt = new RestTemplate(requestFactory); return rt; }}Copy the code

Finally, if there is no problem, you can suspend the user management service, and then run the order service, return the fusing result:

{"message":{"code":-2,"message":" User service is suspended!" ,"status":200}}Copy the code