您好,登錄后才能下訂單哦!
下文給大家帶來負載均衡Ribbon之源碼詳細解析,希望能夠給大家在實際運用中帶來一定的幫助,負載均衡涉及的東西比較多,理論也不多,網上有很多書籍,今天我們就用億速云在行業內累計的經驗來做一個解答。
假設有一個分布式系統,該系統由在不同計算機上運行的許多服務組成。但是,當用戶數量很大時,通常會為服務創建多個副本。每個副本都在另一臺計算機上運行。此時,出現 “Load Balancer(負載均衡器)”。它有助于在云服務器之間平均分配傳入流量。
傳統上,Load Balancers(例如Nginx、F5)是放置在服務器端的組件。當請求來自 客戶端 時,它們將轉到負載均衡器,負載均衡器將為請求指定 服務器。負載均衡器使用的最簡單的算法是隨機指定。在這種情況下,大多數負載平衡器是用于控制負載平衡的硬件集成軟件。
重點:
當負載均衡器位于 客戶端 時,客戶端得到可用的服務器列表然后按照特定的負載均衡策略,分發請求到不同的 服務器 。
重點:
Ribbon是Netflix公司開源的一個客戶單負載均衡的項目,可以自動與 Eureka 進行交互。它提供下列特性:
實際應用中,通常將 RestTemplate 和 Ribbon 結合使用,例如:
@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 的實現 依賴關系示意圖如下:
看了以上關于負載均衡Ribbon之源碼詳細解析,如果大家還有什么地方需要了解的可以在億速云行業資訊里查找自己感興趣的或者找我們的專業技術工程師解答的,億速云技術工程師在行業內擁有十幾年的經驗了。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。