RestTemplate Load Balancing Principle
Why does RestTemplate have load balancing?
After using @LoadBalanced, the Spring container will add interceptors to the modified RestTemplate at startup, and LoadBalanced-related load balancing interfaces will be used to process requests in the interceptor. Through such an indirect processing, the original RestTemplate will be changed from the original RestTemplate to a more NB, so it has load balancing. Function.
So in the content of this chapter, the author will take you to implement a very simple LoadBalanced annotation to add an interceptor to RestTemplate. As for how to achieve load balancing in the interceptor, this still needs to be explored... (If you know how to achieve it, please let me know one or two. Thank you first.)
Introducing web dependencies: pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.0.3.RELEASE</version> </dependency> </dependencies>
Custom Annotation: MyLoadBalanced.java
/** * Modification: domains, parameters, methods */ @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) /** * The purpose is to define how long annotations are retained by the annotations it annotates. There are three strategies defined in the Retention Policy enumeration * 1. SOURCE: Ignored by the compiler * 2. CLASS: Annotations will be retained in the Class file, but not in the VM at run time. This is the default behavior, and all annotations that are not annotated with Retention will adopt this strategy * 3. RUNTIME: Reserve until runtime. So we can get annotation information by reflection. */ @Retention(RetentionPolicy.RUNTIME) /** * Restricted annotation */ @Qualifier public @interface MyLoadBalanced { }
Create Controller: MyController.java
@RestController
@Configuration public class MyController { @Bean // Modify getRestTemplate to Bean for spring management @MyLoadBalanced // Here use the annotations just customized public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
Create a configuration class: Config.java
@Configuration public class Config { @Autowired(required = false)// Necessary, will be those who are by the uuuuuuuuuuu@MyLoadBalanced Annotated objects are automatically assembled into tpls Collection @MyLoadBalanced private List<RestTemplate> tpls = Collections.emptyList(); // stay spring After the container is started, it needs to be done for each RestTemplate We need to set up an interceptor, which will achieve load balancing function. @Bean public SmartInitializingSingleton lbInitializing(){ return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { System.out.println("RestTemplate Collection size:"+tpls.size()); } }; } }
Let's create a startup class to see if the auto-assembly is successful: Application.java
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
As shown in Figure 1, the RestTemplate collection size: 1 indicates that our configuration is in effect...
Let's create a custom interceptor: MyInterceptor.java. The interceptor needs to implement the ClientHttpRequest Interceptor interface
public class MyInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { System.out.println("==================== Enter Custom Interceptor"); return null; } }
Now that we have a custom interceptor, let's modify the configuration class to add an interceptor to every RestTemplate modified by @MyLoadBalanced after the program starts. Modify Config.java as follows:
@Bean public SmartInitializingSingleton lbInitializing(){ return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for(RestTemplate rtl : tpls){ // To prevent overwriting the default interceptor, remove the default interceptor List<ClientHttpRequestInterceptor> interceptors = rtl.getInterceptors(); // Add custom interceptors to default interceptors interceptors.add(new MyInterceptor()); // to RestTemplate Setting up interceptors rtl.setInterceptors(interceptors); } } }; }
Interceptors are used to intercept requests. We also need to define an interface in MyController.java to call tests. Modify MyController.java as follows:
@RequestMapping(value="/getPolice", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE) public String getPolice(){ RestTemplate rtl = getRestTemplate(); String result = rtl.getForObject("http://springCloud-ribbon-police/getPolice", String.class); return result; }
Next, we visit the next interface to see if the interceptor has been successfully configured, as shown in the figure:
The output of the interceptor proves that the interceptor configuration is successful. The reason for the error is that null was returned in the interceptor, so let's solve this problem now.
In the interceptor temporarily does not achieve load balancing function, we take jump as an example, to explain... Modify the old request and return a new request. In this case, we need to return a new request object.
Create NewRequest.java and implement the HttpRequest interface: NewRequest.java
public class NewRequest implements HttpRequest{ private HttpRequest sourceRequest;// Original request request public NewRequest(HttpRequest sourceRequest){ this.sourceRequest = sourceRequest; } @Override public HttpHeaders getHeaders() { return sourceRequest.getHeaders(); } @Override public String getMethodValue() { return sourceRequest.getMethodValue(); } @Override public URI getURI() { try { // To be intercepted URI,Modified to new URI URI uri = new URI("http://localhost:9090/getPoliceById/123"); return uri; } catch (Exception e) { e.printStackTrace(); } return sourceRequest.getURI(); } }
Modify our custom interceptor: MyInterceptor.java
public class MyInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { System.out.println("==================== This is a custom interceptor"); System.out.println("==================== Old URL: "+request.getURI()); NewRequest newRequest = new NewRequest(request); System.out.println("==================== New URL: "+newRequest.getURI()); return execution.execute(newRequest, body); } }
We run the program again and get the following results:
Take another look at the results returned from the page to perfect the interception jump:
OK, that's all about this chapter. A simple custom annotation, a custom interceptor, have you learned it?