【微服务】Ribbon工作原理源码解析

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: 规则均衡规则

  1. BestAvailableRule: 最大可用规则,取 ActiveRequestsCount 最小的服务器,即并发请求压力最下的服务器认为是最合适的服务器
  2. AvailabilityFilteringRule: 可用性过滤规则,过滤条件:1. 连续不断的连接或读取失败 2. 活跃链接数超过阈值,默认:Integer.MAX_VALUE
  3. WeightedResponseTimeRule: 权重响应时间规则,即响应时间越长权重越低,后续被选中的概率越低
  4. RetryRule:重试规则,如若第1次获取不成功,则一致重试,到截至时间时不在重试,跳出重试机制有定时任务InterruptTask设置线程中断位置判断,默认重试时间500毫秒,若没设置subRule,默认轮询RoundRobinRule
  5. RoundRobinRule:轮询,AtomicInteger保证线程并发安全性存储下1次目标服务器索引,计算规则: ( 当前索引 + 1 ) % (服务地址列表数量)
  6. RandomRule: 服务地址列表随机获取1个
  7. ZoneAvoidanceRule(默认规则): 综合区域和可用性判断,(ZoneAvoidancePredicate:过滤掉监控指标达到阈值的区域,AvailabilityPredicate:过滤服务断路或服务器连接太多的服务地址)

ILoadBalancer: 负载均衡器
核心接口:

  • addServers: 添加服务器列表,主要为负载均衡进行合适服务节点选取时提供原料
  • chooseServer: 跪在均衡器选取合适请求服务目标节点
  • markServerDown:标识目标服务器下线(手动调度),默认通过PingTask定时调度
  • getServerList: 获取服务地址列表
  • getReachableServers: 获取可用服务地址列表(BaseLoadBalancer.upServerList 缓存维护)
  • getAllServers:获取所有服务地址列表(BaseLoadBalancer.allServerList 缓存维护)

实现:

  1. NoOpLoadBalancer: 空实现
  2. BaseLoadBalancer: 基础实现
  3. DynamicServerListLoadBalancer:重写initWithNiwsConfig方法,借助PollingServerListUpdater实现服务列表定时(30s)动态刷新
  4. 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为真实服务地址

服务地址解析:

  1. 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);
}
  1. 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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章