In the previous 18 articles, we have implemented the advertising system's advertising placement, advertising retrieval business functions, using service discovery Eureka, service invocation Feign, gateway routing Zuul and error fusing Spring Cloud components such as Hystrix.
Simple call relationships:
But the system will always report errors. We defined some fault-tolerant classes and methods before, but only in the console can we see the error information. We want to statistics some data, how can we see our service invocation more intuitively? Next, we discuss a new fuse monitoring component Hystrix Dashbo. Ard, as its name implies, can be seen from its name. It is a graphical interface for monitoring.
Use of Hystrix in Services
Use in conjunction with openfeign
In our actual project, the most frequently used is the combination of FeignClient#fallback and Hystrix to achieve fusing. Let's take a look at our implementation in mscx-ad-feign-sdk.
@FeignClient(value = "mscx-ad-sponsor", fallback = SponsorClientHystrix.class) public interface ISponsorFeignClient { @RequestMapping(value = "/ad-sponsor/plan/get", method = RequestMethod.POST) CommonResponse<List<AdPlanVO>> getAdPlansUseFeign(@RequestBody AdPlanGetRequestVO requestVO); @RequestMapping(value = "/ad-sponsor/user/get", method = RequestMethod.GET) /** * Feign If the buried hole is a Get request, you must add {@link RequestParam} before all parameters, and you cannot use {@link Param}. * It is automatically forwarded as a POST request. */ CommonResponse getUsers(@RequestParam(value = "username") String username); }
In the above code, we customize a feignclient and give the client a fallback implementation class:
@Component public class SponsorClientHystrix implements ISponsorFeignClient { @Override public CommonResponse<List<AdPlanVO>> getAdPlansUseFeign(AdPlanGetRequestVO requestVO) { return new CommonResponse<>(-1, "mscx-ad-sponsor feign & hystrix get plan error."); } @Override public CommonResponse getUsers(String username) { return new CommonResponse<>(-1, "mscx-ad-sponsor feign & hystrix get user error."); } }
This fallback class implements our custom ISponsorFeignClient, because the fallback method must be consistent with the method signature of the original execution class, so that when execution fails, the fallback method can be mapped to the response's degraded method/fault-tolerant method by reflection.
In the mscx-ad-search service, we call our mscz-ad-sponsor service by injecting ISponsorFeignClient.
@RestController @Slf4j @RequestMapping(path = "/search-feign") public class SearchFeignController { /** * Inject our custom FeignClient */ private final ISponsorFeignClient sponsorFeignClient; @Autowired public SearchFeignController(ISponsorFeignClient sponsorFeignClient) { this.sponsorFeignClient = sponsorFeignClient; } @GetMapping(path = "/user/get") public CommonResponse getUsers(@Param(value = "username") String username) { log.info("ad-search::getUsersFeign -> {}", JSON.toJSONString(username)); CommonResponse commonResponse = sponsorFeignClient.getUsers(username); return commonResponse; } }
Using HystrixCommand
In fact, Hystrix itself provides a direct way to apply the method, using @ com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand. Let's take a look at the source code of this class:
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface HystrixCommand { ... /** * Specifies a method to process fallback logic. * A fallback method should be defined in the same class where is HystrixCommand. * Also a fallback method should have same signature to a method which was invoked as hystrix command. * for example: * <code> * @HystrixCommand(fallbackMethod = "getByIdFallback") * public String getById(String id) {...} * * private String getByIdFallback(String id) {...} * </code> * Also a fallback method can be annotated with {@link HystrixCommand} * <p/> * default => see {@link com.netflix.hystrix.contrib.javanica.command.GenericCommand#getFallback()} * * @return method name */ String fallbackMethod() default ""; ... }
We focus on two main points:
- @ Target({ElementType.METHOD}) indicates that current annotations can only be applied to methods.
- Fault tolerance can be guaranteed by directly defining fallbackMethod. One drawback of this method is that it must be implemented in the same class file as the method, which will make our method appear particularly redundant and inelegant when implemented.
Take our advertisement query in mscx-ad-search as an example:
@Service @Slf4j public class SearchImpl implements ISearch { /** * Fault Tolerance Method for Query Advertising * * @param e The second parameter can be unspecified. If you need to track errors, specify * @return Returns an empty map object */ public SearchResponse fetchAdsFallback(SearchRequest request, Throwable e) { System.out.println("Inquiry Advertising Failed to Enter Fault Tolerant Degradation : %s" + e.getMessage()); return new SearchResponse().builder().adSlotRelationAds(Collections.emptyMap()).build(); } @HystrixCommand(fallbackMethod = "fetchAdsFallback") @Override public SearchResponse fetchAds(SearchRequest request) { ... } }
When we request an error, we go to our fallback method, which is implemented by starting the @EnableCircuitBreaker annotation at application startup. This annotation intercepts all HystrixCommand methods through AOP, integrates HystrixCommand into springboot containers, and annotates the @EnableCircuitBreaker annotation. Method is put into the hystrix thread, and once it fails, fallback method is invoked by reflection.
Create dashboard project
The above code shows two ways Hystrix implements fusing. Next, we implement a graphical interface for request monitoring and create mscx-ad-dashboard, Let's code.
Still follow our spring boot trilogy:
-
Additional dependence
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> <version>1.2.7.RELEASE</version> </dependency> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
-
append notes to
/** * AdDashboardApplication for Hystrix Dashboard Startup class * * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</a> * @since 2019/8/15 */ @SpringBootApplication @EnableDiscoveryClient @EnableHystrixDashboard public class AdDashboardApplication { public static void main(String[] args) { SpringApplication.run(AdDashboardApplication.class, args); } }
-
Reconfiguration
server: port: 1234 spring: application: name: mscx-ad-dashboard eureka: client: service-url: defaultZone: http://server1:7777/eureka/,http://server2:8888/eureka/,http://server3:9999/eureka/ management: endpoints: web: exposure: include: "*"`
Start directly and you can see the following pages:
Add the service address to be monitored: