文章目录
- 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 服务注册源码分析