目录
LoadBalancerAutoConfiguration自动配置类
ribbon自动配置类
自动配置类基本是通过springboot启动时扫描的META-INF/spring.factories 和@ConditionalOnXXX 就能实现自动装配
当前自动配置类中有一个负载均衡窃听器配置静态类LoadBalancerInterceptorConfig
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
/*
LoadBalancerInterceptor 拦截器是Ribbon核心组件极其重要
restTemplate发出的请求进行拦截处理
*/
//将拦截器加载到ioc
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);
};
}
}
LoadBalancerInterceptor 负载均衡拦截器
/*
实现了ClientHttpRequestInterceptor 重新了非常重要的拦截方法intercept
*/
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
//是完成负载均衡工作 执行指定请求 创建新的url
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));
}
/*
拦截给定的请求,并返回响应。
给定的ClientHttpRequestExecution允许拦截器将请求和响应传递给链中的下一个实体。
此方法的典型实现将遵循以下模式:
检查请求和正文
可选地包装请求以筛选HTTP属性。
可以选择修改请求的正文。
restTemplate的方法都会经过这个方法
*/
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
//获取当前请求的URL
final URI originalUri = request.getURI();
//获取请求的名字
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));
}
}
LoadBalancerClient客户端负载均衡器
表示客户端负载均衡器
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* 使用来自指定服务的LoadBalancer的ServiceInstance执行请求。
* @param serviceId 用于查找LoadBalancer的服务ID。
* @param request 负载均衡请求域
* @param <T> 响应的类型
* @throws IOException in case of IO issues.
* @return 对所选对象执行LoadBalancerRequest回调的结果 ServiceInstance.
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
/**
* 使用来自指定服务的LoadBalancer的ServiceInstance执行请求。
* @param serviceId 用于查找LoadBalancer的服务ID。
* @param serviceInstance 要对其执行请求的服务。 指定好具体某个客户端的实例
* @param request 负载均衡请求域
* @param <T> 响应的类型
* @throws IOException in case of IO issues.
* @return 对所选对象执行LoadBalancerRequest回调的结果 ServiceInstance.
*/
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
/**
* 创建一个带有真实主机和端口的正确URI,供系统使用。有些系统使用逻辑服务名作为主机的URI,
* 例如http://myservice/path/to/service。这将用主机:端口自服务实例。
* @param instance 需要被包装操作的服务实例
* @param 以主机作为逻辑服务名创建一个URI。
* @return 重建的URI。被分配服务的实例构建的URI
*/
URI reconstructURI(ServiceInstance instance, URI original);
}
ServiceInstanceChooser
由使用负载均衡器选择要向其发送请求的服务器的类实现。
/**
* 从LoadBalancer中为指定的服务选择ServiceInstance。并返回该实例
* (如三台服务实例,该方法通过serverid 选择一台实例并返回)
* ServiceInstance :包含了改服务实例的全部信息 如主机名端口号等等
*/
ServiceInstance choose(String serviceId);
上述负载均衡器的的所有接口(执行请求,查找实例,返回实例新建的url)都是通过RibbonLoadBalancerClient类来实现
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);
}
/**
* New: Execute a request by selecting server using a 'key'. The hint will have to be
* the last parameter to not mess with the `execute(serviceId, ServiceInstance,
* request)` method. This somewhat breaks the fluent coding style when using a lambda
* to define the LoadBalancerRequest.
* @param <T> returned request execution result type
* @param serviceId id of the service to execute the request to
* @param request to be executed
* @param hint used to choose appropriate {@link Server} instance
* @return request execution result
* @throws IOException executing the request may result in an {@link IOException}
*/
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 特定于rebbon本身的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;
}
特定于ribbon的实例
总结
当我们在ioc中注入restTemplate时加了@LoadBalanced注解就是启用了负载均衡LoadBalancerAutoConfiguration就开始拦截rustTemplate调用的服务。此时我们在使用rustTemplate调用服务时就会被intercept()方法拦截此时LoadBalancerClient.execute方法会去解析得到RibbonServer调用getServer(主机名:端口号 )得到 特定的server 通过request.apply(serviceInstance);按照设置的规则执行请求得到 响应(信息)并返回