유래
이 라인의 그레이 스케일 테스트를 기반으로 회색 Zuul 게이트웨이 라우팅을 달성하기 위해 회사 내에서, 다음과 같이 시작했다, 그래서 당신은 유레카에 메타 데이터 메타 데이터 마이크로 비즈니스 서비스를 구성 등록 및 자체 지원 규칙 정의는 리본 만에 도달 할 필요가 방문 그레이 스케일 서비스의 목적. 이 리본 아이 룰을 사용자 정의해야합니다, 그레이 스케일 요청은 좋은 규칙 개발 후 회색 라벨 메타 데이터, 사용자 정의 아이 룰과 마이크로 비즈니스 서비스에 지원 될 것입니다, 문제는 아이 룰에 규칙을 구성하는 방법입니다 리본 클라이언트 또는 글로벌 효과.
사용의 봄 클라우드 Dalston.SR5 버전
그것에서 공식 문서 사실 우리는 클라이언트를 구성하거나 기본값을 수정하는 방법에 대한 몇 가지 방법을 주어졌다, 그러나의 왜 사용을 설명하지 않았다
이 라인의 다음과 같은 분석 :
- 봄 클라우드 리본이 그것을 이해하기 시작했을 때 자동으로 구성하는 방법의 간단한 분석은 봄 콩에 장착되어있다
- 봄 클라우드 리본 클라이언트 지연로드
- 봄 클라우드 리본 클라이언트 구성이로드, 그것은 세계적인 구성 및 클라이언트 구성을 포함
- 글로벌 구성 클라이언트 구성을 사용자 지정하는 방법
- 공식 문서는 몇 가지 고려 사항을 설명
봄 클라우드 리본 자동 구성
넷플릭스의 현재 버전은 모두 자동으로 구성됩니다 spring-cloud-netflix-core-xxx.jar
자신의에 따라 META-INF/spring.factories
알려진 구성, 봄 클라우드 리본 자동 구성 클래스RibbonAutoConfiguration
RibbonAutoConfiguration
@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties(RibbonEagerLoadProperties.class)
public class RibbonAutoConfiguration {
// 所有针对某个RibbonClient指定的配置
@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
// ribbon是否懒加载的配置文件
@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;
// Spring会给每个RibbonClient创建独立的ApplicationContext上下文
// 并在其上下文中创建RibbonClient对应的Bean:如IClient、ILoadbalancer等
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
// Spring创建的带负载均衡功能的Client,会使用SpringClientFactory创建对应的Bean和配置
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
// 到Spring environment中加载针对某个Client的Ribbon的核心接口实现类
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}
// 如果不是懒加载,启动时就使用RibbonApplicationContextInitializer加载并初始化客户端配置
@Bean
@ConditionalOnProperty(value = "ribbon.eager-load.enabled", matchIfMissing = false)
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(springClientFactory(),
ribbonEagerLoadProperties.getClients());
}
......
}
위의 RibbonAutoConfiguration
콩 주로 다음과 같은 범주로 나누어 만든 :
- 리본 클라이언트 액세스 구성을위한 환경 조성
- SpringClientFactory는 : 리본 클라이언트는 각각 별도의 Spring 애플리케이션 컨텍스트의 ApplicationContext를 생성하고있는 해당 핵심 구성 및 구현 클래스 인터페이스 리본을로드합니다
- PropertiesFactory : 리본 클라이언트 인터페이스 클래스에 봄 환경 환경의 핵심 구성에서 얻고, 인스턴스화하는 데 사용
- 생성
RibbonLoadBalancerClient
하고 springClientFactory 주입, 쉽게 대응하는 구성 및 구현 클래스를 유도하는데,RibbonLoadBalancerClient
그 스프링의LoadBalancerClient
인터페이스 구현 클래스,execute()
메소드가 클라이언트로드 밸런싱을 제공하고는 - 게으른 로딩 관련
- RibbonEagerLoadProperties : 게으른 로딩 구성 항목 속성, 당신은 게으른 로딩, 게으른 로딩하고있는 클라이언트를하지 여부를 지정할 수 있습니다
- RibbonApplicationContextInitializer는 : RibbonClient는 시작 구성 (비 지연로드) 초기화에로드
당신은 컨텍스트 및 구성 정보 RibbonClient를로드하지 않은 기본 시작 프로세스를 볼 수 있지만로드 할 수있을 때만이 지연로드의 사용
봄 클라우드 RibbonClient 지연로드
사용될 때에 만로드되므로 Zuul 게이트웨이는 예를 들어, 그것의 RibbonRoutingFilter
생성 RibbonCommand, 이는 리본 부하 분산을 포함
//## RibbonRoutingFilter Zuul负责路由的Filter
public class RibbonRoutingFilter extends ZuulFilter {
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
Map<String, Object> info = this.helper.debug(context.getMethod(),
context.getUri(), context.getHeaders(), context.getParams(),
context.getRequestEntity());
// 使用ribbonCommandFactory创建RibbonCommand
RibbonCommand command = this.ribbonCommandFactory.create(context);
try {
ClientHttpResponse response = command.execute();
this.helper.appendDebug(info, response.getStatusCode().value(),
response.getHeaders());
return response;
}
catch (HystrixRuntimeException ex) {
return handleException(info, ex);
}
}
}
실행에서 RibbonRoutingFilter#run()
라우팅을 수행하는 forward()
방법을, 여기에 기인 HystrixCommand ribbonCommandFactory의 사용이 방법에 리본 클라이언트의 게으른 로딩을 RibbonCommand을 만들 수 있도록, 내부 통화의 균형을 실행 리본로드, 우리가 살펴 HttpClientRibbonCommandFactory
구현 클래스
//## org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory
public class HttpClientRibbonCommandFactory extends AbstractRibbonCommandFactory {
@Override
public HttpClientRibbonCommand create(final RibbonCommandContext context) {
ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
final String serviceId = context.getServiceId();
// 通过SpringClientFactory获取IClient接口实例
final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
serviceId, RibbonLoadBalancingHttpClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider,
clientFactory.getClientConfig(serviceId));
}
}
생성 RibbonLoadBalancingHttpClient
의 논리를 SpringClientFactory#getClient(serviceId, RibbonLoadBalancingHttpClient.class)
다음과 같이 :
- SpringClientFactory #의 getInstance (이름, clientClass)
- NamedContextFactory #의 getInstance (이름, 유형) :
- 다음 제공된 기본 구성은 통합 레지스터 클래스 RibbonClientConfiguration를 포함하는 생성 CreateContext에 (), 또는 RibbonClient @ RibbonClients @ 구성된 클래스 로직 (defaultConfiguration = XXX)를 호출 할 경우의 ApplicationContext에 대응하는, 클라이언트 얻기
- 만들 수있는 반사 아무 소용이없는 경우, ApplicationContext를 예제의 형태를 취득하고, IClientConfig에 의해 구성
- NamedContextFactory #의 getInstance (이름, 유형) :
완료 RibbonClient 기본적으로 게으른로드가 완료 때, RibbonClient가 핵심 인터페이스의 다른 구현에서 해당의 ApplicationContext를 얻기 위해 계속해서,이 클래스에 따라 구현되는 사용자 지정 기본 / 글로벌 / 클라이언트 생성 된 형상
//## org.springframework.cloud.netflix.ribbon.SpringClientFactory
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.
* @throws RuntimeException if any error occurs
*/
public <C extends IClient<?, ?>> C getClient(String name, Class<C> clientClass) {
return getInstance(name, clientClass);
}
// name代表当前Ribbon客户端,type代表要获取的实例类型,如IClient、IRule
@Override
public <C> C getInstance(String name, Class<C> type) {
// 先从父类NamedContextFactory中直接从客户端对应的ApplicationContext中获取实例
// 如果没有就根据IClientConfig中的配置找到具体的实现类,并通过反射初始化后放到Client对应的ApplicationContext中
C instance = super.getInstance(name, type);
if (instance != null) {
return instance;
}
IClientConfig config = getInstance(name, IClientConfig.class);
return instantiateWithConfig(getContext(name), type, config);
}
// 使用IClientConfig实例化
static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context,
Class<C> clazz, IClientConfig config) {
C result = null;
try {
// 通过以IClientConfig为参数的构造创建clazz类实例
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;
}
}
//## 父类 org.springframework.cloud.context.named.NamedContextFactory
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
implements DisposableBean, ApplicationContextAware {
// 维护Ribbon客户端对应的ApplicationContext上下文
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
// 维护Ribbon客户端的@Configuration配置类
private Map<String, C> configurations = new ConcurrentHashMap<>();
private ApplicationContext parent;
private Class<?> defaultConfigType; // 默认配置类为 RibbonClientConfiguration
private final String propertySourceName; // 默认为 ribbon
private final String propertyName; // 默认读取RibbonClient名的属性为ribbon.client.name
public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType;
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;
}
// 如果包含Client上下文直接返回
// 如果不包含,调用createContext(name),并放入contexts集合
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
// 创建名为name的RibbonClient的ApplicationContext上下文
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// configurations集合中是否包含当前Client相关配置类,包含即注入到ApplicationContext
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
//configurations集合中是否包含default.开头的通过@RibbonClients(defaultConfiguration=xxx)配置的默认配置类
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
// 注册PropertyPlaceholderAutoConfiguration、RibbonClientConfiguration
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
// 添加 ribbon.client.name=具体RibbonClient name的enviroment配置
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object> singletonMap(this.propertyName, name)));
// 设置父ApplicationContext,这样可以使得当前创建的子ApplicationContext可以使用父上下文中的Bean
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
}
context.refresh(); //刷新Context
return context;
}
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
type).length > 0) {
return context.getBean(type);
}
return null;
}
}
위에서의 ApplicationContext 각 RibbonClient의 생성에 중요하다 createContext(name)
@Configuration 구현 클래스가, 그 분석의 포커스를 필요에 따라되는 리본 인터페이스 구성 클래스를 생성하기 위해 상기 코어 로직을 포함하는 방법 (리본 인터페이스 코어 설명하기 위해 참조 )
그리고 createContext(name)
현재 상황에 맞는 리본 클라이언트 관련 방법 및 주입 구성 클래스를 만들 때, 기본 구성 클래스에 추가로 RibbonClientConfiguration
이러한 리본 클라이언트 구성 클래스에 대한 기본 전역 구성 클래스로 하드 코드 및 기타 구성 클래스는, 그러나 또한 방법을 구성하는 방법 그것은?
봄 클라우드 RibbonClient로드 구성은 글로벌 구성 및 클라이언트 구성을 포함
RibbonClient 대응의 ApplicationContext를 생성, 사용 가능한 모든 구성 구성 클래스를 등록
//## org.springframework.cloud.context.named.NamedContextFactory#createContext()
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 1、注册专门为RibbonClient指定的configuration配置类,@RibbonClient注解
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
// 2、将为所有RibbonClient的configuration配置类注册到ApplicationContext
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
// 3、注册defaultConfigType,即Spring的默认配置类 RibbonClientConfiguration
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.refresh(); // 刷新上下文
return context;
}
상기 로직의 ApplicationContext 구성 컨텍스트를 위해 특별히 제조 된 구성 클래스 관련 레지스터 3 리본 장소에서 본, 및 인터페이스에 따라 리본 코어 구성 클래스, 즉 구현 클래스를 만들 구성 RibbonClient의 목적을 달성 할 수있다
- 이 구성에 의한 맵 구성은 지정된 클래스 특별히 배치되고, 해당 레지스터 RibbonClient 이름 ApplicationContext의 콘텍스트에있어서되고
- 구성에서이지도 발견 기본. 처음에는 클래스 구성의 모든 RibbonClient의 기본 구성이며, 각각의 ApplicationContext의 컨텍스트에 등록
- 지정되지 않은 경우, 처음 두 데이터뿐만 아니라 등록 봄 클라우드의 기본 구성 클래스는 개인 개발자 없습니다
RibbonClientConfiguration
그래서 구성은 그것에서 구성 데이터에서이 클래스를지도? ? 분석 단계의 다음 단계
//## RibbonAutoConfiguration
@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
첫번째 RibbonAutoConfiguration 자동 구성 클래스를 생성하는 SpringClientFactory
설정 스프링 용기 내의 상기 @Autowired 구성의 세트를 RibbonClientSpecification
다음 컬렉션 RibbonClientSpecification
이 등록 할 때 수집입니까? ?
//## org.springframework.cloud.netflix.ribbon.RibbonClientConfigurationRegistrar
public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 1、@RibbonClients注解
Map<String, Object> attrs = metadata.getAnnotationAttributes(
RibbonClients.class.getName(), true);
// 1.1 value是RibbonClient[],遍历针对具体的RibbonClient配置的configuration配置类,并注册
if (attrs != null && attrs.containsKey("value")) {
AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
for (AnnotationAttributes client : clients) {
registerClientConfiguration(registry, getClientName(client),
client.get("configuration"));
}
}
// 1.2 找到@RibbonClients注解的defaultConfiguration,即默认配置
// 注册成以default.Classname.RibbonClientSpecification为名的RibbonClientSpecification
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"));
}
// 2、@RibbonClient注解
// 注册某个具体Ribbon Client的configuration配置类
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);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(name + ".RibbonClientSpecification",
builder.getBeanDefinition());
}
}
알 수있는 바와 같이, 클래스 구성 구성의 집합입니다 @RibbonClient
및 @RibbonClients
주석 구성은, 각각 특정 RibbonClient의 구성 및 기본 구성 기본
요약하면, 리본 @Configuration 관련 방법 구성 클래스는로드입니다
- 해당 AnnotationConfigApplicationContext RibbonClient를 생성 한 후, 시작에있어서
@RibbonClient
와@RibbonClients
로드 구성을 설정하는 컨텍스트 레지스터 (있는 경우), 클래스에 대응하는 현재의 설정을 찾기 위해 RibbonClient 이름 애노테이션 - 그리고 다음에 따라 설정 구성을 찾아
@RibbonClients
주석로드 기본. 시작 컨텍스트 레지스터에있는 경우, 기본 구성 클래스를 - 마지막으로 등록 된 봄 클라우드의 기본
RibbonClientConfiguration
그것은 리본 사용 관련 클래스 구성 클래스를 결정 관련 RibbonClient 논리적 맥락의 ApplicationContext 및 등록 리본 클라이언트 구성, 작성하는 방법을 말한다 IClientConfig
같은 시간 초과로 리본 클라이언트 구성 정보를로드하기 위해 클라이언트 구성과 관련된를 구성, 특히 인터페이스 구현 클래스를 만들 수있는 핵심은, 등, 봄 클라우드에서 기본적으로 등록 할 수있는 것은 RibbonClientConfiguration
더 자세히 살펴
RibbonClientConfiguration 부하와 만드는 핵심 리본 인터페이스 클래스를 구성
//## org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
@Import({OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
@Value("${ribbon.client.name}")
private String name = "client";
// TODO: maybe re-instate autowired load balancers: identified by name they could be
// associated with ribbon clients
@Autowired
private PropertiesFactory propertiesFactory;
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
return config;
}
@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;
}
위의 코드 조각 만 차단은, 리본 관련주는 IClientConfig
클라이언트 구성 및 하나의 코어 인터페이스를 IRule
클래스가 구성을로드하기 위해 생성하는 방법을 실현하고,
IClientConfig
IClientConfig
리본 클라이언트 구성 인터페이스를 먼저 참조하십시오 만들 수 있습니다 DefaultClientConfigImpl
기본 구현 클래스를 한 다음 config.loadProperties(this.name)
현재 클라이언트 관련 구성을로드
//## com.netflix.client.config.DefaultClientConfigImpl#loadProperties()
/**
* Load properties for a given client. It first loads the default values for all properties,
* and any properties already defined with Archaius ConfigurationManager.
*/
@Override
public void loadProperties(String restClientName){
enableDynamicProperties = true;
setClientName(restClientName);
// 1、使用Netflix Archaius的ConfigurationManager从Spring env中加载“ribbon.配置项”这类默认配置
// 如没加载到有默认静态配置
loadDefaultValues();
// 2、使用Netflix Archaius的ConfigurationManager从Spring env中加载“client名.ribbon.配置项”这类针对某个Client的配置信息
Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName);
for (Iterator<String> keys = props.getKeys(); keys.hasNext(); ){
String key = keys.next();
String prop = key;
try {
if (prop.startsWith(getNameSpace())){
prop = prop.substring(getNameSpace().length() + 1);
}
setPropertyInternal(prop, getStringValue(props, key));
} catch (Exception ex) {
throw new RuntimeException(String.format("Property %s is invalid", prop));
}
}
}
프로젝트에 리본 구성을 지정하지 않은 경우 위의 의견에 따르면, 그것은 사용 DefaultClientConfigImpl
봄 환경은 "리본. CI는"이 모든 클라이언트 구성을 위해로드됩니다 와서이 포함 된 경우, 기본 정적 구성을, "클라이언트가 .ribbon의 이름을 지정합니다. 구성 정보의 구성 항목 "유형을 클라이언트가로드 올 것이다위한
다음 정적 구성은 :
RibbonClient 핵심 인터페이스 구현 클래스는로드 생성하도록 구성되어
위 갖는 IClientCOnfig
구성 항목을로드하는 방법, 이는 이미 현재 핵심 클래스 RibbonClient 사용하지만, 봄 클라우드 여기에 논리 자체 구현을 정의하는 구성 인터페이스를 포함 이유에 서
@Autowired
private PropertiesFactory propertiesFactory;
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
// 查看propertiesFactory是否有关于当前接口的配置,如有就使用,并创建实例返回
if (this.propertiesFactory.isSet(IRule.class, name)) {
return this.propertiesFactory.get(IRule.class, config, name);
}
// spring cloud 默认配置
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
이제 살펴 보자 PropertiesFactory
논리
public class PropertiesFactory {
@Autowired
private Environment environment;
private Map<Class, String> classToProperty = new HashMap<>();
public PropertiesFactory() {
classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");
classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");
classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");
classToProperty.put(ServerList.class, "NIWSServerListClassName");
classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");
}
// 查看当前clazz是否在classToProperty管理的几个核心接口之一
// 如是,查看Spring environment中是否能找到 “clientName.ribbon.核心接口配置项”的配置信息
public boolean isSet(Class clazz, String name) {
return StringUtils.hasText(getClassName(clazz, name));
}
public String getClassName(Class clazz, String name) {
if (this.classToProperty.containsKey(clazz)) {
String classNameProperty = this.classToProperty.get(clazz);
String className = environment.getProperty(name + "." + NAMESPACE + "." + classNameProperty);
return className;
}
return null;
}
// 也是先调用getClassName()获取Spring enviroment中配置的核心接口实现类名
// 再使用IClientConfig配置信息创建其实例
@SuppressWarnings("unchecked")
public <C> C get(Class<C> clazz, IClientConfig config, String name) {
String className = getClassName(clazz, name);
if (StringUtils.hasText(className)) {
try {
Class<?> toInstantiate = Class.forName(className);
return (C) instantiateWithConfig(toInstantiate, config);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unknown class to load "+className+" for class " + clazz + " named " + name);
}
}
return null;
}
}
따라서, 위 만들기 위해 IRule
논리적 인터페이스 클래스를
- 첫째로 propertiesFactory 보기 봄 환경이있는 경우, 반환의 인스턴스를 만들 것이다, 현재의 아이 룰 코어 리본 클라이언트 인터페이스 구현 클래스에 대한 구성 정보를 구성 (구성 형식 : clientName.ribbon.NFLoadBalancerRuleClassName = 아이 룰 구체적인 구현 클래스)
- 그렇지 않다면, 거기에 그에서 직접 사용 넷플릭스없는
DefaultClientConfigImpl
정적 구성,하지만, 받아 기본 구현 클래스 봄 클라우드 사용자 정의IRule
규칙은 인터페이스ZoneAvoidanceRule
요약 :
첫째, ApplicationContext의 컨텍스트 RibbonClient를 생성 및 구성을 사용하는 구성 클래스를 결정
1, RibbonClients 등록 @ 전역 기본 구성 클래스
2, 클래스 클라이언트 구성의 @의 RibbonClient 등록
3, 봄 클라우드의 기본 구성 클래스 RibbonClientConfiguration
确定配置类后就是加载Client相关的IClientConfig配置信息,并创建核心接口实现类
如果没有自定义全局/客户端配置类,那么就是使用
RibbonClientConfiguration
,而其规则是对于超时等配置(除核心接口实现类以外):使用Netflix的配置逻辑,通过 ribbon.xxx 作为默认配置,以 clientName.ribbon.xxx 作为客户端定制配置
对于核心接口实现类配置:客户端定制配置仍然使用 clientName.ribbon.xxx,但默认配置是Spring Cloud在
RibbonClientConfiguration
方法中写死的默认实现类
已经知道大概的逻辑了,下面就看看具体如何自定义Client配置、全局配置
如何自定义RibbonClient配置、全局配置
这部分在Spring Cloud官方reference中有说明 16.2 Customizing the Ribbon Client
大致意思如下:
一部分配置(非核心接口实现类的配置)可以使用Netflix原生API提供的方式,即使用如
.ribbon. * 的方式配置,具体有哪些配置项,可以参考com.netflix.client.config.CommonClientConfigKey
如果想比较全面的控制RibbonClient并添加一些额外配置,可以使用
@RibbonClient
或@RibbonClients
注解,并配置一个配置类,如上的 FooConfiguration@RibbonClient(name = "foo", configuration = FooConfiguration.class) 是针对名为 foo 的RibbonClient的配置类,也可以使用@RibbonClients({@RibbonClient数组}) 的形式给某几个RibbonClient设置配置类
@RibbonClients( defaultConfiguration = { xxx.class } ) 是针对所有RIbbonClient的默认配置
官方文档说 FooConfiguration配置类 必须是@Configuration的,这样就必须注意,SpringBoot主启动类不能扫描到FooConfiguration,否则针对某个RibbonClient的配置就会变成全局的,原因是在创建每个RibbonClient时会为其创建ApplicationContext上下文,其parent就是主启动类创建的ApplicationContext,子ApplicationContext中可以使用父ApplicationContext中的Bean,且创建Bean时都使用了
@ConditionalOnMissingBean
,所以FooConfiguration如果被主启动类的上下文加载,且创建了比如IRule的实现类,在某个RIbbonClient创建其子ApplicationContext并@Bean想创建其自定义IRule实现类时,会发现parent ApplicationContext已经存在,就不会创建了,配置就失效了但在我的实验中,即使FooConfiguration不加@Configuration注解也可以加载为RibbonClient的配置,且由于没有@Configuration了,也不会被主启动类扫描到
所以主要分成2种配置:
(1)超时时间等静态配置,使用 ribbon.* 配置所有Client,使用
(2)使用哪种核心接口实现类配置,使用@RibbonClients注解做默认配置,使用@RibbonClient做针对Client的配置(注意@Configuration不要被SpringBoot主启动类扫描到的问题)