RestTemplate 负载均衡原理
RestTemplate 是通过拦截器改变请求的URI的方式来指定服务器的,此处将通过一个自定义LoadBalanced的方式来进行说明
1.导入jar包
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
2.自定义 MyLoadBalanced 注解
import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.*; @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MyLoadBalanced { }
3.编写配置类
import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; import java.util.Collections; import java.util.List; @Configuration public class MyConfig { @Autowired(required = false) @MyLoadBalanced private List<RestTemplate> tpls = Collections.emptyList(); @Bean public SmartInitializingSingleton lbInitializing() { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { System.out.println("tpls 的数量:"+tpls.size()); for (RestTemplate tpl : tpls) { List<ClientHttpRequestInterceptor> interceptors = tpl.getInterceptors(); interceptors.add(new MyInterceptor()); tpl.setInterceptors(interceptors); } } }; } }
4.编写Controller测试接口 (访问 /getUser 可以发现执行的是自定义的 MyLoadBalanced 此处应该会报错,因为地址不存在,不过我们主要是为了测试是否会执行 MyLoadBalanced)
import com.idelan.ribbon.config.MyLoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @Configuration public class MyController { @Bean @MyLoadBalanced public RestTemplate tplA() { return new RestTemplate(); } @GetMapping(value = "/getUser") public String getUser() { RestTemplate restTemplate = tplA(); String json = restTemplate.getForObject("http://smart-platform-base/platform/base/getUser", String.class); return json; } @GetMapping(value = "/hello") public String hello() { return "hello world"; } }
5.自定义拦截器来更改接口的访问地址 (@LoadBalanced 此处的逻辑会别我们复杂很多,我们只是简单模拟一下)
(1)自定义 Request 类
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import java.net.URI; import java.net.URISyntaxException; public class MyRequest implements HttpRequest { HttpRequest httpRequest; public MyRequest(HttpRequest httpRequest) { this.httpRequest = httpRequest; } @Override public HttpMethod getMethod() { return httpRequest.getMethod(); } @Override public String getMethodValue() { return httpRequest.getMethodValue(); } @Override public URI getURI() { try { URI newUri = new URI("http://localhost:8080/hello"); return newUri; } catch (URISyntaxException e) { e.printStackTrace(); } return httpRequest.getURI(); } @Override public HttpHeaders getHeaders() { return httpRequest.getHeaders(); } }
(2)自定义拦截器
import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import java.io.IOException; public class MyInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { System.out.println("~~~~~~~~自定义拦截器,uri:"+httpRequest.getURI()); System.out.println("旧的uri:"+httpRequest.getURI()); HttpRequest newRequest = new MyRequest(httpRequest); System.out.println("新的uri:"+newRequest.getURI()); return clientHttpRequestExecution.execute(newRequest, bytes); } }
测试:可以通过访问 /getUser 接口来测试,最终会返回 /hello 接口的内容,因为我们更改了访问地址