假設有一個分佈式系統,該系統由在不一樣計算機上運行的許多服務組成。可是,當用戶數量很大時,一般會爲服務建立多個副本。每一個副本都在另外一臺計算機上運行。此時,出現 「Load Balancer(負載均衡器)」。它有助於在服務器之間平均分配傳入流量。html
傳統上,Load Balancers(例如Nginx、F5)是放置在服務器端的組件。當請求來自 客戶端 時,它們將轉到負載均衡器,負載均衡器將爲請求指定 服務器。負載均衡器使用的最簡單的算法是隨機指定。在這種狀況下,大多數負載平衡器是用於控制負載平衡的硬件集成軟件。java
重點:git
當負載均衡器位於 客戶端 時,客戶端獲得可用的服務器列表而後按照特定的負載均衡策略,分發請求到不一樣的 服務器 。程序員
重點:github
Ribbon是Netflix公司開源的一個客戶單負載均衡的項目,能夠自動與 Eureka 進行交互。它提供下列特性:算法
實際應用中,一般將 RestTemplate 和 Ribbon 結合使用,例如:spring
@Configuration public class RibbonConfig { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
消費者調用服務接口:緩存
@Service public class RibbonService { @Autowired private RestTemplate restTemplate; public String hi(String name) { return restTemplate.getForObject("http://service-hi/hi?name="+name,String.class); } }
@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 { }
經過註釋能夠知道@LoadBalanced註解是用來給RestTemplate作標記,方便咱們對RestTemplate添加一個LoadBalancerClient,以實現客戶端負載均衡。微信
根據spring boot的自動配置原理,能夠知道同包下的LoadBalancerAutoConfiguration,應該是實現客戶端負載均衡器的自動化配置類。代碼以下:
@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 loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { 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 restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean @ConditionalOnMissingBean public LoadBalancedRetryFactory loadBalancedRetryFactory() { return new LoadBalancedRetryFactory() {}; } } @Configuration @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進行轉換獲取到具體應該請求哪一個服務實例。
那再看看添加的攔截器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)); } }
從代碼能夠看出 LoadBalancerInterceptor 攔截了請求後,經過LoadBalancerClient執行具體的請求發送。
打開LoadBalancerClient,發現它是一個接口:
public interface LoadBalancerClient { ServiceInstance choose(String serviceId); <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; URI reconstructURI(ServiceInstance instance, URI original); }
接口說明:
LoadBalancerClient 有一個惟一的實現類 RibbonLoadBalancerClient,關鍵代碼以下:
public class RibbonLoadBalancerClient implements LoadBalancerClient { public ServiceInstance choose(String serviceId) { Server server = this.getServer(serviceId); return server == null ? null : new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server)); } public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId); Server server = this.getServer(loadBalancer); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } else { RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server)); return this.execute(serviceId, ribbonServer, request); } } protected Server getServer(String serviceId) { return this.getServer(this.getLoadBalancer(serviceId)); } protected Server getServer(ILoadBalancer loadBalancer) { return loadBalancer == null ? null : loadBalancer.chooseServer("default"); } protected ILoadBalancer getLoadBalancer(String serviceId) { return this.clientFactory.getLoadBalancer(serviceId); } //省略... }
從 RibbonLoadBalancerClient 代碼能夠看出,實際負載均衡的是經過 ILoadBalancer 來實現的。
ILoadBalancer 接口代碼以下:
public interface ILoadBalancer { public void addServers(List<Server> newServers); public Server chooseServer(Object key); public void markServerDown(Server server); public List<Server> getReachableServers(); public List<Server> getAllServers(); }
接口說明:
ILoadBalancer 的實現 依賴關係示意圖以下:
那麼在整合Ribbon的時候Spring Cloud默認採用了哪一個具體實現呢?咱們經過RibbonClientConfiguration配置類,能夠知道在整合時默認採用了ZoneAwareLoadBalancer來實現負載均衡器。
@Bean @ConditionalOnMissingBean public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) { return (ILoadBalancer)(this.propertiesFactory .isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory .get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater)); }
從這段代碼 ,也能夠看出,負載均衡器所需的主要配置項是IClientConfig, ServerList, ServerListFilter, IRule, IPing, ServerListUpdater。下面逐一分析他們。
IClientConfig 用於對客戶端或者負載均衡的配置,它的默認實現類爲 DefaultClientConfigImpl。
爲LoadBalancer定義「負載均衡策略」的接口。
public interface IRule{ public Server choose(Object key); public void setLoadBalancer(ILoadBalancer lb); public ILoadBalancer getLoadBalancer(); }
IRule 的實現 依賴關係示意圖以下:
定義如何 「ping」 服務器以檢查其是否存活。
public interface IPing { public boolean isAlive(Server server); }
IPing 的實現 依賴關係示意圖以下:
定義獲取全部的服務實例清單。
public interface ServerList<T extends Server> { public List<T> getInitialListOfServers(); public List<T> getUpdatedListOfServers(); }
ServerList 的實現 依賴關係示意圖以下:
容許根據過濾配置動態得到的具備所需特性的候選服務器列表。
public interface ServerListFilter<T extends Server> { public List<T> getFilteredListOfServers(List<T> servers); }
ServerListFilter 的實現 依賴關係示意圖以下:
用於執行動態服務器列表更新。
public interface ServerListUpdater { public interface UpdateAction { void doUpdate(); } void start(UpdateAction updateAction); void stop(); String getLastUpdate(); long getDurationSinceLastUpdateMs(); int getNumberMissedCycles(); int getCoreThreads(); }
ServerListUpdater 的實現 依賴關係示意圖以下:
https://github.com/Netflix/ribbon/wiki
http://tech.lede.com/2018/01/11/rd/server/NetflixRibbon/
http://blog.didispace.com/springcloud-sourcecode-ribbon/
https://www.fangzhipeng.com/springcloud/2017/08/11/Ribbon-resources.html
https://blog.csdn.net/Tincox/article/details/79210309
歡迎掃碼或微信搜索公衆號《程序員果果》關注我,關注有驚喜~