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

news/2024/12/2 21:26:34/

文章目录

    • 1 搭建测试服务
      • 1.1 搭建服务
      • 1.2 启动测试
    • 2 nacos 服务注册原理
      • 2.1 客户端注册
      • 2.2 服务端注册
    • 结语

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()方法

	@Overridepublic 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);}@Overridepublic 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 instanceInstance 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 服务注册源码分析


http://www.ppmy.cn/news/40407.html

相关文章

关于FTP文件传输协议说明,带你了解更详细的文件传输协议

FTP是文件传输协议的缩写。顾名思义&#xff0c;FTP用于在网络上的计算机之间传输文件。您可以使用文件传输协议在计算机帐户之间交换文件&#xff0c;在帐户和台式计算机之间传输文件或访问在线软件档案。但是请记住&#xff0c;许多文件传输协议站点已被大量使用&#xff0c;…

Android Jetpack 从使用到源码深耕【数据库注解Room 从实践到原理 】(二)

上文,我们通过一个简单的sqlite应用实例,引入了Room,知道了Room使用的便捷和好处。然后用Room的方式,重新实现了应用实例中的场景,在这个过程中,我们结合自己已有的知识体系,从使用代码入手,对Room的实现原理,进行了猜想和简单的验证。 Room实现原理,是否真如我们猜想…

Typescript - function 函数(箭头函数 / 参数类型与返回类型 / 可选参数与默认参数 / 剩余参数 / 函数重载)通俗易懂详细示例教程

前言 在 Typescript 中&#xff0c;对 JavaScript 函数进行了 “升级”&#xff0c;继承了基本功能的同时又增加了一些新用法&#xff08;使其更加严谨&#xff09;。 用一个表格&#xff0c;可以大致描绘出异同点。 TypeScriptJavaScript含有类型无类型箭头函数箭头函数&…

java程序解析jts的geometry类型并入PG数据库

场景 GIS开发&#xff0c;会有需要将jts包中的geometry类型数据存入pg&#xff08;postgis扩展后&#xff09;数据库的需求。 工程是springboot&#xff0c;mybatis作为持久层框架。 解决方案 1. pg的geometry字段对应的类型为geometry类型&#xff0c;比如&#xff1a; 2.…

面向对象三大基本特征

面向对象三大基本特征封装继承多态封装 把客观事物封装成抽象的一个类&#xff0c;并且类可以把自己的数据和方法只让可信的类或者对象来操作。 一个类就是一个封装的数据&#xff0c;以及操作这些数据的代码的逻辑实体。 在一个对象的内部&#xff0c;某些代码或者是某些数…

NTP8835(30W内置DSP双通道D类音频功放芯片)

数字功放是一种具有失真小、噪音低、动态范围大等特点的音频功率放大器&#xff1b;由工采网代理的韩国耐福旗下NTP系列专业功率放大器是ClassD功放的一个新里程碑。 NTP8835是一款高性能、高保真功率驱动集成全数字音频放大器&#xff0c;工作电压范围&#xff1a;7V&#xf…

linux上的无线网卡灯不亮

linux上的无线网卡灯不亮&#xff0c;查看了型号后&#xff0c;RTL8811CU 这个方法&#xff0c;可以说是一步到位 先克隆 git 仓库 git clone https://github.com/morrownr/8821cu-20210916.git cd 8821cu-20210916 直接运行安装脚本 ./install-driver.sh 安装完会问两个…

c语言获取天气信息示例(通过心知天气api获取)

关于curl/curl.h库的使用&#xff0c;参考下述内容&#xff1a; VS2010编译libcurl库并简单使用(c语言)_西晋的no1的博客-CSDN博客 1.先在心知天气注册&#xff0c;获取私钥&#xff1a; https://www.seniverse.com/dashboard 2.将私钥放入下述url中【私钥” 直接请求方式】 将…