之前介绍了灰度方案以及实现,分析feign调用的时候,有点不太尽兴,这次再丰富一下。
首先,我们在feign调用时,使用了FeignClient注解。
#接口调用
@FeignClient("demo-service")
public interface DemoServiceFeginClient {
}
# 开启feign
@EnableFeignClients(basePackages = {"com.yxkong.api"})
public class ApiStarter {
}
定位下发现注解位于
FeignClient和EnableFeignClients 位于
Maven:
org.springframework.cloud
spring-cloud-openfeign-core
2.2.8.RELEASE
我们看下EnableFeignClients的源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//关键点,利用spring的动态注册bean的机制,在 Configuration配置加载分析 中已经详细解说
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
//如果有配置defaultConfiguration 则注册到ioc
registerDefaultConfiguration(metadata, registry);
//根据配置扫描的feignClient,生成bean并注册到registry
registerFeignClients(metadata, registry);
}
public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
//从EnableFeignClients查找配置的clients
final Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
//设置过滤条件FeignClient
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
//在getBasePackages将所有的路径聚合
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
//将扫描到的所有FeignClient放入candidateComponents
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
//构建FeignClientFactoryBean注入ioc中
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
//真正的是这块,构建一个FeignClientFactoryBean并注入到ioc中
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
// 注入对象的名称是配置的或者spring自动生成
factoryBean.setName(name);
factoryBean.setContextId(contextId);
//对象类型就是FeignClient标注的对象本身(如此通过@Autowird能快速获取到对应的对象)
factoryBean.setType(clazz);
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(clazz, () -> {
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean
.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class
? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class
? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(),
null));
}
//从FeignClientFactoryBean中获取构建的Feign对象
return factoryBean.getObject();
});
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
qualifiers);
//最终将Feign对象注入到IOC中
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
}
我们看到FeignClientFactoryBean.getObject()中调用了getTarget()方法
public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
ApplicationContextAware, BeanFactoryAware {
@Override
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
FeignContext context = beanFactory != null? beanFactory.getBean(FeignContext.class): applicationContext.getBean(FeignContext.class);
// 这块是关键,构建了一个Feign对象,在这里设置feign具备哪些功能
Feign.Builder builder = feign(context);
//没有配置url的时候走下面的流程
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));
}
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((RetryableFeignBlockingLoadBalancerClient) client)
.getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(type, name, url));
}
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
// 关键点
configureFeign(context, builder);
applyBuildCustomizers(context, builder);
return builder;
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
//配置Feign
configureUsingConfiguration(context, builder);
//从属性中获取配feign的属性
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()),builder);
configureUsingProperties(properties.getConfig().get(contextId), builder);
}
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
主要包含:
1,日志级别
2,Retryer
3,ErrorDecoder
4,Request.Options
5,RequestInterceptor
6,QueryMapEncoder
7,ExceptionPropagationPolicy 异常策略
}
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是HystrixTargeter
Targeter targeter = get(context, Targeter.class);
//具体看HystrixTargeter.target 方法
return targeter.target(this, builder, context, target);
}
//看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");
}
}
class HystrixTargeter implements Targeter {
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
public abstract class Feign {
//构建了Feign的默认参数
public static class Builder {
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
}
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
//invocationHandlerFactory 在Builder中构建了默认InvocationHandlerFactory.Default()
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
public class ReflectiveFeign extends Feign {
public <T> T newInstance(Target<T> target) {
// targetToHandlersByName内构建了SynchronousMethodHandler和BuildTemplateByResolvingArgs
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
// factory 就是在Feign.build传入的,是Feign.Builder初始化的
//在此create的时候ReflectiveFeign.FeignInvocationHandler(target, dispatch);
InvocationHandler handler = factory.create(target, methodToHandler);
//最后代理的handler是FeignInvocationHandler
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
return proxy;
}
static class FeignInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
}
}
}
//FeignClient 没有配置url,只有一个服务名
@FeignClient("demo-service")
public interface DemoServiceFeignClient {
@RequestMapping(value = "/demo/hello",method = {RequestMethod.GET})
ResultBean hello();
}
综上所述,在从FeignClientFactoryBean.getObject的时候,根据配置参数构建了一个Feign。
整个feign对象的生成流程如下:
这里又有两个点:
1,Client 用哪个?怎么注入的?
2,Targeter用哪个?
回过头来,继续看
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
//从spring的context中获取指定类型Client的实例
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
//从spring的context中获取指定类型Targeter的实例
//Targeter是HystrixTargeter
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
//看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");
}
其中
Defult是feign的默认实现,内部使用HttpURLConnection 执行调用;
LoadBalancerFeignClient是在org.springframework.cloud.openfeign.ribbon包下;
剩下的两个在org.springframework.cloud.openfeign.loadbalancer 包下。
这种注入性的配置在springboot里一般都在configuration中。
看下spring.factorieswe文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
AutoConfiguration 是有顺序的。
在FeignRibbonClientAutoConfiguration中,
//第一个就是HttpClientFeignLoadBalancedConfiguration
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
HttpClient5FeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
//没引入这个类,不执行
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Conditional(HttpClient5DisabledConditions.class)
//这里又导入了HttpClient
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancedConfiguration {
}
@Configuration(proxyBeanMethods = false)
//这个也没引入,不执行
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
@Import(OkHttpFeignConfiguration.class)
class OkHttpFeignLoadBalancedConfiguration {
}
@Configuration(proxyBeanMethods = false)
//这个没引入,不执行
@ConditionalOnClass(ApacheHttp5Client.class)
@ConditionalOnProperty(value = "feign.httpclient.hc5.enabled", havingValue = "true")
@Import(HttpClient5FeignConfiguration.class)
class HttpClient5FeignLoadBalancedConfiguration {
}
再看最后一个
@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}
}
到这,默认的Client 的注入有了是LoadBalancerFeignClient。
我们再看
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class, FeignEncoderProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultFeignTargeterConditions.class)
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(FeignCircuitBreakerDisabledConditions.class)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
//优先使用这个
@ConditionalOnProperty(value = "feign.hystrix.enabled", havingValue = "true",
matchIfMissing = true)
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CircuitBreaker.class)
@ConditionalOnProperty(value = "feign.circuitbreaker.enabled", havingValue = "true")
protected static class CircuitBreakerPresentFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(CircuitBreakerFactory.class)
public Targeter circuitBreakerFeignTargeter(
CircuitBreakerFactory circuitBreakerFactory) {
return new FeignCircuitBreakerTargeter(circuitBreakerFactory);
}
}
}
到这,默认的Targeter 是 HystrixTargeter。
我们再看
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);
//最后返回的是targeter.target
return targeter.target(this, builder, context, target);
}
//看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");
}
//具体实现
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
//最终走的是feign.target()
return feign.target(target);
}
}
public abstract class Feign {
public <T> T target(Target<T> target) {
//build()构建了一个ReflectiveFeign,然后执行newInstance
return build().newInstance(target);
}
public Feign build() {
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
public class ReflectiveFeign extends Feign {
public <T> T newInstance(Target<T> target) {
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;
}
}
到此feign初始化的流程分析完了。
然后我们断点验证下:
确实如分析所说。
Feign生成流程分析完毕。
接下来我们分析下Feign使用流程
@Autowired
private DemoServiceFeignClient demoServiceFeginClient;
@GetMapping("hello")
public ResultBean hello(){
String msg = "api进入的版本号是:"+version;
log.info(msg);
return demoServiceFeginClient.hello();
}
我们知道Autowired是通过类型查找的,所以这块一定会找到一个DemoServiceFeignClient的Proxy对象,Proxy的handler是FeignInvocationHandler
我们梳理下我们从上面得到的信息:
Feign使用的是: ReflectiveFeign
Proxy的源也是: ReflectiveFeign
Proxy的handler是: ReflectiveFeign.FeignInvocationHandler
handler 持有的dispatch是:SynchronousMethodHandler
target是:HardCodedTarget
client是:LoadBalancerFeignClient
我们看下handler的源码
static class FeignInvocationHandler implements InvocationHandler {
//要执行的对象
private final Target target;
//方法和SynchronousMethodHandler的映射集合
private final Map<Method, MethodHandler> dispatch;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//具体执行的的是dispatch的invoke
return dispatch.get(method).invoke(args);
}
}
final class SynchronousMethodHandler implements MethodHandler {
public Object invoke(Object[] argv) throws Throwable {
//构建一个RequestTemplate由ReflectiveFeign.BuildEncodedTemplateFromArgs,主要是解析
RequestTemplate template = buildTemplateFromArgs.create(argv);
//超时时间,链接时间配置获取,没有就走默认
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
//关键在这里
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
}
continue;
}
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//执行所有RequestInterceptor并构建一个Request对象
Request request = targetRequest(template);
Response response;
long start = System.nanoTime();
try {
//client是LoadBalancerFeignClient,我们看下源码
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(),elapsedTime);
return resultFuture.join();
}
Request targetRequest(RequestTemplate template) {
//遍历所有的拦截器并执行
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
//最终在return Request.create(this.method, this.url(), this.headers(), this.body, this);
return target.apply(template);
}
}
public final class RequestTemplate implements Serializable {
public Request request() {
if (!this.resolved) {
throw new IllegalStateException("template has not been resolved.");
}
return Request.create(this.method, this.url(), this.headers(), this.body, this);
}
}
public class LoadBalancerFeignClient implements Client {
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
//构建RibbonRequest
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
// 获取配置信息
IClientConfig requestConfig = getClientConfig(options, clientName);
//找到
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
}
private FeignLoadBalancer lbClient(String clientName) {
return this.lbClientFactory.create(clientName);
}
}
public class CachingSpringLoadBalancerFactory {
public FeignLoadBalancer create(String clientName) {
// 缓存有直接返回
FeignLoadBalancer client = this.cache.get(clientName);
if (client != null) {
return client;
}
IClientConfig config = this.factory.getClientConfig(clientName);
//获取ILoadBalancer实例
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
ServerIntrospector.class);
//构建一个FeignLoadBalancer
client = this.loadBalancedRetryFactory != null
? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
this.loadBalancedRetryFactory)
: new FeignLoadBalancer(lb, config, serverIntrospector);
//缓存起来
this.cache.put(clientName, client);
return client;
}
}
FeignLoadBalancer 继承了AbstractLoadBalancerAwareClient,最终executeWithLoadBalancer
public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
try {
return command.submit(
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
// 这个时候从注册中心里获取到了真实的调用url
URI finalUri = reconstructURIWithServer(server, request.getUri());
S requestForServer = (S) request.replaceUri(finalUri);
try {
//执行调用
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
Throwable t = e.getCause();
if (t instanceof ClientException) {
throw (ClientException) t;
} else {
throw new ClientException(e);
}
}
}
}
看下具体的执行流程
至于怎么去做负载,后期再分析。
如果觉得对你有帮助,请关注公众号:5ycode,后续会不断更新哦