Avalanche effect
Many applications in complex distributed architecture have dependency calling relationships, and each dependency will inevitably fail sometimes (exception, timeout, network failure, etc.)
This kind of multiple service layer calls, and the failure of basic services may lead to cascading failures, resulting in the unavailability of the whole system. This phenomenon is called service avalanche effect.
Service avalanche effect is a process in which the unavailability of "service consumers" is caused by the unavailability of "service providers", and the unavailability is gradually enlarged.
As shown in the figure below, A is the service provider, B is the service consumer of A, and C and D are the service consumers of B. The unavailability of A causes the unavailability of B, and when the unavailability is enlarged to C and d like A snowball, the avalanche effect is formed.
Service fuse
For the avalanche effect, Netflix provides a solution: Hystrix. A delay and fault-tolerant open source library used to isolate access to remote services and third-party libraries to prevent cascading failures
Fusing principle
For example, the circuit fuse at home can fuse the circuit immediately if the circuit is short circuited to avoid disaster. After applying this mode in the distributed system, the service caller can judge by himself. When some services respond slowly or there are a large number of timeouts, it can fuse automatically to prevent the whole system from being dragged down
Unlike circuit fusing, Hystrix can realize elastic fault tolerance, and can automatically reconnect when the situation improves. Subsequent requests can be rejected directly through circuit breaking, and some requests are allowed to pass (half open) after a period of time. If the call is successful, it will return to the circuit closed state, otherwise it will continue to be disconnected (fully open)
Use of fuses
Import dependency
Because service fusing occurs at the service caller, it is necessary to add Hystrix dependency in the service consumer application
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
Launch class to enable the Hystrix configuration
The @ EnableHystrix annotation needs to be added on the startup class of the service consumer application to turn on the fuse configuration
@SpringBootApplication @EnableEurekaClient // Open Eureka client configuration @EnableDiscoveryClient // Turn on service discovery // @The RibbonClient loads the custom Ribbon configuration class when starting the microservice // This custom configuration class cannot be placed under the current package and sub package scanned by @ ComponentScan, otherwise the custom configuration class will be shared by all Ribbon clients and cannot be customized // The following configuration explanation: use the custom Ribbon policy MyRule for microservicecloud provider microservices @RibbonClient(name = "microservicecloud-provider", configuration = {MyRuleConfig.class}) @EnableFeignClients // Enable the configuration of Feign declarative call @EnableHystrix // Turn on the Hystrix fuse configuration public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
The lower version of spring cloud uses the @ enablercircuitbreaker annotation, while the higher version of spring cloud uses the @ EnableHystrix annotation, which contains @ enablercircuitbreaker
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @EnableCircuitBreaker // Compatible with lower version annotations public @interface EnableHystrix { }
Claim service method
Add @ HystrixCommand annotation on the service call api method of the controller layer, and declare the service method with its fallbackMethod attribute
com.atguigu.springcloud.controller.ConsumerController#findById3
/** * Use Eureka's service discovery api to call the remote service provider (load balancing Ribbon - Simplified Version) * RestTemplate The @ LoadBalanced annotation needs to be added to the configuration bean before the RestTemplate can automatically load balance * Ribbon The bottom layer uses the {@ link DiscoveryClient} service discovery component * @param id * @return */ @GetMapping(value = "/findById3/{id}") // Fuse method: once calling the service provider fails and throws an exception, it will automatically jump to the method specified in fallbackMethod @HystrixCommand(fallbackMethod = "fallbackHandle") public Dept findById3(@PathVariable(value = "id") Integer id) { Dept dept = restTemplate.getForObject("http://microservicecloud-provider" + "/dept/" + id, Dept.class); if (null == dept) throw new RuntimeException("Query failed"); return dept; }
Method of writing service
Add the declared service method to the controller class of the current service call api
com.atguigu.springcloud.controller.ConsumerController#fallbackHandle
/** * Hystrix Fusing method of fuse * The return value type should be consistent with the above, otherwise an error will be reported * @return */ private Dept fallbackHandle(@PathVariable(value = "id") Integer id) { log.info(">>>Not found id by{}of dept information,A fuse has blown.."); Dept dept = Dept.builder() .deptno(id) .dname("Not found id by" + id + "of dept information,A fuse has blown") .build(); return dept; }
Note: the return value type of the service call method should be consistent with that of the service call api method, otherwise an exception will be thrown
For example:
private String fallbackHandle(@PathVariable(value = "id") Integer id) { log.info(">>>Not found id by{}of dept information,A fuse has blown.."); return "It's blown"; }
Because the return value type (String) of the service call method is inconsistent with the return value type (Dept) of the service call api method, the access error is reported. As shown in the following figure:
Service fuse test
Scenario 1: close the service provider
After closing the service provider and accessing the service call api, the application does not always wait for the response of the service provider, but executes the service fusing method we declared to give appropriate feedback to the customer. In the actual application scenario, the prompt interface should be returned to the customer
Now start the service provider application, access the above api again, and the call is successful
Scenario 2: the service provider accesses the non-existent dept information normally
If you access the dept information that does not exist in the system library table, that is, the service provider does not query the information and returns null, and then the service caller throws an exception through non empty judgment, will it also go to the service method? Now let's look at the test results. It has also been fused
Existing problems
@The fusing processing method of the HystrixCommand(fallbackMethod = "fallbackHandle") service has the defect that the calling logic and fusing logic are highly coupled,
Moreover, when there are many api methods called, each method must be configured with a fallback method of service, which will cause method bloat
How to solve it? The following Feign provides an interface oriented programming solution: service degradation
service degradation
Import dependency
Feign integrates with Hystix by default
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
Enable Feign's support for Hystrix
By default, Hystix is off. We need to enable it through the following configuration parameters:
#OpenFeign has built-in Hystrix, which needs to be turned on (fuse) feign: hystrix: enabled: true
Start class to enable Feign configuration
Don't forget the @ EnableFeignClients annotation on the startup class, and enable the configuration of Feign function
@SpringBootApplication @EnableEurekaClient // Open Eureka client configuration @EnableDiscoveryClient // Turn on service discovery // @The RibbonClient loads the custom Ribbon configuration class when starting the microservice // This custom configuration class cannot be placed under the current package and sub package scanned by @ ComponentScan, otherwise the custom configuration class will be shared by all Ribbon clients and cannot be customized // The following configuration explanation: use the custom Ribbon policy MyRule for microservicecloud provider microservices @RibbonClient(name = "microservicecloud-provider", configuration = {MyRuleConfig.class}) @EnableFeignClients // Enable the configuration of Feign declarative call @EnableHystrix // Turn on the Hystrix fuse configuration public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
Write FeignClient interface
Interface oriented programming is similar to the mapper interface in mybatis. When it is used, the underlying layer will automatically generate a dynamic proxy object. Add @ FeignClient annotation on the interface class, use value attribute to specify the service ID of the service provider to be called, and use fallbackFactory attribute to specify the processing class of service degradation
/** * Class description: the interface of Feign declarative call * fallback Or the fallbackFactory property specifies the service degradation processing logic * @Author wang_qz * @Date 2021/10/27 20:44 * @Version 1.0 */ @FeignClient(value = "microservicecloud-provider", fallbackFactory = DeptFeignClientFallbackFactory.class) public interface DeptFeignClientService { @PostMapping(value = "/dept/add") boolean add(Dept dept); @GetMapping(value = "/dept/{id}") Dept get(@PathVariable(value = "id") Integer id); @GetMapping(value = "/dept") List<Dept> findAll(); }
Write service degradation class
To write a class for logical processing of service degradation, you need to inherit fallbackfactory < T >, and then override feign.hystrix.FallbackFactory.Default#create method
package com.atguigu.springcloud.service.impl; import com.atguigu.springcloud.entities.Dept; import com.atguigu.springcloud.service.DeptFeignClientService; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.List; /** * Class description: the service degradation class specified by fallbackFactory * @Author wang_qz * @Date 2021/10/30 21:18 * @Version 1.0 */ @Component @Slf4j public class DeptFeignClientFallbackFactory implements FallbackFactory<DeptFeignClientService> { @Override public DeptFeignClientService create(Throwable throwable) { return new DeptFeignClientService() { @Override public boolean add(Dept dept) { log.info("Add failed, Service degradation occurred..."); return false; } @Override public Dept get(Integer id) { Dept dept = Dept.builder() .deptno(id) .dname("No query found id by" + id + "of dept information, Service degradation occurred...") .build(); return dept; } @Override public List<Dept> findAll() { log.info("Query failed, Service degradation occurred..."); return null; } }; } }
supplement
@FeignClient also provides a logical processing class for configuring service degradation with the attribute fallback. However, the implementation of service degradation class is slightly different
/** * Class description: the interface of Feign declarative call * fallback Or the fallbackFactory property specifies the service degradation processing logic * @Author wang_qz * @Date 2021/10/27 20:44 * @Version 1.0 */ @FeignClient(value = "microservicecloud-provider", fallback = DeptFeignClientServiceImpl.class) public interface DeptFeignClientService { @PostMapping(value = "/dept/add") boolean add(Dept dept); @GetMapping(value = "/dept/{id}") Dept get(@PathVariable(value = "id") Integer id); @GetMapping(value = "/dept") List<Dept> findAll(); }
The fallback declared service degradation class needs to inherit FeignClient interface class
package com.atguigu.springcloud.service.impl; import com.atguigu.springcloud.entities.Dept; import com.atguigu.springcloud.service.DeptFeignClientService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.List; /** * Class description: the service degradation class specified by fallback * @Author wang_qz * @Date 2021/10/31 11:34 * @Version 1.0 */ @Component @Slf4j public class DeptFeignClientServiceImpl implements DeptFeignClientService { @Override public boolean add(Dept dept) { log.info("Add failed, Service degradation occurred..."); return false; } @Override public Dept get(Integer id) { Dept dept = Dept.builder() .deptno(id) .dname("No query found id by" + id + "of dept information, Service degradation occurred...") .build(); return dept; } @Override public List<Dept> findAll() { log.info("Query failed, Service degradation occurred..."); return null; } }
Use of Feign
Use feign interface in service invocation api
/** * Feign Declarative call */ @Autowired private DeptFeignClientService feignClientService; /** * Call the remote service provider using Eureka's service discovery api (Feign declarative call) * Feign Built in Ribbon load balancing, default polling policy * @param id * @return */ @GetMapping(value = "/findById4/{id}") public Dept findById4(@PathVariable(value = "id") Integer id) { Dept dept = feignClientService.get(id); return dept; }
Service degradation test
Scenario 1: the service provider accesses normally. There is no dept information
It is found that there is no service degradation, but null processing is returned
Scenario 2: the service provider is normal, makes non null judgment in the service call api, throws an exception, and accesses no dept information
/** * Call the remote service provider using Eureka's service discovery api (Feign declarative call) * Feign Built in Ribbon load balancing, default polling policy * @param id * @return */ @GetMapping(value = "/findById4/{id}") public Dept findById4(@PathVariable(value = "id") Integer id) { Dept dept = feignClientService.get(id); // Non null judgment, throw exception if (null == dept) throw new RuntimeException("Query failed"); return dept; }
As a result, service degradation did not occur, but returned to the default exception information interface of the system
Scenario 3: the service provider accesses the existing dept information normally
Normal return
Scenario 4: close the service provider and visit at will
Service degradation occurs. That is, when the overall resources are insufficient, turn off some services first, and then turn them back when the resources are ok
Note that we need to make clear the difference between service fusing and service degradation
Service fuse
It is generally caused by a service failure or abnormality, which is similar to the fuse in real life. When an abnormal condition is triggered, the whole service will be blown directly, rather than waiting until this service
Service response timed out
service degradation
The so-called service degradation is generally considered from the overall load, that is, when a service is blown, the server will no longer be called. At this time, the client can prepare a local service
fallback callback, which returns a default value
In this way, although the service level drops, it will not cause avalanche effect. Pulling across the whole call chain is much better than directly paralyzing
Personal blog
Welcome to personal blog: https://www.crystalblog.xyz/
Alternate address: https://wang-qz.gitee.io/crystal-blog/