Spring.Factories这种机制实际上是仿照java中的SPI扩展机制实现的
springboot核心基础之spring.factories机制 - 知乎
SpringBoot1==IDEA编写一个自己的starter_一个java开发的博客-CSDN博客_idea创建spring starter
======================================
spring-cloud-starter-alibaba-nacos-discovery
将要注册到nacos中的服务使用的配置文件bootstrap.yaml
bootstrap.yaml中设置的配置项,会被扫描成对应的properties对象。
spring.factories中配置的类会被项目自动扫描注入。
com.alibaba.cloud.nacos.NacosServiceAutoConfiguration
@Configuration(proxyBeanMethods = false) @ConditionalOnDiscoveryEnabled @ConditionalOnNacosDiscoveryEnabled public class NacosServiceAutoConfiguration { @Bean public NacosServiceManager nacosServiceManager() { return new NacosServiceManager(); } }
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration
在这个类中,根据spring cloud提供的三个类和接口,分别进行了实现和继承。
org.springframework.cloud.client.serviceregistry.ServiceRegistry
org.springframework.cloud.client.serviceregistry.Registration
org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class,
NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosServiceManager, nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers,
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(registrationCustomizers.getIfAvailable(),
nacosDiscoveryProperties, context);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}
@ConfigurationProperties("spring.cloud.service-registry.auto-registration") public class AutoServiceRegistrationProperties {
@ConfigurationProperties("spring.cloud.nacos.discovery") public class NacosDiscoveryProperties {
public class NacosRegistration implements Registration, ServiceInstance {
上面的代码已经将NacosAutoServiceRegistration注入了容器,其中有个register方法。
com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration
NacosAutoServiceRegistration的父类AbstractAutoServiceRegistration实现了ApplicationListener接口,重写了onApplicationEvent方法,用该方法监听了WebServerInitializedEvent,这个事件有个实现类叫做ServletWebServerInitializedEvent,springboot中的tomcat在启动后就会发布一个ServletWebServerInitializedEvent,被onApplicationEvent监听到后会执行方法org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration#bind,
org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration#start
com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register
NacosServiceRegistry实现了接口ServiceRegistry,重写了接口的方法,同时自己定义了这些方法需要用到的一些成员变量NacosDiscoveryProperties(读取配置文件生成)和NacosServiceManager(管理NamingService)。register方法就是根据配置文件生成一个instance,再将instance交给namingService.registerInstance(serviceId, group, instance) ,
正式开始服务注册NacosNamingService
com.alibaba.nacos.client.naming.NacosNamingService#registerInstance(java.lang.String, java.lang.String, com.alibaba.nacos.api.naming.pojo.Instance)
先看下NacosNamingService是怎么创建出来的:
com.alibaba.cloud.nacos.registry.NacosServiceRegistry#register
com.alibaba.cloud.nacos.registry.NacosServiceRegistry#namingService
com.alibaba.cloud.nacos.NacosServiceManager#getNamingService()
com.alibaba.cloud.nacos.NacosServiceManager#buildNamingService
com.alibaba.nacos.api.NacosFactory#createNamingService(java.util.Properties)
com.alibaba.nacos.api.naming.NamingFactory#createNamingService(java.util.Properties)
public static NamingService createNamingService(Properties properties) throws NacosException {
try {
Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
Constructor constructor = driverImplClass.getConstructor(Properties.class);
return (NamingService) constructor.newInstance(properties);
} catch (Throwable e) {
throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
}
}
com.alibaba.nacos.client.naming.NacosNamingService#NacosNamingService(java.util.Properties)
创建NacosNamingService完成后会立即执行init方法,
private void init(Properties properties) throws NacosException {
ValidatorUtils.checkInitParam(properties);
this.namespace = InitUtils.initNamespaceForNaming(properties);
InitUtils.initSerialization();
InitUtils.initWebRootContext(properties);
initLogName(properties);
this.changeNotifier = new InstancesChangeNotifier();
NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
NotifyCenter.registerSubscriber(changeNotifier);
this.serviceInfoHolder = new ServiceInfoHolder(namespace, properties);
//初始化
this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, properties, changeNotifier);
}
public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, Properties properties,
InstancesChangeNotifier changeNotifier) throws NacosException {
//初始化服务列表拉取的定时任务
this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this,
changeNotifier);
this.serverListManager = new ServerListManager(properties, namespace);
this.serviceInfoHolder = serviceInfoHolder;
this.securityProxy = new SecurityProxy(properties, NamingHttpClientManager.getInstance().getNacosRestTemplate());
initSecurityProxy();
//默认是瞬时对象,如果是瞬时对象,走这里会去初始化心跳管理的定时任务,用的GRPC
this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties,
serviceInfoHolder);
//如果不是瞬时对象,走这里会去初始化心跳管理的定时任务
this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties,
serviceInfoHolder);
}
会创建一个服务列表拉取更新的对象(去nacos服务端拉取可用的服务列表),涉及一个ScheduledThreadPoolExecutor线程池的创建,核心线程数为CPU核数除以2 。老版本的这里应该是要创建一个HostReactor对象,新版变了。
public ServiceInfoUpdateService(Properties properties, ServiceInfoHolder serviceInfoHolder,
NamingClientProxy namingClientProxy, InstancesChangeNotifier changeNotifier) {
//定时拉取服务列表的线程池
this.executor = new ScheduledThreadPoolExecutor(initPollingThreadCount(properties),
new NameThreadFactory("com.alibaba.nacos.client.naming.updater"));
this.serviceInfoHolder = serviceInfoHolder;
this.namingClientProxy = namingClientProxy;
this.changeNotifier = changeNotifier;
}
这个线程池什么时候使用的呢?
是在spring.factories中配置的下一个注入的对象中使用
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnBlockingDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class })
@AutoConfigureAfter(NacosDiscoveryAutoConfiguration.class)
public class NacosDiscoveryClientConfiguration {
@Bean
public DiscoveryClient nacosDiscoveryClient(
NacosServiceDiscovery nacosServiceDiscovery) {
return new NacosDiscoveryClient(nacosServiceDiscovery);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled", matchIfMissing = true)
//NacosWatch 这里会去nacos拉取服务列表
public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties);
}
}
因为NacosWatch实现了SmartLifecycle接口,所以在启动完Tomcat后会调用NacosWatch的start()方法。
com.alibaba.cloud.nacos.discovery.NacosWatch#start
com.alibaba.nacos.api.naming.NamingService#subscribe(java.lang.String, java.lang.String, java.util.List<java.lang.String>, com.alibaba.nacos.api.naming.listener.EventListener)
com.alibaba.nacos.client.naming.NacosNamingService#subscribe(java.lang.String, java.lang.String, java.util.List<java.lang.String>, com.alibaba.nacos.api.naming.listener.EventListener)
@Override
public void subscribe(String serviceName, String groupName, List<String> clusters, EventListener listener)
throws NacosException {
if (null == listener) {
return;
}
String clusterString = StringUtils.join(clusters, ",");
//监听?监听啥
changeNotifier.registerListener(groupName, serviceName, clusterString, listener);
//定时拉取
clientProxy.subscribe(serviceName, groupName, clusterString);
}
com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate#subscribe
com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService#scheduleUpdateIfAbsent
com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService#addTask
executor.schedule(task, DEFAULT_DELAY, TimeUnit.MILLISECONDS);
执行的线程对象为com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService.UpdateTask.run
默认是瞬时对象,所以通过GRPC调用nacos的服务端,如果不是就走http的。老版本没有引入GRPC,只有HTTP.
com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#queryInstancesOfService
在run方法的finally里可以为下次拉取设置时间,60秒拉取一次
} finally {
if (!isCancel) {
executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60),
TimeUnit.MILLISECONDS);
}
}
会创建心跳管理对象,涉及一个ScheduledThreadPoolExecutor线程池的创建,核心线程数为CPU核数除以2 。
com.alibaba.nacos.client.naming.NacosNamingService#init
public NamingHttpClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListManager serverListManager,
Properties properties, ServiceInfoHolder serviceInfoHolder) {
super(securityProxy, properties);
this.serverListManager = serverListManager;
this.setServerPort(DEFAULT_SERVER_PORT);
this.namespaceId = namespaceId;
//心跳管理
this.beatReactor = new BeatReactor(this, properties);
this.pushReceiver = new PushReceiver(serviceInfoHolder);
this.maxRetry = ConvertUtils.toInt(properties.getProperty(PropertyKeyConst.NAMING_REQUEST_DOMAIN_RETRY_COUNT,
String.valueOf(UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT)));
}
public BeatReactor(NamingHttpClientProxy serverProxy, Properties properties) {
this.serverProxy = serverProxy;
int threadCount = initClientBeatThreadCount(properties);
//线程池管理心跳
this.executorService = new ScheduledThreadPoolExecutor(threadCount, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.setName("com.alibaba.nacos.naming.beat.sender");
return thread;
}
});
}
public static final int DEFAULT_CLIENT_BEAT_THREAD_COUNT =
ThreadUtils.getSuitableThreadCount(1) > 1 ? ThreadUtils.getSuitableThreadCount(1) / 2 : 1;
再回到服务注册方法
com.alibaba.nacos.client.naming.NacosNamingService#registerInstance(java.lang.String, java.lang.String, com.alibaba.nacos.api.naming.pojo.Instance)
默认是瞬时对象是GRPC调用,
com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#registerService
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
instance);
redoService.cacheInstanceForRedo(serviceName, groupName, instance);
doRegisterService(serviceName, groupName, instance);
}
/**
* Execute register operation.
*
* @param serviceName name of service
* @param groupName group of service
* @param instance instance to register
* @throws NacosException nacos exception
*/
public void doRegisterService(String serviceName, String groupName, Instance instance) throws NacosException {
InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
NamingRemoteConstants.REGISTER_INSTANCE, instance);
//请求服务器进行注册
requestToServer(request, Response.class);
redoService.instanceRegistered(serviceName, groupName);
}
可以看到request对象中包含了该服务需要存到nacos服务端的信息,IP地址、端口之类的
可以看到访问的是服务端的9848端口,转门提供给GRPC用的
com.alibaba.nacos.common.remote.client.RpcClient#request(com.alibaba.nacos.api.remote.request.Request, long)
端口 | 与主端口的偏移量 | 描述 |
9848 | 1000 | 客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求 |
9849 | 1001 | 服务端gRPC请求服务端端口,用于服务间同步等 |
在这个request方法里打上断点,可以发现在启动过程中,先发了4个请求去服务端获取该服务的配置文件: application-dev.yml ruoyi-file ruoyi-file.yml ruoyi-file-dev.yml
实际能读到两个配置文件
如果不是瞬时对象的注册
com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy#registerService
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName,
instance);
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
if (instance.isEphemeral()) {
//心跳信息构建,默认值是15S一次检测
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
//将心跳检测任务BeatTask对象放入线程池
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
final Map<String, String> params = new HashMap<String, String>(32);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, groupedServiceName);
params.put(CommonParams.GROUP_NAME, groupName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put(IP_PARAM, instance.getIp());
params.put(PORT_PARAM, String.valueOf(instance.getPort()));
params.put(WEIGHT_PARAM, String.valueOf(instance.getWeight()));
params.put("enable", String.valueOf(instance.isEnabled()));
params.put(HEALTHY_PARAM, String.valueOf(instance.isHealthy()));
params.put(EPHEMERAL_PARAM, String.valueOf(instance.isEphemeral()));
params.put(META_PARAM, JacksonUtils.toJson(instance.getMetadata()));
//将服务信息注册到nacos服务器 nacosUrlInstance=/nacos/v1/ns/instance
reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
}
//15S一次心跳检测
public static final long DEFAULT_HEART_BEAT_TIMEOUT = TimeUnit.SECONDS.toMillis(15);
发出心跳请求com.alibaba.nacos.client.naming.beat.BeatReactor.BeatTask#run
JsonNode result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
===========================================
发送心跳请求
JsonNode result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
@Override
public void run() {
if (beatInfo.isStopped()) {
return;
}
long nextTime = beatInfo.getPeriod();
try {
//发送心跳
JsonNode result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
//服务端返回的心跳间隔时间,下次心跳以这个间隔为准
long interval = result.get(CLIENT_BEAT_INTERVAL_FIELD).asLong();
boolean lightBeatEnabled = false;
if (result.has(CommonParams.LIGHT_BEAT_ENABLED)) {
lightBeatEnabled = result.get(CommonParams.LIGHT_BEAT_ENABLED).asBoolean();
}
BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
if (interval > 0) {
nextTime = interval;
}
int code = NamingResponseCode.OK;
if (result.has(CommonParams.CODE)) {
code = result.get(CommonParams.CODE).asInt();
}
if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
Instance instance = new Instance();
instance.setPort(beatInfo.getPort());
instance.setIp(beatInfo.getIp());
instance.setWeight(beatInfo.getWeight());
instance.setMetadata(beatInfo.getMetadata());
instance.setClusterName(beatInfo.getCluster());
instance.setServiceName(beatInfo.getServiceName());
instance.setInstanceId(instance.getInstanceId());
instance.setEphemeral(true);
try {
serverProxy.registerService(beatInfo.getServiceName(),
NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
} catch (Exception ignore) {
}
}
} catch (NacosException ex) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
JacksonUtils.toJson(beatInfo), ex.getErrCode(), ex.getErrMsg());
} catch (Exception unknownEx) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, unknown exception msg: {}",
JacksonUtils.toJson(beatInfo), unknownEx.getMessage(), unknownEx);
} finally {
//每次都更新间隔时间
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
}
==================================================
服务管理,去服务端拉取最新的服务列表
com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService#scheduleUpdateIfAbsent
com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService#addTask
executor.schedule(task, DEFAULT_DELAY, TimeUnit.MILLISECONDS);
执行的线程对象为com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService.UpdateTask.run
@Override
public void run() {
//每次更新下次拉取的时间,默认1秒一次
long delayTime = DEFAULT_DELAY;
try {
if (!changeNotifier.isSubscribed(groupName, serviceName, clusters) && !futureMap.containsKey(
serviceKey)) {
NAMING_LOGGER.info("update task is stopped, service:{}, clusters:{}", groupedServiceName, clusters);
isCancel = true;
return;
}
ServiceInfo serviceObj = serviceInfoHolder.getServiceInfoMap().get(serviceKey);
if (serviceObj == null) {
serviceObj = namingClientProxy.queryInstancesOfService(serviceName, groupName, clusters, 0, false);
serviceInfoHolder.processServiceInfo(serviceObj);
lastRefTime = serviceObj.getLastRefTime();
return;
}
if (serviceObj.getLastRefTime() <= lastRefTime) {
serviceObj = namingClientProxy.queryInstancesOfService(serviceName, groupName, clusters, 0, false);
serviceInfoHolder.processServiceInfo(serviceObj);
}
lastRefTime = serviceObj.getLastRefTime();
if (CollectionUtils.isEmpty(serviceObj.getHosts())) {
incFailCount();
return;
}
// TODO multiple time can be configured.
delayTime = serviceObj.getCacheMillis() * DEFAULT_UPDATE_CACHE_TIME_MULTIPLE;
resetFailCount();
} catch (Throwable e) {
incFailCount();
NAMING_LOGGER.warn("[NA] failed to update serviceName: {}", groupedServiceName, e);
} finally {
if (!isCancel) {
//每次更新下次拉取的时间,默认1秒一次
executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60),
TimeUnit.MILLISECONDS);
}
}
}
FailOver
开启的话定时备份服务信息
===================================================
服务调用A调用B
ruoyi cloud用的是openfeign+okhttp,
被调用者没特别的,就是调用者使用了openfeign+okhttp,
A启动的时候因为启动类上的@EnableFeignClients,该注解import了FeignClientsRegistrar,FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,这个接口是spring提供的扩展接口,重写接口的registerBeanDefinitions方法,可以往IOC容器中放入bean。
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
//去扫描带有FeignClient注解的接口
registerFeignClients(metadata, registry);
}
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
比如这里扫描到了接口com.ruoyi.system.api.RemoteFileService, 生成JDK动态代理对象HardCodedTarget(type=RemoteFileService, name=ruoyi-file, url=http://ruoyi-file)
org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget
org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance
org.springframework.cloud.openfeign.DefaultTargeter#target
feign.Feign.Builder#target(feign.Target<T>)
feign.ReflectiveFeign#newInstance 可以看到给使用FeignClient标注的接口生成了对应的代理对象,接口中的每个方法都有对应的代理对象处理,代理对象的类为SynchronousMethodHandler。
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 factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
factoryBean.setRefreshableClient(isClientRefreshEnabled());
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));
}
//当bean被加载的时候会使用这里的加载方法,生成代理对象
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
registerOptionsBeanDefinition(registry, contextId);
}
接口中每个方法对应一个代理对象。
后面一旦通过这个接口实现调用,就用进入到代理对象SynchronousMethodHandler的invoke方法:
@Override
public Object invoke(Object[] argv) throws Throwable {
//生成请求的模板
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) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//发送前执行下切面,可以实现自定义AOP切面,在这一步会被执行,比如加上一些header
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
//发送请求
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) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (decoder != null) {
return responseInterceptor
.aroundDecode(new InvocationContext(decoder, metadata.returnType(), response));
}
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");
return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient#execute
在这利用负载均衡选择一个本次请求使用的被调用者的IP和端口等生成newRequest 。这里和ribbon是怎么结合的呢?
默认是RoundRobinLoadBalancer
还有个nacosLoadBalancer 但是貌似不起作用,过程应该是:
要回到spring.factories,
其中的com.alibaba.cloud.nacos.loadbalancer.LoadBalancerNacosAutoConfiguration注入了NacosLoadBalancerClientConfiguration 。
其中又注入了nacosLoadBalancer。
com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancerClientConfiguration#nacosLoadBalancer 。
org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient#execute
org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient#choose(java.lang.String, org.springframework.cloud.client.loadbalancer.Request<T>)
org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer#choose(org.springframework.cloud.client.loadbalancer.Request)
com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer#choose
org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils#executeWithLoadBalancerLifecycleProcessing
feign.Client.Default#execute 默认使用这个,但是可以换成okhttp或者apache httpClient
feign.Client.Default#convertAndSend
发送成功请求到达被调用方的controller~
================================================