SpringBoot Actual E-commerce Project Mall (20K + STAR)
Abstract
Spring Cloud Hystrix is one of the core components of the Spring Cloud Netflix subproject. It has a series of service protection features such as service fault tolerance and thread isolation. This article describes its usage in detail.
Hystrix profile
In the microservice architecture, services communicate with each other by means of remote invocation. Once a invoked service fails, its dependent services will also fail. At this time, the spread of failure will occur, and eventually the system will break down. Hystrix implements a circuit breaker mode. When a service fails, the circuit breaker monitors and returns an error response to the caller, rather than waiting for a long time. This prevents the caller from holding up the thread for a long time, preventing the failure from spreading. Hystrix features service degradation, service circuit breaker, thread isolation, request caching, request merging, and service monitoring.
Create a hystrix-service module
Here we create a hystrix-service module to demonstrate common hystrix functions.
Add 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>
Copy the code
Configure it in application.yml
The port, registry address, and invocation path of the user-service are 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
Copy the code
Add @enablecircuitbreaker to the boot class to enable the breaker function of the Hystrix
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixServiceApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixServiceApplication.class, args);
}
Copy the code
Create a UserHystrixController interface to invoke the user-service service
Service degradation demonstration
- Add an interface for testing service degradation to UserHystrixController:
@GetMapping("/testFallback/{id}")
public CommonResult testFallback(@PathVariable Long id) {
return userService.getUser(id);
}
Copy the code
- Add the call method and service degradation method to UserService. Add @hystrixCommand annotation to the 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);
}
Copy the code
- Start the Eureka-server, user-service, and hystrix-service services.
- Call interface test: http://localhost:8401/user/testFallback/1
- Disable the user-service service and test the interface again. It is found that service degradation has occurred:
@ HystrixCommand,
Common arguments in @hystrixCommand
- FallbackMethod: Specifies the handling method for service degradation.
- IgnoreExceptions: Ignores certain exceptions without service degradation.
- CommandKey: specifies the command name, which is used to distinguish different commands.
- GroupKey: indicates the group name. Hystrix collects command alarms and dashboard information based on different groups.
- ThreadPoolKey: The name of the thread pool, which is used to divide the thread pool.
Sets the command, group, and thread pool name
- Add test interface to UserHystrixController:
@GetMapping("/testCommand/{id}")
public CommonResult testCommand(@PathVariable Long id) {
return userService.getUserCommand(id);
}
Copy the code
- Select * from UserService where UserService = 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);
}
Copy the code
Ignore some exception degradation using ignoreExceptions
- Add test interface to UserHystrixController:
@GetMapping("/testException/{id}")
public CommonResult testException(@PathVariable Long id) {
return userService.getUserException(id);
}
Copy the code
- Added in the UserService implementation method, ignores NullPointerException here, when the id is 1 throw IndexOutOfBoundsException, id for throwing NullPointerException when 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);
}
Copy the code
- Call interface test: http://localhost:8401/user/tesException/1
- Call interface test: http://localhost:8401/user/tesException/1
Hystrix request cache
When the amount of concurrent requests in the system is increasing, we need to optimize the system by using cache to reduce the number of concurrent requests and provide the effect of response speed.
Related note
- @cacheeresult: Enable caching. By default, all parameters are used as the key of the cache. CacheKeyMethod can be specified with a method of mandatory String;
- @cachekey: specifies a CacheKey. You can specify a parameter or attribute in a parameter as a CacheKey. CacheKeyMethod can also be specified by a String method;
- CacheRemove: To remove a cache, specify a commandKey.
Testing using caching
- Add a test interface that uses cache to UserHystrixController and call getUserCache three times directly:
@GetMapping("/testCache/{id}")
public CommonResult testCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.getUserCache(id);
userService.getUserCache(id);
return new CommonResult("Operation successful".200);
}
Copy the code
- Add getUserCache to 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);
}
/** * Method to generate key for cache */
public String getCacheKey(Long id) {
return String.valueOf(id);
}
Copy the code
- Call interface testing http://localhost:8401/user/testCache/1, call the three getUserCache method in this interface, but I only print a log, shows that there are two times of cache:
Test removing cache
- Add a test interface for removing caches to UserHystrixController and call the removeCache method once:
@GetMapping("/testRemoveCache/{id}")
public CommonResult testRemoveCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.removeCache(id);
userService.getUserCache(id);
return new CommonResult("Operation successful".200);
}
Copy the code
- Add a removeCache method to UserService that removes the cache:
@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);
}
Copy the code
- Call interface testing http://localhost:8401/user/testRemoveCache/1, there are two queries are walking is the interface:
Problems with cache usage
- We need to initialize and close HystrixRequestContext before and after each cached request. Otherwise, the following exceptions will occur:
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.518].Copy the code
- Here we solve this problem by using a filter to initialize and close HystrixRequestContext before and after each request:
/** * 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(); }}}Copy the code
Request to merge
The communication between services in a microservice system needs to be realized through remote calls, which will consume more thread resources as the number of calls increases. Hystrix provides the @hystrixCollapser for merging requests, reducing communication consumption and the number of threads in the collapse request.
Common properties in @hystrixCollapser
- BatchMethod: Method for setting the request merge;
- CollapserProperties: Request merge properties to control instance properties, many of which are collapsed;
- TimerDelayInMilliseconds: Collapse the property in the collapserProperties to control how often to fold the request;
Function demonstration
- Add the testCollapser method in the UserHystrixController, and make two service calls, then a third 200ms later:
@GetMapping("/testCollapser")
public CommonResult testCollapser(a) 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("Operation successful".200);
}
Copy the code
- Implement the merge request using the @hystrixCollapser, and all multiple calls to getUserFuture are transformed into a single call to getUserByIds:
@HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {
@HystrixProperty(name = "timerDelayInMilliseconds", value = "100")})public Future<User> getUserFuture(Long id) {
return new AsyncResult<User>(){
@Override
public User invoke(a) {
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());
returnuser; }}; }@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();
}
Copy the code
- http://localhost:8401/user/testCollapser access interface testing, because we set the 100 milliseconds for a request to merge, the first two are combined, merged last alone.
Common Hystrix configurations
Global configuration
hystrix:
command: # to control the behavior of HystrixCommand
default:
execution:
isolation:
strategy: THREAD # Control HystrixCommand's isolation policy, THREAD-> THREAD pool isolation policy (default), SEMAPHORE-> SEMAPHORE isolation policy
thread:
timeoutInMilliseconds: 1000 Configure the timeout period for HystrixCommand execution. If the timeout period is exceeded, the service will be degraded
interruptOnTimeout: true # Configure whether to interrupt the execution of HystrixCommand timeout
interruptOnCancel: true Configure whether to interrupt when HystrixCommand execution is cancelled
timeout:
enabled: true # Configure HystrixCommand execution with timeout enabled
semaphore:
maxConcurrentRequests: 10 When using the semaphore isolation policy, it is used to control the amount of concurrency. Requests exceeding this amount will be rejected
fallback:
enabled: true # to control whether service degradation is enabled
circuitBreaker: # used to control the behavior of HystrixCircuitBreaker
enabled: true Use to control whether the circuit breaker tracks health status and fuse requests
requestVolumeThreshold: 20 # Requests exceeding this number will be rejected
forceOpen: false Force open circuit breaker to reject all requests
forceClosed: false Force off circuit breaker to receive all requests
requestCache:
enabled: true # to control whether request caching is enabled
collapser: # Controls the execution behavior of the HystrixCollapser
default:
maxRequestsInBatch: 100 Control the maximum number of requests that can be merged at a time
timerDelayinMilliseconds: 10 # Controls how many milliseconds in which requests are combined
requestCache:
enabled: true Controls whether caching is enabled for merge requests
threadpool: # to control the behavior of the thread pool where HystrixCommand is executed
default:
coreSize: 10 # Number of core threads in the thread pool
maximumSize: 10 # Maximum number of threads in the thread pool. Requests exceeding this number will be rejected
maxQueueSize: - 1 # sets the maximum queue size for the thread pool. -1 is SynchronousQueue and all positive numbers are LinkedBlockingQueue
queueSizeRejectionThreshold: 5 # Set the thread pool queue rejection threshold. Since LinkedBlockingQueue cannot be dynamically resized, use this parameter to control the number of threads
Copy the code
The instance configuration
For instance configuration, you only need to replace default with the corresponding key in the global configuration.
hystrix:
command:
HystrixComandKey: # Replace default by Hystrixon-Comr andKey
execution:
isolation:
strategy: THREAD
collapser:
HystrixCollapserKey: # CollapserKey in the default folder
maxRequestsInBatch: 100
threadpool:
HystrixThreadPoolKey: Replace default with HystrixThreadPoolKey
coreSize: 10
Copy the code
Description of related keys in the configuration file
- HystrixComandKey corresponds to the commandKey attribute in @hystrixCommand;
- HystrixCollapserKey corresponds to the collapserKey attribute in the @hystrixCollapser annotation;
- HystrixThreadPoolKey corresponds to the threadPoolKey attribute in @hystrixCommand.
The module used
Springcloud - learning ├ ─ ─ eureka - server-- Eureka Registry├ ─ ─ the user - service-- Provides services for the CRUD interface of User objects└ ─ ─ hystrix - serviceThe Hystrix service calls the test service
Copy the code
Project source address
Github.com/macrozheng/…
The public,
Mall project full set of learning tutorials serialized, pay attention to the public account for the first time.