这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战
背景
最近在微服务的调用上涉及到一个场景,就是在多个微服务中,当某种业务条件满足的前提下,会触发当前服务去拉取多个微服务中的同一个接口去获取信息,平常我们使用的服务间调用都是通过feign的方式进行服务间http请求调用,但是在这种场景下,由于多个服务地址,不知道多到什么程度,是随着业务系统的业务逻辑来决定的,这时就得使用上RestTemplate在SpringBoot中的相应功能。
引入
RestTemplate是Spring提供的用来进行http请求的客户端,它直接提供了请求的api接口,能够有效的提高客户端的编写效率,再也不用去写httpClient的工具类了。在Springboot中使用RestTemplate和在普通服务中使用RestTemplate有所不同。
如果只是想让RestTemplate拥有http请求的能力,直接创建一个RestTemplate,然后直接调用现有api即可。
测试:简单编写一个get请求代码段。
public static void main(String[] args) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getForObject("请求的url", String.class);
}
在springboot中,RestTemplate还可以具有获取注册中心对应服务地址的功能,使用上也非常简单。只需要在注入到Spring容器中的restTemplate上添加@LoadBalanced注解后,在使用时就可以使用服务名进行http请求的调用。
@Bean("lbRestTemplate")
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
具体调用:post请求方法 通过restTemplate做负载均衡,请求地址以http://服务名/..接口地址
public String postRequest(Object object, String url) {
// 省略部分代码
return lrt.postForObject(url, request, String.class);
}
那么,问题来了,为什么添加了@LoadBalance之后,TestTemplate就具备了通过服务名进行请求的能力。
分析
首先根据springboot中的设计结构,基本上组件都是通过auto的方式添加的,@LoadBalance也不例外。
LoadBalancerAutoConfiguration是@LoadBalance生效的关键配置类,它将所有标记的对象都收集到了这里。
- 这里是一个全局的收集动作,会将所有的容器中的
@LoadBalanded标记的容器中对象都收集到这里。
public class LoadBalancerAutoConfiguration {
// 1.
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated() {
// 设置拦截
});
}
}
在实现上实际上是通过发起请求之前经过一系列的拦截器为这里收集到的restTemplate进行对应的请求地址装换。这是一个扩展点,用户也可以自己编写对应的拦截器,然后添加到需要的restTempalte中,比如需要一些日志拦截、转码、请求加密、响应解密等场景。
这里产生作用的拦截器是LoadBalancerInterceptor
- 获取对应的服务名
- 拆解出服务名,使用loadBalanceClient进行具体的处理
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
// 1.
String serviceName = originalUri.getHost();
// 2.
return this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request, body, execution));
}
}
具体的处理是在子类RibbonLoadBalancerClient中,这里实际上还涉及到负载均衡的一些策略等,然后获取到对应的服务实例,然后去发起对应的请求。
- 根据服务名获取对应的负载均衡器,这与策略相关
- 根据策略获取到对应的服务实例
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
// 1.
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 2.
Server server = getServer(loadBalancer, hint);
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
总结
RestTemplate打入容器时加上LoadBalance注解后,由于启动SpingBoot启动时触发auto自动装配机制,加载了对应的配置类,使得将有注解标记的RestTemplate对象都进行了收集,并对这些对象都添加了对应的拦截器,该拦截器的实现上进行了服务名到http直接地址的转换。