自动配置
- org.springframework.cloud.openfeign.FeignAutoConfiguration
注意@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
成立时 的时候才会使用本类的 HttpClientFeignConfiguration 或 OkHttpFeignConfiguration. 如果使用的rabbion 则org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration
起作用. - org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration
@Import({ HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class })
导入的三个配置类,会根据配置选择一个连接框架来启用 feignClient . - org.springframework.cloud.openfeign.FeignClientsConfiguration
客户端配置类 - org.springframework.cloud.openfeign.FeignClientsConfiguration.HystrixFeignConfiguration
配置 hystrix
配置属性
- org.springframework.cloud.openfeign.FeignClientProperties
客户端配置超时配置等 - org.springframework.cloud.openfeign.support.FeignHttpClientProperties
底层连接框架(okhttp/httpclient/Connection)的配置 - org.springframework.cloud.openfeign.encoding.FeignClientEncodingProperties
请求解/压缩配置
feign 如何整合ribbon的
以 HttpClientFeignLoadBalancedConfiguration
为例.okhttp类似.
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
LoadBalancerFeignClient
feign负载均衡的客户端delegate
此处使用的httpclientclientFactory
Ribbon 的客户端工厂类.负责cachingFactory
缓存 clientFactory
http调用执行流程
- 首先fegin的http请求入口在
feign.SynchronousMethodHandler#invoke
- 然后执行
feign.SynchronousMethodHandler#executeAndDecode
然后到response = client.execute(request, options);
- 就进入的
LoadBalancerFeignClient
类中.FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest( this.delegate, request, uriWithoutHost); IClientConfig requestConfig = getClientConfig(options, clientName); return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
getClientConfig
获取每个feign对应的properties和loadBalancer
从 clientFactory 获取配置IClientConfig getClientConfig(Request.Options options, String clientName) { IClientConfig requestConfig; if (options == DEFAULT_OPTIONS) { requestConfig = this.clientFactory.getClientConfig(clientName); } else { requestConfig = new FeignOptionsClientConfig(options); } return requestConfig; }
SpringClientFactory
是在org.springframework.cloud.netflix.ribbon.SpringClientFactory
这里初始化的.- 然后getInstance方法交给
org.springframework.cloud.context.named.NamedContextFactory#getInstance
方法- 子类
@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); } //父类的方法 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; } //getContext() 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); } //创建 protected AnnotationConfigApplicationContext createContext(String name) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); if (this.configurations.containsKey(name)) { for (Class<?> configuration : this.configurations.get(name) .getConfiguration()) { context.register(configuration); } } for (Map.Entry<String, C> entry : this.configurations.entrySet()) { if (entry.getKey().startsWith("default.")) { for (Class<?> configuration : entry.getValue().getConfiguration()) { context.register(configuration); } } } 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.setDisplayName(generateDisplayName(name)); context.refresh(); return context; }
在每个服务第一次请求的时候,会到抽象工厂类NamedContextFactory中获取当前服务对被调用服务配置,通过getContext方法获取,该方法会先从map中获取:
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
获取不到会执行createContext方法动态加载配置。该方法中用到了spring的注解容器:AnnotationConfigApplicationContext.该容器主要配合@Configuration和注解使用。上述代码中向该容器中注入RibbonClientConfiguration配置类,调用context.refresh()刷新容器,也就拿到了每个服务的配置.到这里也明白了,为什么feign调用,第二次调用的时间会比第一次用时略少.在我们进行feign这是ribbon的一种饥饿加载机制,ribbon也提供了修改配置:ribbon.eager-load.enabled=true ribbon.eager-load.clients=hello-service, user-service
- 子类