0201服务注册源码解析-nacos2.x-微服务架构

1 搭建测试服务

1.1 搭建服务

我们基于springboot来搭建简单的订单和库存服务,通过订单服务调用库存服务,项目代码和配置在文章最后的仓库有,项目文件结构如下图1-1所示:

在这里插入图片描述

1.2 启动测试

  • 第一步:启动nacos服务端,默认服务列表,服务管理->服务列表默认少没有服务端;
  • 第二步:启动订单服务和库存服务,nacos服务列表出现相应的服务;
  • 第三步:测试API,我这里用的postman测试

nacos服务列表如下图2.1-1所示:

在这里插入图片描述

postman api接口测试如下图2.1-2所示:

在这里插入图片描述

idea 库存服务控制台输出如下2.1-3所示:

在这里插入图片描述

我们的测试系统搭建和运行测试通过,下面我们通过nacos官网文档和追踪nacos源码,来分析和理解nacos的服务注册和发现原理。

2 nacos 服务注册原理

2.1 客户端注册

我们使用nacos客户端一般引入如下依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

即然nacos是基于springboot来完成自动装配,那我们查看spring-cloud-alibaba-nacos-discovery-2.1.0.RELEASE.jar包下/META-INF\spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.alibaba.cloud.nacos.NacosDiscoveryAutoConfiguration,\
  com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,\
  com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientAutoConfiguration,\
  com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
  com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration

NacosServiceRegistryAutoConfiguration 是咱们服务注册的核心配置类,该类中定义了三个核心的 Bean 对象:

  • NacosServiceRegistry:nacos服务注册,服务注册的实现类
  • NacosRegistration:nacos注册类,获取设置nacos注册属性
  • NacosAutoServiceRegistration:nacos自动服务注册

NacosAutoServiceRegistration类实现服务向nacos自动注册的功能,它的继承关系如下图2.1-1所示:

在这里插入图片描述

当容器启动且上下文准备完毕后, 会调用实现ApplicationListener接口的onApplicationEvent(),跳过中间方法bind()->start()->register()

	protected void register() {
		this.serviceRegistry.register(getRegistration());
	}

继续向下调用实现了ServiceRegistry的NacosServiceRegister(上面配置类其中之一Bean)的register()方法

	@Override
	public void register(Registration registration) {

		// 省略校验。。。
		String serviceId = registration.getServiceId();

		Instance instance = getNacosInstanceFromRegistration(registration);

		try {
			namingService.registerInstance(serviceId, instance);
			// 省略日志和异常处理
	}

继续向下调用实现NacosService接口的NacosNamingService类的registerInstance()方法

    public void registerInstance(String serviceName, Instance instance) throws NacosException {
        registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
    }

    @Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {

   // 省略。。。

        serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
    }

继续调用NamingProxy类的registerService()方法

    public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {

        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
            namespaceId, serviceName, instance);

        final Map<String, String> params = new HashMap<String, String>(9);
        params.put(CommonParams.NAMESPACE_ID, namespaceId);
        params.put(CommonParams.SERVICE_NAME, serviceName);
        params.put(CommonParams.GROUP_NAME, groupName);
        params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
        params.put("ip", instance.getIp());
        params.put("port", String.valueOf(instance.getPort()));
        params.put("weight", String.valueOf(instance.getWeight()));
        params.put("enable", String.valueOf(instance.isEnabled()));
        params.put("healthy", String.valueOf(instance.isHealthy()));
        params.put("ephemeral", String.valueOf(instance.isEphemeral()));
        params.put("metadata", JSON.toJSONString(instance.getMetadata()));

        reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);

    }

req API我们不在向下追踪,实际通过HttpClient进行远程调用,有兴趣的自行查阅源码或者相关文档。

下面给出示例此处debugger截图如下2.1-2所示:

在这里插入图片描述

关于nacos客户端服务自动注册,分析到这里,下面我们去看看请求到达服务端后,服务端怎么处理的。

2.2 服务端注册

nacos官网注册实例URL,如下:

/nacos/v2/ns/instance

前面讲解知道nacos本质是基于springboot的web应用,那就好办了。我们去nacos源码找相应的controller,看看是哪个方法匹配该url。

com.alibaba.nacos.naming.controllers.v2.InstanceControllerV2#register()方法完成服务的服务注册

@CanDistro
@PostMapping
@Secured(action = ActionTypes.WRITE)
public Result<String> register(InstanceForm instanceForm) throws NacosException {
    // 省略校验
    // build instance
    Instance instance = buildInstance(instanceForm);
    instanceServiceV2.registerInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), instance);
    // 省略事件发布
}
  • buildInstance():方法用于构建实例对象,通过new+set

我们继续跟进InstanceOperatorClientImpl#registerInstance()方法

@Override
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
    NamingUtils.checkInstanceIsLegal(instance);
    
    boolean ephemeral = instance.isEphemeral();
    // 生成clientId  ip:port#true(orfalse)
    String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);
  // 缓存客户端
    createIpPortClientIfAbsent(clientId);
  // 生成服务Servcie对象,new一个新对象
    Service service = getService(namespaceId, serviceName, ephemeral);
  // 注册实例
    clientOperationService.registerInstance(service, instance, clientId);
}

我们来看下它是如何缓存客户端的即createIpPortClientIfAbsent()方法如何执行的?

private void createIpPortClientIfAbsent(String clientId) {
    if (!clientManager.contains(clientId)) {
        ClientAttributes clientAttributes;
        if (ClientAttributesFilter.threadLocalClientAttributes.get() != null) {
            clientAttributes = ClientAttributesFilter.threadLocalClientAttributes.get();
        } else {
            clientAttributes = new ClientAttributes();
        }
        clientManager.clientConnected(clientId, clientAttributes);
    }
}
  • clientManager:客户端管理接口实现类,类内部维护ConcurrentHashmap结构的clients用于缓存客户端。
  • 判断缓存是否有该clientId,如果没有通过通过ClientFactory创建新的Client对象,放入缓存。

继续跟进clientOperationService.registerInstance(),ephemeral默认为false即临时实例,调用EphemeralClientOperationServiceImpl#registerInstance()方法,代码如下:

@Override
public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
    NamingUtils.checkInstanceIsLegal(instance);

    Service singleton = ServiceManager.getInstance().getSingleton(service);
    if (!singleton.isEphemeral()) {
        throw new NacosRuntimeException(NacosException.INVALID_PARAM,
                String.format("Current service %s is persistent service, can't register ephemeral instance.",
                        singleton.getGroupedServiceName()));
    }
    Client client = clientManager.getClient(clientId);
    if (!clientIsLegal(client, clientId)) {
        return;
    }
    InstancePublishInfo instanceInfo = getPublishInfo(instance);
    client.addServiceInstance(singleton, instanceInfo);
    client.setLastUpdatedTime();
    client.recalculateRevision();
// 省略事件发布
}
  • 在上面创建的Service纳入ServiceManager管理,和clientManager类似
  • clientManager获取客户端client
  • 客户端绑定服务和实例,设置最后更新时间等等

到这里我们暂时梳理下客户端和服务的的注册流程,其中还涉及很多其他操作,我们后面慢慢讲解。

结语

如果小伙伴什么问题或者指教,欢迎交流。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/spring-cloud-study.git

参考地址:

[1]Nacos官网

[2]最新版Nacos 2.X服务端源码分析(一)

[3]Nacos 服务注册源码分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值