一:简要回顾
先来回顾下上个章节讲到的sping cloud提供的负载均衡逻辑。spring-cloud-commons中对负载均衡做了几个抽象,
1,LoadBalancerInterceptor类
负载均衡拦截器,用于添加到RestTemplate的拦截器链中从而拦截请求。其intercept方法会执行LoadBalancerClient的execute方法。
对于一般的拦截器来说,在拦截器的intercept方法中经过处理后就直接调用ClientHttpRequestExecution的execute方法迭代执行完所有的拦截器,并将最后个拦截器的执行结果返回。而在这里却会先将其执行代码封装到LoadBalancerRequest类型的对象中,等到确认了要访问的服务实例后再执行【在LoadBalancerClient中】。
2,LoadBalancerClient接口
定义了负载均衡客户端所需要实现的方法。继承自ServiceInstanceChooser接口实现从负载均衡器中筛选出服务实例的功能;同时定义了一个供 LoadBalancerInterceptor 调用的execute方法,该方法接收服务id(serviceId)与LoadBalancerRequest类型实例作为参数,返回ClientHttpResponse 类型的响应结果。
3,LoadBalancerRequest接口
定义LoadBalancerClient接口中execute方法执行的参数类型,该接口中只有一个接收ServiceInstance参数的apply方法。该接口的目的是封装拦截器的执行逻辑,等到后面确认服务实例后再调用。 该类型的实例由LoadBalancerRequestFactory创建返回。
4,LoadBalancerRequestFactory类
只是一个普通的工具类工厂,目的是创建并返回LoadBalancerRequest类型实例对象。默认的实现是在createRequest方法中将ServiceInstance、HttpRequest 、LoadBalancerClient 封装到ServiceRequestWrapper包装器对象中,最后将包装器对象作为参数继续调用ClientHttpRequestExecution 的execute方法从而继续调用其它的拦截器对象直到请求完毕并返回。
ServiceRequestWrapper主要是重写了getURI方法使用LoadBalancerClient的reconstructURI方法得到真正的服务实例访问地址。
整个的过程大致是
- LoadBalancerInterceptor拦截请求执行intercept方法
- 使用intercept方法中调用LoadBalancerRequestFactory来创建封装了拦截器处理逻辑的LoadBalancerRequest类型实例对象
- 使用serviceId和LoadBalancerRequest作为参数调用LoadBalancerClient实例的execute方法,在得到访问的服务实例后执行LoadBalancerRequest的apply方法并返回
二,SpringCloud Ribbon源码分析
针对spring-cloud-netflix-ribbon-2.2.0.RELEASE的源码
我们继续分析Ribbon是怎么让RestTemplate达到负载均衡的目的的。
在其/META-INF/spring.factories文件中对启动类进行了如下的配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
由上个章节所知,只要在IOC中具备LoadBalancerClient的实现类即可让RestTemplate实现负载均衡,Ribbon也正是这么做的,先看RibbonAutoConfiguration的里面都做了些啥
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
//对Ribbon客户端进行配置
@RibbonClients
//在EurekaClientAutoConfiguration配置类执行后执行
@AutoConfigureAfter(
name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
//在LoadBalancerAutoConfiguration与AsyncLoadBalancerAutoConfiguration前执行
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
AsyncLoadBalancerAutoConfiguration.class })
//读取配置数据
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
//收集IOC中所有的RibbonClient的配置信息
//【通过上面的@RibbonClients注解生成的RibbonClientSpecification对象】
@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
//读取“ribbon.eager-load.enabled”以及“ribbon.eager-load.clients”的配置
//用于判断是否启动“饥饿加载”【应用启动后立即创建指定的Ribbon客户端】
@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;
@Bean
public HasFeatures ribbonFeature() {
return HasFeatures.namedFeature("Ribbon", Ribbon.class);
}
//创建Ribbon使用的SpringClientFactory对象,该对象会读取Ribbon客户端的配置信息,
//并为每个客户端创建一个AnnotationConfigApplicationContext上下文【首次调用某个客户端的时候才创建,
//可以使用“饥饿加载”让指定的服务一开始就创建】。
//内部使用org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
//作为默认的配置配置项
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
//创建LoadBalancerClient 类型的RibbonLoadBalancerClient对象【这里是最关键的地方】,
//当这个实例存在后,就可以使用RestTemplate进行客户端负载均衡了
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
//创建可以进行“重试”的LoadBalancedRetryFactory类型对象
@Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
final SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryFactory(clientFactory);
}
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}
//立马执行“饥饿加载”
@Bean
@ConditionalOnProperty("ribbon.eager-load.enabled")
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(springClientFactory(),
ribbonEagerLoadProperties.getClients());
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpRequest.class)
//当ribbon.restclient.enabled参数设置为ture时执行以下配置【如果这里为true,那么拦截器将不能被设置】
@ConditionalOnRibbonRestClient
protected static class RibbonClientHttpRequestFactoryConfiguration {
@Autowired
private SpringClientFactory springClientFactory;
@Bean
public RestTemplateCustomizer restTemplateCustomizer(
final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
return restTemplate -> restTemplate
.setRequestFactory(ribbonClientHttpRequestFactory);
}
@Bean
public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
return new RibbonClientHttpRequestFactory(this.springClientFactory);
}
}
}
从上可知,@RibbonClients注解会首先执行,然后如果有EurekaClientAutoConfiguration配置类(Eureka Starter中)存在则会等待该配置类结束后再执行RibbonAutoConfiguration,最后执行Spring Cloud Commos里面的LoadBalancerAutoConfiguration与AsyncLoadBalancerAutoConfiguration配置。如下图所示:
小结:从上面的代码可知,该配置类的目的就是创建LoadBalancerClient 的实例对象RibbonLoadBalancerClient。
首先得到每个客户端的配置(RibbonClientSpecification),然后将这些配置传递给SpringClientFactory 来创建每个Ribbon客户端,接着使用SpringClientFactory作为构造参数创建LoadBalancerClient类型的RibbonLoadBalancerClient实例,从而便与RestTemplate集成在了一起【SprigClientFactory中还会读取配置文件中的Ribbon客户端配置】。
1,@RibbonClients
@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
//这里是关键的地方
@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClients {
RibbonClient[] value() default {};
Class<?>[] defaultConfiguration() default {};
}
其中的RibbonClient注解源码如下
@Configuration(proxyBeanMethods = false)
//同@RibbonClients 注解一样引入同一个类
@Import(RibbonClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RibbonClient {
/**
* Ribbon client 的名字,同name属性
*/
String value() default "";
/**
* Ribbon client 的名字
*/
String name() default "";
/**
* 自定义的@Configuration类,用于配置该Ribbon 客户端
* 可以配置ILoadBalancer,ServerListFilter,IRule等类型的bean,将会覆盖默认的配置 (RibbonClientConfiguration )
*/
Class<?>[] configuration() default {};
}
RibbonClientConfigurationRegistrar
该类的目的是加载在注解中配置的Ribbon客户端的配置信息,默认情况下由于并没有设置任何值,所以并不会加载任何的配置到IOC中。但我们完全可以自定义@Configuration来定制Ribbon客户端的行为。
public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
//该方法被调用对注解进行解析,然后将解析结果封装成RibbonClientSpecification实例对象加入IOC中
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
Map<String, Object> attrs = metadata
.getAnnotationAttributes(RibbonClients.class.getName(), true);
if (attrs != null && attrs.containsKey("value")) {
//解析各个设置到这里的@RibbonClient注解
AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
for (AnnotationAttributes client : clients) {
//将解析出的每个注解注册到IOC中【服务名 - 服务对应的配置】
registerClientConfiguration(registry, getClientName(client),
client.get("configuration"));
}
}
if (attrs != null && attrs.containsKey("defaultConfiguration")) {
//添加默认的配置【如果有设置】
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
//注册客户端的配置信息
registerClientConfiguration(registry, name,
attrs.get("defaultConfiguration"));
}
Map<String, Object> client = metadata
.getAnnotationAttributes(RibbonClient.class.getName(), true);
String name = getClientName(client);
if (name != null) {
registerClientConfiguration(registry, name, client.get("configuration"));
}
}
private String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
String value = (String) client.get("value");
if (!StringUtils.hasText(value)) {
value = (String) client.get("name");
}
if (StringUtils.hasText(value)) {
return value;
}
throw new IllegalStateException(
"Either 'name' or 'value' must be provided in @RibbonClient");
}
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(RibbonClientSpecification.class);
//Ribbon客户端名字【RestTemplate中通过使用该名字替换URL中的主机地址进行请求】
builder.addConstructorArgValue(name);
//Ribbon客户端使用的配置类(用于覆盖全局默认配置)
builder.addConstructorArgValue(configuration);
//将RibbonClientSpecification实例定义信息注册到Spring中
registry.registerBeanDefinition(name + ".RibbonClientSpecification",
builder.getBeanDefinition());
}
}
如果在代码中我们通过@RibbonClients或@RibbonClient注解对Ribbon客户端进行了配置(也可在配置文件中配置),那么这些配置都会被封装到RibbonClientSpecification类型的并添加到IOC,从而供后面的注入所使用。
完全可以在其它地方再次使用@RibbonClients来定义其全局默认配置,从而影响使用Ribbon客户端发起请求时的行为,如与服务发现集成。
2,SpringClientFactory
该类是Ribbon的关键工厂类,它主要负责 创建客户端(Client)、负载平衡器(ILoadBalancer)和客户端配置实例(IClientConfig)。它为每个Ribbon 客户端 创建一个Spring ApplicationContext,并从中提取它所需的bean。
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
static final String NAMESPACE = "ribbon";
public SpringClientFactory() {
//重点:加载默认的配置,该配置的内容在后面说明
//三个参数分别为:默认配置类,属性源(指定读取什么开头的配置),属性名
super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
}
/**
* Get the rest client associated with the name.
*/
public <C extends IClient<?, ?>> C getClient(String name, Class<C> clientClass) {
return getInstance(name, clientClass);
}
/**
* Get the load balancer associated with the name.
*/
public ILoadBalancer getLoadBalancer(String name) {
return getInstance(name, ILoadBalancer.class);
}
/**
* Get the client config associated with the name.
*/
public IClientConfig getClientConfig(String name) {
return getInstance(name, IClientConfig.class);
}
/**
* Get the load balancer context associated with the name.
*/
public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {
return getInstance(serviceId, RibbonLoadBalancerContext.class);
}
static <C> C instantiateWithConfig(Class<C> clazz, IClientConfig config) {
return instantiateWithConfig(null, clazz, config);
}
static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
Class<C> clazz, IClientConfig config) {
C result = null;
try {
Constructor<C> constructor = clazz.getConstructor(IClientConfig.class);
result = constructor.newInstance(config);
}
catch (Throwable e) {
// Ignored
}
if (result == null) {
result = BeanUtils.instantiate(clazz);
if (result instanceof IClientConfigAware) {
((IClientConfigAware) result).initWithNiwsConfig(config);
}
if (context != null) {
context.getAutowireCapableBeanFactory().autowireBean(result);
}
}
return result;
}
@Override
public <C> C getInstance(String name, Class<C> type) {
C instance = super.getInstance(name, type);
if (instance != null) {
return instance;
}
IClientConfig config = getInstance(name, IClientConfig.class);
//实例化对象
return instantiateWithConfig(getContext(name), type, config);
}
//判断缓存中是否有ApplicationContext,如果游就直接返回,
//否则创建AnnotationConfigApplicationContext,同时初始化再缓存【在创建ApplicationContext的过程中将会读取配置数据、加载默认的配置等,具体过程在父类中】
@Override
protected AnnotationConfigApplicationContext getContext(String name) {
return super.getContext(name);
}
}
由于上面getContext(String name)方法很关键,所以特将父类中的代码摘出来
protected AnnotationConfigApplicationContext getContext(String name) {
//判断缓存中是否有指定名的ApplicationContext,如果有就直接取得并返回,否则创建
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
//创建ApplicationContext并缓存
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//configurations就是RibbonAutoConfiguration配置类中收集的所有通过注解配置的客户端配置(RibbonClientSpecification)
if (this.configurations.containsKey(name)) {
//如果有针对该客户端的配置,则注册到ApplicationContext中
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
//查找默认配置项并加载到IOC中
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
//将PropertyPlaceholderAutoConfiguration以及默认配置RibbonClientConfiguration注册到IOC中
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, name)));
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
接下来看下其默认的配置类RibbonClientConfiguration
该类中在Bean创建的时候都添加了@ConditionalOnMissingBean注解,说明我们可以将其默认的Bean给替换掉
@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
// 导入客户端的配置,默认使用ApacheHttpClient创建RibbonLoadBalancingHttpClient客户端
@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {
/**
* Ribbon client default connect timeout.
*/
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
/**
* Ribbon client default read timeout.
*/
public static final int DEFAULT_READ_TIMEOUT = 1000;
/**
* Ribbon client default Gzip Payload flag.
*/
public static final boolean DEFAULT_GZIP_PAYLOAD = true;
//配置文件中的 ribbon.client.name
@RibbonClientName
private String name = "client";
/*
* 通过该对象可以读取配置文件中定义的实例对象,包含以下几个类型
* ILoadBalancer、IPing、IRule、ServerList、ServerListFilter
* 对应的添加以下配置即可替换默认的
* {ribbon.client.name}.ribbon.NFLoadBalancerClassName
* {ribbon.client.name}.ribbon.NFLoadBalancerPingClassName
* {ribbon.client.name}.ribbon.NFLoadBalancerRuleClassName
* {ribbon.client.name}.ribbon.NIWSServerListClassName
* {ribbon.client.name}.ribbon.NIWSServerListFilterClassName
*/
@Autowired
private PropertiesFactory propertiesFactory;
//用于解析ribbon客户端的配置数据
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
return config;
}
//Ribbon负载均衡策略。默认的策略能够在多区域环境下选出最佳区域的实例进行访问
@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;
}
//Ribbon实例检查策略。默认 这实际上并不会检查实例是否可用而始终返回true。
@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();
}
//服务实例清单的维护机制
@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;
}
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
}
//负载均衡器。默认的具备了区域感知的能力
@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);
}
//服务实例清单过滤机制。默认的策略能够优先过滤出与请求调用方处于同区域的服务实例
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
return this.propertiesFactory.get(ServerListFilter.class, config, name);
}
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.initWithNiwsConfig(config);
return filter;
}
@Bean
@ConditionalOnMissingBean
public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,
IClientConfig config, RetryHandler retryHandler) {
return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
}
@Bean
@ConditionalOnMissingBean
public RetryHandler retryHandler(IClientConfig config) {
return new DefaultLoadBalancerRetryHandler(config);
}
@Bean
@ConditionalOnMissingBean
public ServerIntrospector serverIntrospector() {
return new DefaultServerIntrospector();
}
//初始化方法
@PostConstruct
public void preprocess() {
setRibbonProperty(name, DeploymentContextBasedVipAddresses.key(), name);
}
static class OverrideRestClient extends RestClient {
private IClientConfig config;
private ServerIntrospector serverIntrospector;
protected OverrideRestClient(IClientConfig config,
ServerIntrospector serverIntrospector) {
super();
this.config = config;
this.serverIntrospector = serverIntrospector;
initWithNiwsConfig(this.config);
}
@Override
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = updateToSecureConnectionIfNeeded(original, this.config,
this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri);
}
@Override
protected Client apacheHttpClientSpecificInitialization() {
ApacheHttpClient4 apache = (ApacheHttpClient4) super.apacheHttpClientSpecificInitialization();
apache.getClientHandler().getHttpClient().getParams().setParameter(
ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
return apache;
}
}
}
3,RibbonLoadBalancerClient
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient 的源码如下:
public class RibbonLoadBalancerClient implements LoadBalancerClient {
private SpringClientFactory clientFactory;
public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
this.clientFactory = clientFactory;
}
@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);
URI uri;
Server server;
if (instance instanceof RibbonServer) {
RibbonServer ribbonServer = (RibbonServer) instance;
server = ribbonServer.getServer();
uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
}
else {
server = new Server(instance.getScheme(), instance.getHost(),
instance.getPort());
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
uri = updateToSecureConnectionIfNeeded(original, clientConfig,
serverIntrospector, server);
}
return context.reconstructURIWithServer(server, uri);
}
@Override
public ServiceInstance choose(String serviceId) {
return choose(serviceId, null);
}
/**
* New: Select a server using a 'key'.
* @param serviceId of the service to choose an instance for
* @param hint to specify the service instance
* @return the selected {@link ServiceInstance}
*/
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
//在拦截器中被调用的方法。通过该方法调用Ribbon的负载均衡器得到需要访问的服务实例,然后执行LoadBalancerRequest的apply方法并将最后的结果返回。
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
throws IOException {
return execute(serviceId, request, null);
}
/**
* New: Execute a request by selecting server using a 'key'. The hint will have to be
* the last parameter to not mess with the `execute(serviceId, ServiceInstance,
* request)` method. This somewhat breaks the fluent coding style when using a lambda
* to define the LoadBalancerRequest.
* @param <T> returned request execution result type
* @param serviceId id of the service to execute the request to
* @param request to be executed
* @param hint used to choose appropriate {@link Server} instance
* @return request execution result
* @throws IOException executing the request may result in an {@link IOException}
*/
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
//获取Ribbon的LoadBalancer实例对象
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
//从LoadBalancer中得到选出的服务实例
Server server = getServer(loadBalancer, hint);
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);
}
@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);
}
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
//调用LoadBalanceClient的apply方法【内部会执行】
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;
}
private ServerIntrospector serverIntrospector(String serviceId) {
ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
ServerIntrospector.class);
if (serverIntrospector == null) {
serverIntrospector = new DefaultServerIntrospector();
}
return serverIntrospector;
}
private boolean isSecure(Server server, String serviceId) {
IClientConfig config = this.clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
return RibbonUtils.isSecure(config, serverIntrospector, server);
}
// Note: This method could be removed?
protected Server getServer(String serviceId) {
return getServer(getLoadBalancer(serviceId), null);
}
protected Server getServer(ILoadBalancer loadBalancer) {
return getServer(loadBalancer, null);
}
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
// Use 'default' on a null hint, or just pass it on?
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
/**
* Ribbon-server-specific {@link ServiceInstance} implementation.
*/
public static class RibbonServer implements ServiceInstance {
private final String serviceId;
private final Server server;
private final boolean secure;
private Map<String, String> metadata;
public RibbonServer(String serviceId, Server server) {
this(serviceId, server, false, Collections.emptyMap());
}
public RibbonServer(String serviceId, Server server, boolean secure,
Map<String, String> metadata) {
this.serviceId = serviceId;
this.server = server;
this.secure = secure;
this.metadata = metadata;
}
@Override
public String getInstanceId() {
return this.server.getId();
}
@Override
public String getServiceId() {
return this.serviceId;
}
@Override
public String getHost() {
return this.server.getHost();
}
@Override
public int getPort() {
return this.server.getPort();
}
@Override
public boolean isSecure() {
return this.secure;
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map<String, String> getMetadata() {
return this.metadata;
}
public Server getServer() {
return this.server;
}
@Override
public String getScheme() {
return this.server.getScheme();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("RibbonServer{");
sb.append("serviceId='").append(serviceId).append('\'');
sb.append(", server=").append(server);
sb.append(", secure=").append(secure);
sb.append(", metadata=").append(metadata);
sb.append('}');
return sb.toString();
}
}
}
至此,Spring cloud Ribbon的解析基本告一段落。总的原理就是Spring cloud coomms已经提供了让RestTemplate进行负载均衡调用的“规范”,Spring cloud Ribbion基于该规范实现了LoadBalancerClient接口提供一个负载均衡的客户端,然后就可以使用netfix的Ribbon进行一系列的操作从而达到负载均衡的目的。 后面章节还将继续分析Ribbon与Eureka等服务发现组件配合使用的原理。