Ribbon
Spring Cloud Ribbon 是一个基于HTTP 和 TCP 的客户端负载均衡工具, 它基于 Netflix Ribbon实现. 通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模板请求自动转换成客户端负载均衡的服务调用.
1.Rest Template
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class WebConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
}
2. 源码分析
2.1 LoadBalanced
/**
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
负载均衡客户端(LoadBalanceClient)
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* Copy From org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser
* Choose a ServiceInstance from the LoadBalancer for the specified service
*/
ServiceInstance choose(String serviceId);
/**
* execute request using a ServiceInstance from the LoadBalancer for the specified service
* 使用从负载君合器中挑选出的服务实例来执行请求内容
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
/**
* execute request using a ServiceInstance from the LoadBalancer for the specified service
* 使用指定的服务器实例执行请求内容
*/
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
/**
* Create a proper URI with a real host and port for systems to utilize.
* Some systems use a URI with the logical serivce name as the host,
* such as http://myservice/path/to/service. This will replace the
* service name with the host:port from the ServiceInstance.
* 为系统构建一个合适的 host:port 形式的 URI
*/
URI reconstructURI(ServiceInstance instance, URI original);
}

被@LoadBalanced 注解修饰的RestTemplate 队列列表,在LoadBalancerAutoConfiguration#loadBalancedRestTemplateInitializerDeprecated初始化, 给客户端负载均衡RestTemplate增加 LoadBalancerInterceptor拦截器
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));
}
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
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, requestFactory.createRequest(request, body, execution));
}
/**
* 执行 org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient中的execute方法
*/
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
// 通过serviceId获取具体的服务实例
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
// 执行rest请求
return execute(serviceId, ribbonServer, request);
}
}
2.3 负均衡策略

