nacos源码解析==SPI和spring.factories机制-服务注册-心跳发送-服务拉取-服务调用

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)

端口与主端口的偏移量描述
98481000客户端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~

================================================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值