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);
}
}