待整理

# 深入探讨 Ribbon

# 入口1、RibbonClientConfiguration 看 Ribbon 全局

官方给出的提示

Spring Cloud creates a new ensemble as an ApplicationContext on demand for each named client using RibbonClientConfiguration. This contains (amongst other things) an ILoadBalancer, a RestClient, and a ServerListFilter.

根据 RibbonClientConfiguration 分离相关信息如下。

# 默认规则接口 (IRule

职责:选择服务器 -> 根据 负载均衡算法,获取的需要调用的服务器

默认实现:ZoneAvoidanceRule

@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
	if (this.propertiesFactory.isSet(IRule.class, name)) {
		return this.propertiesFactory.get(IRule.class, config, name);
	}
	ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
	rule.initWithNiwsConfig(config);
	return rule;
}

# 默认 PING 策略(IPing

职责:监测服务器是否存活。 -> 通过 ping 远程服务地址,有响应-存活,无响应-down 默认实现:DummyPing

@Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
	if (this.propertiesFactory.isSet(IPing.class, name)) {
		return this.propertiesFactory.get(IPing.class, config, name);
	}
	return new DummyPing();
}

# 默认 负载均衡器 ILoadBalancer

职责:(通过查看 基础接口:ILoadBalancer

addServers : 增加服务其

chooseServer : 根据关联 key 获取服务器

markServerDown : 标记服务器宕机

getServerList/getReachableServers/getAllServers : 获取服务列表

-> 通过 服务列表 能对 服务列表 进行 各种基础数据的增删改查。

默认实现:ZoneAvoidanceRule

@Bean
	@ConditionalOnMissingBean
	public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
			ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
			IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
		if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
			return this.propertiesFactory.get(ILoadBalancer.class, config, name);
		}
		return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
				serverListFilter, serverListUpdater);
	}

# 默认 负载均衡器上下文 LoadBalancerContext

职责:

reconstructURIWithServer : 根据 服务信息转化 具体 主机+端口的形式

getServerFromLoadBalancer:获取服务实例(通过 ILoadBalancer 进行获取)

默认实现:RibbonLoadBalancerClient

@Bean
@ConditionalOnMissingBean
public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,
	IClientConfig config, RetryHandler retryHandler) {
	return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
}

# 服务列表

职责:初始化,服务列表并获取 默认实现:ConfigurationBasedServerList

@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList<Server> ribbonServerList(IClientConfig config) {
	if (this.propertiesFactory.isSet(ServerList.class, name)) {
		return this.propertiesFactory.get(ServerList.class, config, name);
	}
	ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
	serverList.initWithNiwsConfig(config);
	return serverList;
}

小贴士:如果 服务列表 通过 Eureka 来进行 维护的时候,默认实现为:DiscoveryEnabledNIWSServerList

# 入口2、 RIbbon 自动 装配 RibbonAutoConfiguration

关注 LoadBalancerClient 负载均衡器客户端 ,代码如下:

@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
	return new RibbonLoadBalancerClient(springClientFactory());
}

其主要职责 与 Ribbon 的上下文 LoadBalancerContext 的功能职责相似,都是用来进行 服务名称--URL 的转化,以及 服务列表获取服务实例的获取

# Ribbon 原理总结

通过上述,我们可以知道,Ribbon 其实就是通过维护一个 服务名称IP+端口 的映射列表。使得无需关注 对应 IP + 端口,只要一次配置,就能够在项目中直接通过 服务名称 进行 HTTP 远程服务访问。

Ribbon 还提供了 一些系列的负载均衡算法,进行客户端的负载均衡。

其相关配置信息也存在 其上下文 LoadBalancerContext 中,而通过查看,我们可以得知,其父容器为 ApplicationContext ,可以理解为 其内容仍然存在在 IOC 容器

# Ribbon 通过 注解 @LoadBalanced 实现 RestTemplate 负载均衡原理

查看相关配置类 LoadBalancerAutoConfiguration,代码如下:

@Configuration
@ConditionalOnClass({RetryTemplate.class})
public static class RetryInterceptorAutoConfiguration {
    public RetryInterceptorAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory, LoadBalancedBackOffPolicyFactory backOffPolicyFactory, LoadBalancedRetryListenerFactory retryListenerFactory) {
        return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, lbRetryPolicyFactory, requestFactory, backOffPolicyFactory, retryListenerFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
        return new RestTemplateCustomizer() {
            public void customize(RestTemplate restTemplate) {
                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            }
        };
    }
}

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

结论

通过查看源码,我们可以很直观的看出来,RibbonRestTemplate 负载均衡的实现,其实是通过 拦截器的方式来做的,也就是在 RestTemplate 触发 请求进入到拦截器中,通过拦截器 中的 loadBalancer ,进行 ServerName -URL 的转化。

精彩内容推送,请关注公众号!
最近更新时间: 4/13/2020, 9:09:21 PM