Ribbon负载均衡原理学习记录


ribbon项目通过RestTemplate发起微服务请求,但是知道的都是知道RestTemplate是spring自带的 那么和ribbon有什么关系呢?

 
标识.png

 

我们找到了@LoadBalanced标注的负载均衡标识
点进去

 

 
loadbalanced.png

@Retention(RetentionPolicy.RUNTIME)
ps:这里标识指的是 当编译完成后 运行保留 在jvm中运行 可以被反射调用
注意上面的绿色注释LoadBalancerClient来配置它
打开LoadBalancerClient类
里面有三个方法

 

 
image.png

 

同时继承了ServiceInstanceChooser 类
字面意思 服务选择实例

public interface LoadBalancerClient extends ServiceInstanceChooser { /** * execute request using a ServiceInstance from the LoadBalancer for the specified * service * @param serviceId the service id to look up the LoadBalancer * @param request allows implementations to execute pre and post actions such as * incrementing metrics * @return the result of the LoadBalancerRequest callback on the selected * ServiceInstance */ <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; /** * execute request using a ServiceInstance from the LoadBalancer for the specified * service * @param serviceId the service id to look up the LoadBalancer * @param serviceInstance the service to execute the request to * @param request allows implementations to execute pre and post actions such as * incrementing metrics * @return the result of the LoadBalancerRequest callback on the selected * ServiceInstance */ <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. * @param instance * @param original a URI with the host as a logical service name * @return a reconstructed URI */ URI reconstructURI(ServiceInstance instance, URI original); } 
public interface ServiceInstanceChooser { /** * Choose a ServiceInstance from the LoadBalancer for the specified service * @param serviceId the service id to look up the LoadBalancer * @return a ServiceInstance that matches the serviceId */ ServiceInstance choose(String serviceId); } 

ServiceInstance choose(String serviceId); 根据serviceId即服务ID查询服务

<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; 根据服务来执行请求内容

URI reconstructURI(ServiceInstance instance, URI original);
拼接请求方式 传统中是ip:port 现在是服务名称:port 形式

定位当前类位置 发现相关类

 

 
image.png

 

当然太多了 就不仔细说了 有空自己去看
我们关注自动装配这个类 有关自动装配相关知识 后面再仔细说

/** * Auto configuration for Ribbon (client side load balancing). * * @author Spencer Gibb * @author Dave Syer * @author Will Tran */ @Configuration @ConditionalOnClass(RestTemplate.class) @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializer( final List<RestTemplateCustomizer> customizers) { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } } }; } @Autowired(required = false) private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); @Bean @ConditionalOnMissingBean public LoadBalancerRequestFactory loadBalancerRequestFactory( LoadBalancerClient loadBalancerClient) { return new LoadBalancerRequestFactory(loadBalancerClient, transformers); } @Configuration @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 new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean public RetryTemplate retryTemplate() { RetryTemplate template = new RetryTemplate(); template.setThrowLastExceptionOnExhausted(true); return template; } @Bean @ConditionalOnMissingBean public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() { return new LoadBalancedRetryPolicyFactory.NeverRetryFactory(); } @Bean @ConditionalOnMissingBean public LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory() { return new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory(); } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryInterceptorAutoConfiguration { @Bean @ConditionalOnMissingBean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory, LoadBalancedBackOffPolicyFactory backOffPolicyFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, lbRetryPolicyFactory, requestFactory, backOffPolicyFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } } 

ps:@ConditionalOnMissingClass当缺少指定的value类时 创建某类
@ConditionalOnClass(RestTemplate.class) RestTemplate类必须存在当前工作环境中
@ConditionalOnBean(LoadBalancerClient.class) 在spring的Bean工程中必须有LoadBalancerClient的实现Bean;
该配置类中 主要做了三件事:
1.创建了一个LoadBalancerInterceptor 负载均衡拦截器 用于对客户端发起的请求进行拦截,以实现客户端负载均衡.
2.创建一个RestTemplateCustomizer的Bean,用于给RestTemplate增加
LoadBalancerInterceptor拦截器。
3.维护被@LoadBananced注解修饰的RestTemplate对象列表,并在初始化,通过调用RestTemplateCustomizer实例来给需要的客户端负载均衡RestTemplate增加拦截器LoadBalancerInterceptor 拦截器

看LoadBalancerInterceptor拦截器是如何将一个普通RestTemplate实现负载均衡的

**
 * @author Spencer Gibb * @author Dave Syer * @author Ryan Baxter * @author William Tran */ 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)); } } 
public interface ClientHttpRequestInterceptor { /** * Intercept the given request, and return a response. The given {@link ClientHttpRequestExecution} allows * the interceptor to pass on the request and response to the next entity in the chain. * * <p>A typical implementation of this method would follow the following pattern: * <ol> * <li>Examine the {@linkplain HttpRequest request} and body</li> * <li>Optionally {@linkplain org.springframework.http.client.support.HttpRequestWrapper wrap} the request to filter HTTP attributes.</li> * <li>Optionally modify the body of the request.</li> * <li><strong>Either</strong> * <ul> * <li>execute the request using {@link ClientHttpRequestExecution#execute(org.springframework.http.HttpRequest, byte[])},</li> * <strong>or</strong> * <li>do not execute the request to block the execution altogether.</li> * </ul> * <li>Optionally wrap the response to filter HTTP attributes.</li> * </ol> * * @param request the request, containing method, URI, and headers * @param body the body of the request * @param execution the request execution * @return the response * @throws IOException in case of I/O errors */ ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException; } 

可以看到LoadBalancerInterceptor 实现了重写ClientHttpRequestInterceptor Http拦截的请求
可以看到拦截器中注入了LoadBalancerClient的实现.当一个被@LoadBalanced 注解修饰的RestTemplate对象向外发起Http请求时用intercept方法 截取出 由于我们host用的是RPC框架 originalUri.getHost();获取的是服务名 然后在调用之前的LoadBalancerClient execute去执行请求
继续往下 看LoadBalancerClient的execute实现

@Override public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { 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)); 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) { 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; } 

可以看到通过
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer);
获取服务实例


    protected Server getServer(ILoadBalancer loadBalancer) { if (loadBalancer == null) { return null; } return loadBalancer.chooseServer("default"); // TODO: better handling of key } 
 
image.png

 

并未使用之前的LoadBalancerClient类的choose来获取
而是使用netflix ribbon来获取

public interface ILoadBalancer { void addServers(List<Server> var1); Server chooseServer(Object var1); void markServerDown(Server var1); /** @deprecated */ @Deprecated List<Server> getServerList(boolean var1); List<Server> getReachableServers(); List<Server> getAllServers(); } 

主要是这几个功能
addServers添加服务
chooseServer 选择服务
markServerDown停止服务
getReachableServers 获得正常能访问的服务
getAllServers 获取所有服务 包括dang机的

 
image.png


ILoadBalancer 的实现扩展以上 而springCloud扩展默认采用ZoneAwareLoadBalancer

 
image.png

 

2019年4月8日23:59:53 明天上班 休息了


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM