Ribbon核心工作原理

Ribbon核心工作原理

1.Ribbon的核心接口

接口 描述 默認實現
IClientConfig 定義 Ribbon 中管理配置的接口 DefaultClientConfiglmpl
IRule 定義 Ribbon 中負載均衡策略的接口 ZoneAvoidanceRule
IPing 定義定期 ping 服務檢查可用性的接口 DummyPing
ServerList 定義獲取服務列表方法的接口 ConfigurationBasedServerList
ServerListFilter 定義特定期望獲取服務列表方法的接口 ZonePreferenceServerListFilter
ILoadBalancer 定義負載均衡選擇服務的核心方法的接口 ZoneAwareLoadBalancer
ServerListUpdater 爲 DynamicServerListLoadBalancer 定義動態 更新
服務列表的接口
PollingServerListUpdater

2.LoadBalancerClient-負載均衡的基石

/**
 * Represents a client side load balancer
 * @author Spencer Gibb
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {
	// 使用來自LoadBalancer的ServiceInstance對指定對象執行請求
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
	// 使用來自LoadBalancer的ServiceInstance對指定對象執行請求
	<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
	// 使用真實的主機和端口創建適當的URI
	URI reconstructURI(ServiceInstance instance, URI original);
}
public interface ServiceInstanceChooser {

    /**從LoadBalancer中爲指定服務選擇一個ServiceInstance
     * Choose a ServiceInstance from the LoadBalancer for the specified service
     */
    ServiceInstance choose(String serviceId);
}

這兩個類奠定了從負載均衡器中選取服務實例,用服務實例構建真實服務地址,以及發送請求的底層架構基礎。

3.LoadBalancerAutoConfiguration-負載均衡配置的自動裝配

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
    /***
    *從類的註解就可以看出,它配置加載的時機一是當前工程環境必須有 RestTemplate 的實 例,
二是在工程環境中必須初始化了 LoadBalancerClient 的實現類
    **/
	@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();
	/**
	*LoadBalancerRequestFactory 用於創建 LoadBalancerRequest和LoadBalancerlnterceptor 使用,它在低版本中是沒有的.LoadBalancerInterceptorConfig 中 則 維 護 了 Load- Balancerinterceptor 與RestTemplateCustomizer 的實例 , 它們的作用如下:
	□ LoadBalancerlnterceptor : 攔截每一次 HTTP 請求,將請求綁定進 Ribbon 負載均衡的 生命週期。
	□ RestTemplateCustomizer: 爲每個 RestTemplate 綁定 LoadBalancerlnterceptor 攔截器。
	**/
	@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);
            };
		}
	}
......
}

在這裏就很明確了,通過 loadBalancerClient生成 LoadBalancerInterceptor併爲每個 RestTemplate 綁定 LoadBalancerlnterceptor 攔截器。

4.LoadBalancerInterceptor-負載均衡攔截器

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private LoadBalancerClient loadBalancer;
	private LoadBalancerRequestFactory requestFactory;

.....
    // 執行攔截器
	@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));
	}
}

利用 ClientHttpRequestlnterceptor 來對每次 HTTP 請求進行攔截的,此類是 Spring 中維護的請求攔截器,實現它的 intercept 方法就可以使請求進入方法體,從而做一些處 理。可以看出這裏把請求攔截下來之後使用了 LoadBalancerClientexecute 方法來處理請求,由於我 們在 RestTemplate 中使用的 URI 是形如 http://myservice/path/to/service 的,所以這裏的 getHost() 方法實 際 取 到 的 就 是 服 務 名 myservice。LoadBalancerClient 接 口 只 有 一 個 實 現 類 , 即RibbonLoadBalancerClient

4.RibbonLoadBalancerClient-Ribbon的負載均衡實現

public class RibbonLoadBalancerClient implements LoadBalancerClient {
.....
    @Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
    	// 獲取具體的服務實例 就是根基服務的實例名字獲取服務的相關元數據信息
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    	// Server 就是具體服務實例的封裝 
    	// 就是發生負載均衡過程的地方
		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);
	}
    
    protected Server getServer(ILoadBalancer loadBalancer) {
		if (loadBalancer == null) {
			return null;
		}
		return loadBalancer.chooseServer("default"); // default 就是默認的負載均衡算法的key
	}
    /********************************非本類的實現********************************/
     public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try { 
                // 我們發現, rule.choose(key) 中的 rule 其實就是 IRule, 至此,攔截的HTTP 請求與負載均 衡策略得以關聯起來
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章