前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Nacos服务注册与发现源码

Nacos服务注册与发现源码

原创
作者头像
eeaters
修改2021-09-26 11:22:24
1.4K0
修改2021-09-26 11:22:24
举报
文章被收录于专栏:阿杰阿杰

首次搭建一个使用nacos的项目:

- https://cloud.tencent.com/developer/article/1881617

自动装配

spring.factories

代码语言:javascript
复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
  com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
  com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
  com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
  com.alibaba.cloud.nacos.NacosServiceAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
  com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration

nacos简单结构

nacos-client端底层的核心类是HostReactor

服务注册与发现

条件注解

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.enabled",matchIfMissing = true)
public @interface ConditionalOnNacosDiscoveryEnabled {
}

NacosDiscoveryProperties

启用服务发现时会创建该类,这个类是包含了以spring.cloud.nacos.discovery开头的nacos配置信息

NacosServiceDiscovery

spring cloud中定义服务发现的类是DiscoveryClient , 在nacos中实现类为NacosDiscoveryClient; 而NacosDiscoveryClient是NacosServiceDiscovery的包装类, 通过NacosServiceManager来进行实际的操作

NacosServiceRegistry

代码语言:javascript
复制
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
    private final NacosDiscoveryProperties nacosDiscoveryProperties;
    @Autowired
    private NacosServiceManager nacosServiceManager;
    
    //客户端往nacos注册中心进行服务注册
    @Override
    public void register(Registration registration) {
​
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
            return;
        }
​
        NamingService namingService = namingService();
        String serviceId = registration.getServiceId();
        String group = nacosDiscoveryProperties.getGroup();
        //当前节点实例信息
        Instance instance = getNacosInstanceFromRegistration(registration);
        try {
            //通过beanReactor创建心跳检测任务(有一定配置,默认是true,创建临时节点,定时查询nacos,如果nacos上没找到就重新注册上去)
            //通过serverProxy往nacos客户端注册服务实例
            namingService.registerInstance(serviceId, group, instance);
        }
        catch (Exception e) {
            rethrowRuntimeException(e);
        }
    }
}

NacosServiceManager

nacos服务管理,逻辑全部委派给NamingService或NamingMaintainService来操作

NamingMaintainService

这个类应该是与NacosNamingService有相反的作用,

NacosNamingService是主动获取或者被动接受nacos的消息,但是不能操作,相当于crud的r

该类是可以操作nacos,拥有了crud的cud功能

NacosNamingService

NacosServiceManager管理的NamingService的实现类,一个NacosServiceManager持有的NacosNamingService是一个单例对象,创建方式是典型的懒汉模式

代码语言:javascript
复制
    private NamingService buildNamingService(Properties properties) {
        if (Objects.isNull(namingService)) {
            synchronized (NacosServiceManager.class) {
                if (Objects.isNull(namingService)) {
                    namingService = createNewNamingService(properties);
                }
            }
        }
        return namingService;
    }

通过NamingProxy类对服务进行管理

通过BeatReactor类做心跳检测

通过HostReactor类做事件处理

NamingProxy

命名代理, 主要作用是与nacos之间进行交互

对于nacos需要账号密码的开启定时任务;定时刷新登录nacos服务端的token

BeatReactor

当前服务进行注册并且节点是临时节点(默认注册的就是临时节点)时, 会创建心跳检测任务,NacosServiceRegistry中有注释进行解释

任务的key=groupname+服务名称+ip+port; 如 : DEFAULT_GROUP@@nacos-consumer#172.23.215.241#8090

HostReactor

如果nacos的服务注册与发现有内核,这个类可能就是内核吧

代码语言:javascript
复制
public class HostReactor implements Closeable {
   private static final long DEFAULT_DELAY = 1000L;
    private static final long UPDATE_HOLD_INTERVAL = 5000L;
    private final Map<String, ScheduledFuture<?>> futureMap = new HashMap<String, ScheduledFuture<?>>();
    //服务的内存缓存
    private final Map<String, ServiceInfo> serviceInfoMap;
    //临时记录节点变更
    private final Map<String, Object> updatingMap;
    //本地启用一个udp端口,主动接受服务端的变更通知
    private final PushReceiver pushReceiver;
    //心跳检测 - 在这个类中创建心跳检测任务
    private final BeatReactor beatReactor;
    //nacos交互代理
    private final NamingProxy serverProxy;
    //failover-mode 模式下,使用本地文件代替内存缓存来返回服务信息
    private final FailoverReactor failoverReactor;
    //本地文件缓存的地址,默认 user.home\nacos\naming\namespace
    private final String cacheDir;
    private final boolean pushEmptyProtection;
    private final ScheduledExecutorService executor;
    //实例变更事件的订阅者
    private final InstancesChangeNotifier notifier;
    public HostReactor(NamingProxy serverProxy, BeatReactor beatReactor, String cacheDir, boolean loadCacheAtStart,
            boolean pushEmptyProtection, int pollingThreadCount) {
        // init executorService
        this.executor = new ScheduledThreadPoolExecutor(pollingThreadCount, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setDaemon(true);
                thread.setName("com.alibaba.nacos.client.naming.updater");
                return thread;
            }
        });
        this.beatReactor = beatReactor;
        this.serverProxy = serverProxy;
        this.cacheDir = cacheDir;
        if (loadCacheAtStart) {
            this.serviceInfoMap = new ConcurrentHashMap<String, ServiceInfo>(DiskCache.read(this.cacheDir));
        } else {
            this.serviceInfoMap = new ConcurrentHashMap<String, ServiceInfo>(16);
        }
        this.pushEmptyProtection = pushEmptyProtection;
        this.updatingMap = new ConcurrentHashMap<String, Object>();
        this.failoverReactor = new FailoverReactor(this, cacheDir);
        this.pushReceiver = new PushReceiver(this);
        this.notifier = new InstancesChangeNotifier();
        
        //注册一个InstancesChangeEvent事件的发布器,将notifier作为订阅者
        NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
        NotifyCenter.registerSubscriber(notifier);
    }
}

本地文件缓存:

类似于zookeeper的节点信息, 节点示例:

代码语言:javascript
复制
{"name":"DEFAULT_GROUP@@nacos-consumer","clusters":"DEFAULT","cacheMillis":10000,"hosts":[{"instanceId":"172.23.215.241#8090#DEFAULT#DEFAULT_GROUP@@nacos-consumer","ip":"172.23.215.241","port":8090,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@nacos-consumer","metadata":{"preserved.register.source":"SPRING_CLOUD"},"instanceHeartBeatInterval":5000,"instanceHeartBeatTimeOut":15000,"ipDeleteTimeout":30000}],"lastRefTime":1632383613389,"checksum":"","allIPs":false,"reachProtectionThreshold":false,"valid":true}

HostReactor#getServiceInfo

这个方法在看代码的时候有一些疑惑的地方,在debug后整理如下:

  • serviceInfoMap初始化是loadCacheAtStart默认是false, 初始化后是一个空的map对象
    • 反证一下: 如果初始化时就缓存所有服务缓存,但是这个服务只需要连接某一个或几个服务就够了, 全部缓存确实也不合理
  • 在需要使用某个服务时,如果serviceMap中不存在
    • 如果updatingMap中存在,就进行等待, 这样子下面的操作就不是每一个线程都执行,而是只有一个线程执行
    • 这个服务会放到updatingMap中
    • 立即执行服务的刷新操作
      • 通过serverProxy获取该服务的数据
      • 与本地进行对比,如果发生变更就发送实例变更事件InstancesChangeEvent, 并且将拉到的数据缓存到本地cacheDir目录下
      • 通知第一步中等待的线程可以执行了
    • 从updatingMap中移出
  • computerIfAbsent(是否有定时主动拉取远端配置的定时任务) 和变更事件一样; 对于首次使用的服务会创建一个定时任务主动拉取最新配置
  • 返回服务信息

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自动装配
    • spring.factories
      • nacos简单结构
      • 服务注册与发现
        • 条件注解
          • NacosDiscoveryProperties
            • NacosServiceDiscovery
              • NacosServiceRegistry
                • NacosServiceManager
                  • NamingMaintainService
                    • NacosNamingService
                      • NamingProxy
                        • BeatReactor
                          • HostReactor
                            • 本地文件缓存:
                            • HostReactor#getServiceInfo
                        相关产品与服务
                        微服务引擎 TSE
                        微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档