实际应用中,通常将 RestTemplate 和 Ribbon 结合使用,RestTemplate增加 @LoadBalance注解后,在进行远程调度时能够做到负载均衡,例如:
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
消费者调用服务接口:
@RestController
@Api(tags = "消费端")
public class ConsumerController {
public static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";
@Autowired
private RestTemplate restTemplate;
@ApiOperation(value="查询用户")
@PostMapping(value = "/search/v1")
public RestResult search(@RequestBody UserTest userTest) {
try {
return restTemplate.postForObject(PAYMENT_URL + "/user/search/v1", userTest, RestResult.class);
} catch (Exception e) {
return RestResult.fail(ResultCode.DATA_ACCESS_ERROR);
}
}
}
@LoadBalanced,通过源码可以发现这是一个标记注解:
用于标记要配置为使用LoadBalancer客户端的RestTemplate或WebClient bean。
// 用于标记要配置为使用LoadBalancer客户端的RestTemplate或WebClient bean。
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
LoadBalancerAutoConfiguration 负载均衡器自动配置类 在@LoadBalanced同包下
// Ribbon的自动配置(客户端负载平衡)。
@Configuration(proxyBeanMethods = false) // 代理方法 每个@Bean方法被调用多少次返回的组件都是新创建的
@ConditionalOnClass(RestTemplate.class) // 条件装配 满足
@ConditionalOnBean(LoadBalancerClient.class) // 条件装配 满足
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
/**
* Auto configuration for retry mechanism.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {
};
}
}
/**
* Auto configuration for retry intercepting mechanism.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
public static class RetryInterceptorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRetryProperties properties,
LoadBalancerRequestFactory requestFactory,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
requestFactory, loadBalancedRetryFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
从源码可以看出,这个类作用主要是使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例。
// 实现了ClientHttpRequestInterceptor 有个非常重要的拦截方法intercept
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
// for backwards compatibility
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
/*
* 拦截给定的请求,并返回响应。 通过LoadBalancerClient执行具体的请求发送。
*/
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
// http://CLOUD-PROVIDER-SERVICE/user/search/v1
final URI originalUri = request.getURI();
// CLOUD-PROVIDER-SERVICE
String serviceName = originalUri.getHost();
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
}
实现了ClientHttpRequestInterceptor 有个非常重要的拦截方法intercept,拦截给定的请求,并返回响应。 通过LoadBalancerClient执行具体的请求发送。
跟一下LoadBalancerClient源码
public interface LoadBalancerClient extends ServiceInstanceChooser {
// 根据传入的服务id,指定的负载均衡器中的服务实例执行请求。
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
// 使用来自LoadBalancer的ServiceInstance为指定服务执行请求。
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
// 创建一个带有真实主机和端口的正确URI,供系统使用。有些系统使用逻辑服务名作为主机的URI,
URI reconstructURI(ServiceInstance instance, URI original);
}
继承了ServiceInstanceChooser
根据传入的服务id,从负载均衡器中为指定的服务选择一个服务实例。
(如三台服务实例,该方法通过serverid 选择一台实例并返回)
ServiceInstance :包含了改服务实例的全部信息 如主机名端口号等等
public interface ServiceInstanceChooser {
/**
* 根据传入的服务id,从负载均衡器中为指定的服务选择一个服务实例。
* @param serviceId The service ID to look up the LoadBalancer.
* @return A ServiceInstance that matches the serviceId.
*/
ServiceInstance choose(String serviceId);
}
LoadBalancerClient 的实现类 RibbonLoadBalancerClient,关键代码如下:
@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
Assert.notNull(instance, "instance can not be null");
//获取服务实例的id
String serviceId = instance.getServiceId();
//获取服务的上下文
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
URI uri;
Server server;
//如果当前服务实例已经是ribbon服务实例
if (instance instanceof RibbonServer) {
//获取ribbon服务实例
RibbonServer ribbonServer = (RibbonServer) instance;
//得到服务
server = ribbonServer.getServer();
//通过服务实例和old url(原始url当前请求的url)重新编译成new的url
uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
}
else {
//如果第一次请求 构建一个新的服务 主机名 端口号 名称
server = new Server(instance.getScheme(), instance.getHost(),
instance.getPort());
//获取serviceId对应配置信息
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
//构建新的url
uri = updateToSecureConnectionIfNeeded(original, clientConfig,
serverIntrospector, server);
}
//返回指定实例的新的url
return context.reconstructURIWithServer(server, uri);
}
/**
执行请求
*/
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
throws IOException {
return execute(serviceId, request, null);
}
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
//通过id得到ILoadBalancer 该方法定义了 新增 删除 查找指定服务等方法
//具体看具体实现类
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
//通过serviceId解析出来指定的server (server 形式为 ip:端口号)
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
//通过得到的服务信息 创建了一个RibbonServer 特定于Ribbon本身的server实现
//该server中有对应的主机名 解析的server 源数据 等具体信息
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
//重载 得到特定的ribbonserver 就开始真正执行操作了
return execute(serviceId, ribbonServer, request);
}
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonServer) {
//如果当前的实例属于RibbonServer 得到当前的server
server = ((RibbonServer) serviceInstance).getServer();
}
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
//获取当前服务工厂的负载均衡上下文
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
//开始发送请求
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
//返回响应结果
return returnVal;
}
// catch IOException and rethrow so RestTemplate behaves correctly
catch (IOException ex) {
statsRecorder.recordStats(ex);
throw ex;
}
catch (Exception ex) {
statsRecorder.recordStats(ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
从 RibbonLoadBalancerClient 代码可以看出,实际负载均衡的是通过 ILoadBalancer 来实现的。
public interface ILoadBalancer {
/**
* 向负载均衡器中添加一个服务实例集合。
*/
public void addServers(List<Server> newServers);
/**
* 跟据key,从负载均衡器获取服务实例。
*/
public Server chooseServer(Object key);
/**
* 用来标记某个服务实例下线。
*/
public void markServerDown(Server server);
/**
* 这个方法被弃用
*/
@Deprecated
public List<Server> getServerList(boolean availableOnly);
/**
* 获取可用的服务实例集合。
*/
public List<Server> getReachableServers();
/**
* 获取所有服务实例集合,包括下线的服务实例。
*/
public List<Server> getAllServers();
}
ILoadBalancer 的实现 依赖关系示意图如下:
默认采用了ZoneAwareLoadBalancer来实现负载均衡器。
public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}
为LoadBalancer定义“负载均衡策略”的接口。 如轮询,随机等等
public interface IRule{
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
当我们使用@LoadBalanced注解赋予RestTemplate负载均衡的能力时
LoadBalancerAutoConfiguration对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例。
此时我们在使用RestTemplate调用服务时就会被intercept()方法拦截进LoadBalancerClient.execute方法。通过负载均衡器得到特定的Server然后通过request.apply(serviceInstance);执行请求得到 响应并返回。