Spring Cloud Alibaba组件之深入分析Nacos服务调用源码实现

本文深入分析了Spring Cloud Alibaba Nacos作为注册中心时的服务调用实现。核心类NacosRibbonClientConfiguration通过NacosServerList获取服务实例列表,客户端通过定时更新和UDP网络传输保持服务信息实时性。服务端提供HTTP接口和UDP广播机制,确保服务变更的快速同步。
摘要由CSDN通过智能技术生成


前言

本文将分析基于Spring Cloud Alibaba Nacos作为注册中心时,服务调用的实现过程。在Spring Cloud架构体系下,对于不同注册中心,其服务调用的核心流程都是一样的,唯一的区分是在获取服务列表时存在差异,而对于服务列表的获取,Spring Cloud抽象出了一个规范接口,不同注册中心只需要实现该规范接口即可。因此,在看本文之前,读者可以先看下之前写的《Spring Cloud组件之深入分析Ribbon服务调用源码实现》这篇文章,本文会基于该文章基础上进行分析

核心类

回顾下《Spring Cloud组件之深入分析Ribbon服务调用源码实现》这篇文章,当通过restTemplate发起服务调用时,需要为当前调用的服务创建对应的上下文对象,并且注入两个核心配置类,而Nacos与Eureka区别在于,Nacos提供的配置类为NacosRibbonClientConfiguration,而另外一个配置类则是Ribbon提供的规范,对不同注册中心来说都是一样的

NacosRibbonClientConfiguration

对于解析NacosRibbonClientConfiguration类,其最关键是会通过@Bean方式向目标服务对应的上下文对象中注入了ServerList类型的Bean,这里的实例为NacosServerList

@Configuration(proxyBeanMethods = false)
@ConditionalOnRibbonNacos
public class NacosRibbonClientConfiguration {
   

	@Autowired
	private PropertiesFactory propertiesFactory;

	@Bean
	@ConditionalOnMissingBean
	public ServerList<?> ribbonServerList(IClientConfig config,
			NacosDiscoveryProperties nacosDiscoveryProperties) {
   
		if (this.propertiesFactory.isSet(ServerList.class, config.getClientName())) {
   
			ServerList serverList = this.propertiesFactory.get(ServerList.class, config,
					config.getClientName());
			return serverList;
		}
		//自定义的nacos服务列表
		NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);
		serverList.initWithNiwsConfig(config);
		return serverList;
	}

	@Bean
	@ConditionalOnMissingBean
	public NacosServerIntrospector nacosServerIntrospector() {
   
		return new NacosServerIntrospector();
	}

}

NacosServerList实现了ServerList接口提供的getUpdatedListOfServersgetInitialListOfServers两个规范方法,内部都调用了getServers方法,该方法就是完成了服务下客户端实例列表的获取,下面分析实现的流程:

public class NacosServerList extends AbstractServerList<NacosServer> {
   
    @Override
	public List<NacosServer> getInitialListOfServers() {
   
		return getServers();
	}

	@Override
	public List<NacosServer> getUpdatedListOfServers() {
   
	    //获取目标服务列表
		return getServers();
	}
	private List<NacosServer> getServers() {
   
		try {
   
			String group = discoveryProperties.getGroup();
			//获取目标实例列表
			List<Instance> instances = discoveryProperties.namingServiceInstance()
					.selectInstances(serviceId, group, true);
			return instancesToServerList(instances);
		}
		catch (Exception e) {
   
			throw new IllegalStateException(
					"Can not get service instances from nacos, serviceId=" + serviceId,
					e);
		}
	}
}

获取服务下实例列表

获取服务下实例列表流程:首先,尝试从当前客户端serviceInfoMap 缓存中获取当前调用的服务信息,如果获取不到,则会执行updateServiceNow方法,从远程拉取服务信息到本地。在拉取服务信息时,先通过serverProxy调用queryList方法,对服务端节点发起HTTP GET请求来拉取对应的服务信息,请求地址为:/v1/ns/instance/list,之后,将拉取到的服务信息,更新到serviceInfoMap缓存中,最后,再根据serviceInfoMap中找到对应的serviceInfo实例,并根据该实例来获取最终进行服务调用的实例列表

public class NacosNamingService implements NamingService {
   
   ....
   @Override
    public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException {
   
        ServiceInfo serviceInfo;
        if (subscribe) {
   
        	//获取请求服务对应的ServiceInfo对象
            serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
        } else {
   
            serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
        }
        //获取实例列表
        return selectInstances(serviceInfo, healthy);
    }
}
public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
   
        ...
        //尝试从serviceInfoMap获取服务信息
        ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);

        if (null == serviceObj) {
   
            serviceObj = new ServiceInfo(serviceName, clusters);
            serviceInfoMap.put(serviceObj.getKey(), serviceObj);

            updatingMap.put(serviceName, new Object());
            //向服务端拉取服务信息
            updateServiceNow(serviceName, clusters);
            updatingMap.remove(serviceName);

        } else if (updatingMap.containsKey(serviceName)) {
   

            if (UPDATE_HOLD_INTERVAL > 0) {
   
                // hold a moment waiting for update finish
                synchronized (serviceObj) {
   
                    try {
   
                        serviceObj.wait(UPDATE_HOLD_INTERVAL);
                    } catch (InterruptedException e) {
   
                        NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
                    }
                }
            }
        }
        //创建定时更新服务信息任务
        scheduleUpdateIfAbsent(serviceName, clusters);
        
        return serviceInfoMap.get(serviceObj.getKey());
    }
public void updateServiceNow(String serviceName, String clusters) {
   
        ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
        try {
   
            //发起HTTP请求查询服务信息
            String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false);

            if (StringUtils.isNotEmpty(result)) {
   
            	//将查询到服务信息更新到serviceInfoMap中
                processServiceJSON(result);
            }
        } catch (Exception e) {
   
            NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
        } finally {
   
            if (oldService != null) {
   
                synchronized (oldService) {
   
                    oldService.notifyAll();
                }
            }
        }
    }
public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)
        throws NacosException {
   

        final Map<String, String> params = new HashMap<String, String>(8);
        params.put
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值