例子這次先不寫了。。。直接原始碼走起。部分設計跟Ribbon一樣,這邊就不在累述,建議先看Ribbon系列。
依然從spring.factories說起。注意到這裡有這幾個類:FeignAutoConfiguration、FeignRibbonClientAutoConfiguration。
啟動
FeignAutoConfiguration
載入FeignContext,這裡會賦值FeignClientSpecification的集合,
後面說FeignClientSpecification類怎麼來的,其實跟Ribbon用的是同一個方法。
FeignContext的defaultConfigType是FeignClientsConfiguration.class,這個和Ribboon用法一樣,後面會載入。
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
另一個載入的是HystrixTargeter,雖然是HystrixTargeter,如果Feign.Builder不是feign.hystrix.HystrixFeign.Builder,後面呼叫的還是Feign.Builder的target方法。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
FeignRibbonClientAutoConfiguration
載入CachingSpringLoadBalancerFactory,這個類會注入SpringClientFactory,看過了Ribbon,是不是就知道了他要做什麼。沒錯,他會建立一個ILoadBalancer,用於負載均衡。
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
SpringClientFactory factory) {
return new CachingSpringLoadBalancerFactory(factory);
}
FeignRibbonClientAutoConfiguration還import了DefaultFeignLoadBalancedConfiguration類,在這個類,會建立一個LoadBalancerFeignClient,後面需要的Client就是他了。
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}
@EnableFeignClients
我們使用feign的時候,都會使用這個註解,註解裡Import了FeignClientsRegistrar這個類,FeignClientsRegistrar實現了ImportBeanDefinitionRegistrar介面,呼叫registerBeanDefinitions方法。這個方法做了兩件事,一件事是註冊FeignClientSpecification類,後面會注入到上面的FeignContext中,這個用法跟Ribbon - 幾種自定義負載均衡策略提的一樣。另外一件事就是建立FactoryBean。
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 註冊FeignClientSpecification類
registerDefaultConfiguration(metadata, registry);
// 透過註解的資訊註冊FeignClientFactoryBean類,
//這個是FactoryBean,所以容器fresh的時候會呼叫他的getObject方法
registerFeignClients(metadata, registry);
}
這個方法就會掃描EnableFeignClients和FeignClient的註解,然後註冊FeignClientFactoryBean型別的bean,這個是FactoryBean,所以容器fresh的時候會呼叫他的getObject方法。
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// 掃描EnableFeignClients註解
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
// 如果EnableFeignClients沒有配置clients資訊,掃描FeignClient註解
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
// 掃描FeignClient註解
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
// 如果配置clients資訊,就忽略FeignClient註解,直接讀取clients資訊
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 註冊FeignClientFactoryBean
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
FeignClientFactoryBean#getObject
getObject會呼叫getTarget方法,他會建立一個Feign.Builder,建立的時候會傳FeignContext,這個FeignContext的載入在上面已經提過了。然後呼叫loadBalance方法返回代理物件,這個代理就是後面遠端呼叫用的。
<T> T getTarget() {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(type, name, url));
}
// 直接ip的形式,程式碼略
}
FeignClientFactoryBean#feign
建立一個Feign.Builder,FeignContext#getInstance,這個類繼承了NamedContextFactory,流程和ribbon的SpringClientFactory#getContext一樣。上面提到FeignContext的defaultConfigType是FeignClientsConfiguration.class,所以還會載入OptionalDecoder解碼、SpringEncoder編碼、SpringMvcContract,解析MVC註解、NEVER_RETRY重試、DefaultFeignLoggerFactory日誌、FeignClientConfigurer、Feign.builder()。
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
// 整合配置Feign.Builder
configureFeign(context, builder);
return builder;
}
FeignClientFactoryBean#loadBalance
這裡獲取Client和Targeter類,Client就是上面的LoadBalancerFeignClient,他是所有共享的。Targeter是HystrixTargeter,他是serviceId私有的。上面已經提了他最終是呼叫Feign.Builder#target。
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
// 異常略
}
Feign.Builder#target
build方法是構建一個ReflectiveFeign物件,newInstance是生成代理物件。
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
build方法中,ReflectiveFeign建立的時候傳了ParseHandlersByName,InvocationHandlerFactory,SynchronousMethodHandler三個物件。
public Feign build() {
// 其他略
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
代理物件生成,生成後,後面對代理物件的訪問,都會呼叫FeignInvocationHandler#invoke方法。
public <T> T newInstance(Target<T> target) {
// ParseHandlersByName是透過Contrac用來解析介面定義。
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
// 給每個方法建立一個SynchronousMethodHandler物件
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 建立一個InvocationHandler物件,這裡是FeignInvocationHandler
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
總結
啟動的時候,為每個介面,生成代理物件。
方法呼叫
FeignInvocationHandler#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 其他略
//dispatch.get(method)獲取的是SynchronousMethodHandler
return dispatch.get(method).invoke(args);
}
SynchronousMethodHandler#invoke
呼叫LoadBalancerFeignClient#execute,LoadBalancerFeignClient載入上面已經講過了。
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
// 其他略
return executeAndDecode(template, options);
// 其他略
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 其他略
response = client.execute(request, options);
// 其他略
}
LoadBalancerFeignClient#execute
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
// 獲取Uri
URI asUri = URI.create(request.url());
// 獲取serviceId
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
// 獲取FeignLoadBalancer,透過FeignLoadBalancer呼叫請求
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
lbClientFactory是CachingSpringLoadBalancerFactory,所以呼叫CachingSpringLoadBalancerFactory#create的create方法。
CachingSpringLoadBalancerFactory有個熟悉的SpringClientFactory物件,他負責獲取ILoadBalancer、IClientConfig、ServerIntrospector,然後透過這三個構建一個FeignLoadBalancer物件。既然得到了ILoadBalancer,那後續負載均衡的部分就不再繼續了,參考Ribbon - 負載均衡流程。
private FeignLoadBalancer lbClient(String clientName) {
return this.lbClientFactory.create(clientName);
}
public FeignLoadBalancer create(String clientName) {
FeignLoadBalancer client = this.cache.get(clientName);
if (client != null) {
return client;
}
IClientConfig config = this.factory.getClientConfig(clientName);
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
ServerIntrospector.class);
client = this.loadBalancedRetryFactory != null
? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
this.loadBalancedRetryFactory)
: new FeignLoadBalancer(lb, config, serverIntrospector);
this.cache.put(clientName, client);
return client;
}
總結
主要是透過代理物件,然後呼叫Ribbon進行負載均衡。我們這邊只講述了主流程,怎麼構建HTTP請求,怎麼處理返回結果,都略過了。。。。。