Ribbon: 主要爲微服務名解析並提供負載均衡的功能,可理解爲 DNS + Nginx的組合體
1. Ribbon 基本概念
- RestTemplate: 提供完整Http功能訪問API,提供interceptors嵌入點,Ribbon通過loadBalancerInterceptor實現服務名轉真實服務地址,MetricsClientHttpRequestInterceptor實現接口指標監控
- loadBalancerInterceptor:負載均衡攔截器,主要爲RestTemplate與Ribbon結合提供切入點,RestTemplate藉助loadBalancerInterceptor引入Ribbon組件,提供對服務名的真實解析
- RibbonLoadBalancerClient:Ribbon負載均衡器客戶端,主要用以協調原始請求在Ribbon框架處理流程,類似Mybatis Executor角色
- IClientConfig: 客戶端配置,可用於負載均衡器、Http客戶端等配置,可理解爲配置緩存
- IRule: 負載均衡規則
- ILoadBalancer: 負載均衡器根據IRule規則選擇最合適的服務請求處理服務器地址,內部維護服務地址列表(PollingServerListUpdater 定時任務維護30s刷新1次)和上線服務地址列表(PingTask: 定時任務維護(Timer調度) 10s)
IRule: 規則均衡規則
- BestAvailableRule: 最大可用規則,取 ActiveRequestsCount 最小的服務器,即併發請求壓力最下的服務器認爲是最合適的服務器
- AvailabilityFilteringRule: 可用性過濾規則,過濾條件:1. 連續不斷的連接或讀取失敗 2. 活躍鏈接數超過閾值,默認:Integer.MAX_VALUE
- WeightedResponseTimeRule: 權重響應時間規則,即響應時間越長權重越低,後續被選中的概率越低
- RetryRule:重試規則,如若第1次獲取不成功,則一致重試,到截至時間時不在重試,跳出重試機制有定時任務InterruptTask設置線程中斷位置判斷,默認重試時間500毫秒,若沒設置subRule,默認輪詢RoundRobinRule
- RoundRobinRule:輪詢,AtomicInteger保證線程併發安全性存儲下1次目標服務器索引,計算規則: ( 當前索引 + 1 ) % (服務地址列表數量)
- RandomRule: 服務地址列表隨機獲取1個
- ZoneAvoidanceRule(默認規則): 綜合區域和可用性判斷,(ZoneAvoidancePredicate:過濾掉監控指標達到閾值的區域,AvailabilityPredicate:過濾服務斷路或服務器連接太多的服務地址)
ILoadBalancer: 負載均衡器
核心接口:
- addServers: 添加服務器列表,主要爲負載均衡進行合適服務節點選取時提供原料
- chooseServer: 跪在均衡器選取合適請求服務目標節點
- markServerDown:標識目標服務器下線(手動調度),默認通過PingTask定時調度
- getServerList: 獲取服務地址列表
- getReachableServers: 獲取可用服務地址列表(BaseLoadBalancer.upServerList 緩存維護)
- getAllServers:獲取所有服務地址列表(BaseLoadBalancer.allServerList 緩存維護)
實現:
- NoOpLoadBalancer: 空實現
- BaseLoadBalancer: 基礎實現
- DynamicServerListLoadBalancer:重寫initWithNiwsConfig方法,藉助PollingServerListUpdater實現服務列表定時(30s)動態刷新
- ZoneAwareLoadBalancer: 區域感知負載均衡器,根據區域活躍請求數來判斷,避免區域請求過於集中(ZoneAvoidanceRule規則判斷)
2. Ribbon 默認裝配組件
核心配置類:RibbonAutoConfiguration、RibbonClientConfiguration
RibbonAutoConfiguration: Ribbon自動配置
- configurations:RibbonClientSpecification ribbon客戶端配置,主要由@RibbonClient、@RibbonClients
- ribbonEagerLoadProperties: Ribbon服務餓加載配置:默認Ribbon服務列表啓動的時候不會加載,通過ribbon.eager-load-enabled和ribbon.eager-load-clients配置需提前加載的服務地址信息,便於應用啓動第一次訪問服務可無延遲啓動
- SpringClientFactory(重點):Ribbon Client、Loadbalancer、IClientConfig創建工廠,藉助父類NamedContextFactory 實現1個服務1個IOC容器,服務之間通過容器隔離管理,維護在NamedContextFactory.contexts 緩存
- LoadBalancerClient:Ribbon負載均衡器客戶端,負責整個負載均衡流程調度,例如:轉義服務名爲真實服務節點地址,默認實現RibbonLoadBalancerClient
- LoadBalancedRetryPolicyFactory: 重試策略,依賴於spring-retry 組件
- RibbonApplicationContextInitializer:Ribbon應用上下文初始化器,監聽ApplicationReadyEvent事件,可以提前加載ribbonEagerLoadProperties配置的服務地址列表
- ConditionalOnRibbonRestClient: Ribbon客戶端條件選擇器,通過ribbon.http.client.enabled或ribbon.restclient.enabled開啓,默認不開啓
- PropertiesFactory: 屬性工廠維護的Ribbon配置,用於服務自定義Ribbon配置(ribbon客戶端名稱 + ribbon + classNameProperty(類名),例如:NFLoadBalancerPingClassName 定時ping服務列表決定是否上下線)
PropertiesFactory用例:
- eureka-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule: eureka-provider 服務負載均衡規則採用RandomRule (EurekaRibbonClientConfiguration實現)
RibbonClientConfiguration: Ribbon 客戶端設置,前面是可選,最後是默認HttpClientRibbonConfiguration
- HttpClientConfiguration:默認引入DefaultApacheHttpClientConnectionManagerFactory、DefaultApacheHttpClientFactory(Http客戶端連接管理器工廠、Http客戶端創建工廠),可選支持OkHttp工具包
- OkHttpRibbonConfiguration: OKHttp 框架支持,通過ribbon.okhttp.enabled開啓,藉助OKHttp實現底層Http交互
- RestClientRibbonConfiguration: OverrideRestClient重寫RestTempalte,通過ribbon.http.client.enabled或ribbon.restclient.enabled開啓
- HttpClientRibbonConfiguration: 默認Ribbon客戶端配置,藉助HttpClientConfiguration引入Factory創建HttpClientConnectionManager、CloseableHttpClient、RibbonLoadBalancingHttpClient(提供負載均衡的HttpClient)、RetryableRibbonLoadBalancingHttpClient(RibbonLoadBalancingHttpClient基礎加重試功能)
- IClientConfig: Ribbon客戶端配置,默認DefaultClientConfigImpl,可通過IClientConfig.Builder建造器自定義
- IRule: 負載均衡規則,默認ZoneAvoidanceRule: 區域和可用性規則
- IPing: ping 服務地址列表,判斷節點是否存活,默認DummyPing 總是存活,可通過 (${ribbon.client.name} + ribbon + className)規則全局設置
- ServerList: 服務地址API,默認ConfigurationBasedServerList,用於獲取服務關聯的列表,支持Eureka、Zookeeper、Nacos等服務註冊中心
- ServerListUpdater: 服務地址定時更新,默認PollingServerListUpdater,定時任務30s刷新1次
- ILoadBalancer: ribbon 負載均衡器,默認ZoneAwareLoadBalancer
- ZonePreferenceServerListFilter: 區域更偏愛的過濾器,例如:服務列表獲取有Localhost、Debian, 更偏愛本地Localhost環境,類似Spark近距離數據處理提高效率場景,工作流程: ServerList獲取上線列表 --> ZonePreferenceServerListFilter 過濾更偏愛的節點信息
- RibbonLoadBalancerContext:Ribbon負載均衡器上下文,相當於運行環境,可用與指標監控,例如:更新服務節點(ServerStats)狀態:niws.loadbalancer.default.connectionFailureCountThreshold等指標
- RetryHandler:重試處理器,例如:Ribbon客戶端請求發生異常,請求是否重試、重試的節點判斷等,可避免故障節點請求路由
- ServerIntrospector: 服務器內省機制,用於獲取服務節點元數據、判斷端口是否安全 Https或Http請求(ServerIntrospectorProperties.securePorts 配置)
3. Ribbon 整合Eureka
自動配置類:RibbonEurekaAutoConfiguration
@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class): 引入Eureka Ribbon定製化配置 EurekaRibbonClientConfiguration
條件選擇器
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonAndEurekaEnabledCondition.class)
@interface ConditionalOnRibbonAndEurekaEnabled {
}
//解釋: 只要引入EurekaClient 依賴,條件默認成立,OnRibbonAndEurekaEnabledCondition 即是否引入EurekaClient組件
private static class OnRibbonAndEurekaEnabledCondition extends AllNestedConditions {
public OnRibbonAndEurekaEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass(DiscoveryEnabledNIWSServerList.class)
@ConditionalOnBean(SpringClientFactory.class)
@ConditionalOnProperty(value = "ribbon.eureka.enabled", matchIfMissing = true)
static class Defaults {}
@ConditionalOnBean(EurekaClient.class)
static class EurekaBeans {}
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
static class OnEurekaClientEnabled {}
}
EurekaRibbonClientConfiguration: Eureka Ribbon的個性化組件定製
- IPing: Eureka定製服務地址ping策略:NIWSDiscoveryPing根據InstanceStatus狀態判斷,UP,DOWN,STARTING, OUT_OF_SERVICE,UNKNOWN
- ServerList: 服務地址列表API DomainExtractingServerList裝飾DiscoveryEnabledNIWSServerList,由eurekaClient獲取服務地址列表
- ServerIntrospector: 服務節點內省器,EurekaServerIntrospector實現
4. RestTemplate 模板定製
4.1 LoadBalancerInterceptor
作用: 藉助RestTemplate的Interceptor機制引入Ribbon功能- 服務名解析、服務節點負載均衡
配置:LoadBalancerAutoConfiguration
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
// 負載均衡請求工廠,相當於包裝原始的HttpRequest方便負載均衡器處理
return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
}
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
// LoadBalancerInterceptor: 自定義負載均衡攔截器
@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 配置攔截器
restTemplate.setInterceptors(list);
}
};
}
}
4.2 MetricsHandlerInterceptor
作用:Ribbon Http請求監控
自動配置:MetricsInterceptorConfiguration
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(WebMvcConfigurerAdapter.class)
static class MetricsWebResourceConfiguration extends WebMvcConfigurerAdapter {
// 指標攔截器
@Bean
MetricsHandlerInterceptor servoMonitoringWebResourceInterceptor() {
return new MetricsHandlerInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(servoMonitoringWebResourceInterceptor());
}
}
// 藉助BeanPostProcessor聲明週期追加到restTemplate
private static class MetricsInterceptorPostProcessor
implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext context;
private MetricsClientHttpRequestInterceptor interceptor;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof RestTemplate) {
if (this.interceptor == null) {
this.interceptor = this.context
.getBean(MetricsClientHttpRequestInterceptor.class);
}
RestTemplate restTemplate = (RestTemplate) bean;
// create a new list as the old one may be unmodifiable (ie Arrays.asList())
ArrayList<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(interceptor);
// 追加指標攔截器
interceptors.addAll(restTemplate.getInterceptors());
restTemplate.setInterceptors(interceptors);
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
this.context = context;
}
}
5. Ribbon 客戶端服務列表地址涮新機制
調用棧:
DynamicServerListLoadBalancer.enableAndInitLearnNewServersFeature
PollingServerListUpdater.start
源碼:
@Override
public synchronized void start(final UpdateAction updateAction) {
// CAS: 設置狀態位,開啓定時刷新服務節點列表信息
if (isActive.compareAndSet(false, true)) {
final Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
if (!isActive.get()) {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
return;
}
try {
// 策略接口: 取決於服務註冊中心類型
// Zookeeper: ZookeeperServerList
// Eureka: DiscoveryEnabledNIWSServerList
// ConfigurationBasedServerList: 手動配置
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
// 定時任務調度刷新服務節點信息,默認 30s
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
initialDelayMs,
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
} else {
logger.info("Already active, no-op");
}
}
6. ILoadBalancer 節點列表信息維護
節點數據緩存:
- BaseLoadBalancer.allServerList: 所有節點列表
- BaseLoadBalancer.upServerList: 在線節點列表(可用節點列表)
心跳檢測調度: lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000); 默認10s
upServerList維護(PingTask):
class Pinger {
private final IPingStrategy pingerStrategy;
public Pinger(IPingStrategy pingerStrategy) {
this.pingerStrategy = pingerStrategy;
}
public void runPinger() throws Exception {
if (!pingInProgress.compareAndSet(false, true)) {
return; // Ping in progress - nothing to do
}
// we are "in" - we get to Ping
Server[] allServers = null;
boolean[] results = null;
Lock allLock = null;
Lock upLock = null;
try {
/*
* The readLock should be free unless an addServer operation is
* going on...
*/
allLock = allServerLock.readLock();
allLock.lock();
allServers = allServerList.toArray(new Server[allServerList.size()]);
allLock.unlock();
int numCandidates = allServers.length;
// 根據Ping策略ping對應服務節點,具體Ping的方式IPing
results = pingerStrategy.pingServers(ping, allServers);
final List<Server> newUpList = new ArrayList<Server>();
final List<Server> changedServers = new ArrayList<Server>();
for (int i = 0; i < numCandidates; i++) {
boolean isAlive = results[i];
Server svr = allServers[i];
boolean oldIsAlive = svr.isAlive();
// 設置服務節點存活狀態
svr.setAlive(isAlive);
if (oldIsAlive != isAlive) {
changedServers.add(svr);
logger.debug("LoadBalancer [{}]: Server [{}] status changed to {}",
name, svr.getId(), (isAlive ? "ALIVE" : "DEAD"));
}
if (isAlive) {
newUpList.add(svr);
}
}
upLock = upServerLock.writeLock();
upLock.lock();
// 重置上線服務器列表
upServerList = newUpList;
upLock.unlock();
// ServerStatusChangeListener: 服務器狀態監聽器通知
notifyServerStatusChangeListener(changedServers);
} finally {
pingInProgress.set(false);
}
}
}
7. RestTemplate 流程分析
案例代碼:restTemplate.getForEntity("http://spring-eureka-service/hello", String.class).getBody();
7.1 RestTemplate doExecute
核心代碼:ClientHttpRequest request = createRequest(url, method);
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
// 請求工廠: InterceptingClientHttpRequestFactory,裝飾默認工廠:SimpleClientHttpRequestFactory
// 工作流程:InterceptingClientHttpRequestFactory構建負載均衡請求成簡單請求最後委託給SimpleClientHttpRequestFactory處理原始Http請求
// InterceptingHttpAccessor.getRequestFactory:RestTemplate父類重寫 HttpAccessor 接口
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
if (logger.isDebugEnabled()) {
logger.debug("Created " + method.name() + " request for \"" + url + "\"");
}
return request;
}
7.2 RibbonClientHttpRequestFactory 構建請求
接口:org.springframework.http.client.InterceptingClientHttpRequestFactory.createRequest
protected InterceptingClientHttpRequest(ClientHttpRequestFactory requestFactory,
List<ClientHttpRequestInterceptor> interceptors, URI uri, HttpMethod method) {
this.requestFactory = requestFactory; // 包裝工廠:SimpleClientHttpRequestFactory
this.interceptors = interceptors; // 藍機器:LoadBalancerInterceptor、MetricsClientHttpRequestInterceptor
this.method = method;
this.uri = uri;
}
7.3 RestTemplate 執行請求
源碼:
org.springframework.web.client.RestTemplate.doExecute
response = request.execute();
org.springframework.http.client.AbstractClientHttpRequest.execute
org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal
org.springframework.http.client.InterceptingClientHttpRequest.executeInternal
InterceptingClientHttpRequest.executeInternal
@Override
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
// 請求攔截器處理,類似Servlet FilterChain機制,責任鏈方式處理
return requestExecution.execute(this, bufferedOutput);
}
private class InterceptingRequestExecution implements ClientHttpRequestExecution {
private final Iterator<ClientHttpRequestInterceptor> iterator;
public InterceptingRequestExecution() {
this.iterator = interceptors.iterator();
}
@Override
public ClientHttpResponse execute(HttpRequest request, final byte[] body) throws IOException {
// RestTemplate配置攔截器,攔截器處理流程,設計很巧妙 nextInterceptor.intercept 將自身傳遞進去,然後回調execute方法,從而實現整個攔截鏈條的完整處理
// 類似設計:FilterChain.doFilter(request, response); org.apache.catalina.core.ApplicationFilterChain.internalDoFilter
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
// 普通處理流程
else {
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
List<String> values = entry.getValue();
for (String value : values) {
delegate.getHeaders().add(entry.getKey(), value);
}
}
if (body.length > 0) {
if (delegate instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
@Override
public void writeTo(final OutputStream outputStream) throws IOException {
StreamUtils.copy(body, outputStream);
}
});
}
else {
StreamUtils.copy(body, delegate.getBody());
}
}
return delegate.execute();
}
}
}
7.4 LoadBalancerInterceptor 負載均衡器攔截器
源碼:org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor.intercept
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost(); // 獲取主機名 http://spring-eureka-service/hello,即spring-eureka-service 服務名
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
7.5 LoadBalancerClient 負責任務調度
LoadBalancerClient默認注入:RibbonLoadBalancerClient(RibbonAutoConfiguration.loadBalancerClient)
接口:org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.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);
}
//服務器信息封裝類,包含服務ID,服務節點信息、是否HTTPS安全機制、節點服務器元數據信息
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
接着調用:org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute
@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);
}
//SpringClientFactory 緩存獲取或利用配置構建 RibbonLoadBalancerContext
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
// 構建Ribbon狀態記錄器,通知noteOpenConnection、noteRequestCompletion,用於請求狀態記錄
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;
}
7.6 請求底層處理邏輯
LoadBalancerRequest: 內部類
new LoadBalancerRequest<ClientHttpResponse>() {
@Override
public ClientHttpResponse apply(final ServiceInstance instance) throws Exception {
// 請求包裝類,包含loadBalancer,用於後面解析服務名爲真實服務器IP
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
// 請求轉換器:允許應用轉換負載均衡請求,例如:添加自定義請求頭等
if (transformers != null) {
for (LoadBalancerRequestTransformer transformer : transformers) {
serviceRequest = transformer.transformRequest(serviceRequest, instance);
}
}
// 執行請求,回到 InterceptingRequestExecution.execute, 因爲前面攔截器執行完成,所以不在走攔截流程
return execution.execute(serviceRequest, body);
}
};
@Override
public ClientHttpResponse execute(HttpRequest request, final byte[] body) throws IOException {
// 之前攔截器鏈條已經走完,這裏不走這段邏輯
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
// 真實請求處理,最終底層由org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal 實現,很簡單的Http請求處理
else {
// 注意:這裏請求爲ServiceRequestWrapper,包含負載均衡器 request.getURI() 即 ServiceRequestWrapper.getURI (this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());)
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
List<String> values = entry.getValue();
for (String value : values) {
delegate.getHeaders().add(entry.getKey(), value);
}
}
if (body.length > 0) {
if (delegate instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
@Override
public void writeTo(final OutputStream outputStream) throws IOException {
StreamUtils.copy(body, outputStream);
}
});
}
else {
StreamUtils.copy(body, delegate.getBody());
}
}
return delegate.execute();
}
}
}
*** 至此整個Ribbon負載均衡處理流程完成 ***
8.0 組件詳細說明
8.0.1 負載均衡客戶端 LoadBalancerClient
作用:負責負載均衡請求流程調度
接口:
execute(String serviceId, LoadBalancerRequest<T> request) 執行請求
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) 執行請求(重載)
reconstructURI: 解析服務ID爲真實服務地址
服務地址解析:
- org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.reconstructURI
@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
Assert.notNull(instance, "instance can not be null");
String serviceId = instance.getServiceId();
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
// 構造真實服務器地址信息
Server server = new Server(instance.getHost(), instance.getPort());
// 獲取RibbonClient客戶端配置
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
// 按需更新URI地址:例如:Https
URI uri = RibbonUtils.updateToHttpsIfNeeded(original, clientConfig, serverIntrospector, server);
// 翻譯URI爲真實服務器地址
return context.reconstructURIWithServer(server, uri);
}
- com.netflix.loadbalancer.LoadBalancerContext.reconstructURIWithServer
public URI reconstructURIWithServer(Server server, URI original) {
String host = server.getHost();
int port = server.getPort();
String scheme = server.getScheme();
//請求主機地址 == 服務節點host
if (host.equals(original.getHost())
&& port == original.getPort()
&& scheme == original.getScheme()) {
return original;
}
if (scheme == null) {
scheme = original.getScheme();
}
if (scheme == null) {
scheme = deriveSchemeAndPortFromPartialUri(original).first();
}
// 路徑解析處理
try {
StringBuilder sb = new StringBuilder();
sb.append(scheme).append("://");
// url包含用戶信息解析:例如: http://zhangsan:[email protected] 格式
if (!Strings.isNullOrEmpty(original.getRawUserInfo())) {
sb.append(original.getRawUserInfo()).append("@");
}
// 拼接真實服務器IP
sb.append(host);
if (port >= 0) {
sb.append(":").append(port);
}
// 拼接其他路徑
sb.append(original.getRawPath());
if (!Strings.isNullOrEmpty(original.getRawQuery())) {
sb.append("?").append(original.getRawQuery());
}
if (!Strings.isNullOrEmpty(original.getRawFragment())) {
sb.append("#").append(original.getRawFragment());
}
// 構建新的URI直接返回
URI newURI = new URI(sb.toString());
return newURI;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}