Nacos源码之服务注册源码解析

前言

本篇就开始探索Nacos的服务注册逻辑,建议大家准备两个项目,一个是Nacos的源码项目,一个是整合Nacos注册中心的业务项目。探索过程也是分为客户端和服务端的代码解析,我觉得这样应该更利于代码跟进和理解,篇幅过长,还请耐心观看。

主要类图

在这里插入图片描述

客户端逻辑

首先来看客户端,也就是我们的项目在启动时,Nacos依赖偷偷干了哪些见不得人的事情。打开项目依赖,找到引入的注册中心依赖,我们知道SpringBoot项目启动时会读取spring.factories文件进行自动装配
在这里插入图片描述
你们猜一猜哪个是首先要看的类,我猜是NacosDiscoveryAutoConfiguration这个类,你问我为什么,因为它写在第一位。你觉得不是?那我只能说是男人的直觉,源码看多掉头发的男人的直觉,没有毛病~
点开这个类,反手就是注入三个Bean,分别是NacosServiceRegistryNacosRegistrationNacosAutoServiceRegistration

@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter({
    AutoServiceRegistrationConfiguration.class,
		AutoServiceRegistrationAutoConfiguration.class })
public class NacosDiscoveryAutoConfiguration {
   

	@Bean
	public NacosServiceRegistry nacosServiceRegistry(
			NacosDiscoveryProperties nacosDiscoveryProperties) {
   
		return new NacosServiceRegistry(nacosDiscoveryProperties);
	}

	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosRegistration nacosRegistration(
			NacosDiscoveryProperties nacosDiscoveryProperties,
			ApplicationContext context) {
   
		return new NacosRegistration(nacosDiscoveryProperties, context);
	}

	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosAutoServiceRegistration nacosAutoServiceRegistration(
			NacosServiceRegistry registry,
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			NacosRegistration registration) {
   
		return new NacosAutoServiceRegistration(registry,
				autoServiceRegistrationProperties, registration);
	}
}

NacosDiscoveryProperties这个类就是提供Nacos各种参数配置的类,很多集成框架都提供了这么一个类,比如RedisProperties,只需要在配置参数类上加上@ConfigurationProperties注解,然后在配置类或者启动类上加上@EnableConfigurationProperties注解,就可以将配置参数类注入到容器中,这种高逼格的做法大家学废了吗。

NacosRegistration这个配置类的功能主要就是处理我们配置的metadata元数据,既然支持灵活配置,那就肯定需要代码来支持。

NacosServiceRegistry这个配置类就比较重要了,它里面包含了一个NamingService命名服务类:

public class NacosServiceRegistry implements ServiceRegistry<Registration> {
   
	private final NacosDiscoveryProperties nacosDiscoveryProperties;

	private final NamingService namingService;

	public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
   
		this.nacosDiscoveryProperties = nacosDiscoveryProperties;
		this.namingService = nacosDiscoveryProperties.namingServiceInstance();
	}

	......
}

实例化的时候调用了NacosDiscoveryProperties的**namingServiceInstance()**方法

public NamingService namingServiceInstance() {
   

	if (null != namingService) {
   
		return namingService;
	}

	try {
   
		namingService = NacosFactory.createNamingService(getNacosProperties());
	}
	catch (Exception e) {
   
		log.error("create naming service error!properties={},e=,", this, e);
		return null;
	}
	return namingService;
}

跟进去最后调用了NamingFactory工厂的**createNamingService()**静态方法

public class NamingFactory {
   

    public static NamingService createNamingService(String serverList) throws NacosException {
   
        try {
   
            Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
            Constructor constructor = driverImplClass.getConstructor(String.class);
            NamingService vendorImpl = (NamingService)constructor.newInstance(serverList);
            return vendorImpl;
        } catch (Throwable e) {
   
            throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
        }
    }

    public static NamingService createNamingService(Properties properties) throws NacosException {
   
        try {
   
            Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
            Constructor constructor = driverImplClass.getConstructor(Properties.class);
            NamingService vendorImpl = (NamingService)constructor.newInstance(properties);
            return vendorImpl;
        } catch (Throwable e) {
   
            throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
        }
    }
}

可以看到通过获取带有Properties参数的构造器反射实例化NacosNamingService对象,实例化时就会调用带有Properties参数的构造方法

public class NacosNamingService implements NamingService {
   
	/**
     * 命名空间【public、prod、dev】
     *
     * Each Naming service should have different namespace.
     */
    private String namespace;

    private String endpoint;

    /**
     * Nacos 服务地址【127.0.0.1:8848,127.0.0.1:8848】
     **/
    private String serverList;

    /**
     * 本地注册表缓存存放目录【/Users/xxx/nacos/naming/public】
     **/
    private String cacheDir;

    /**
     * 日志文件名【naming.log】
     **/
    private String logName;

    /**
     * 本机响应器
     **/
    private HostReactor hostReactor;

    /**
     * 心跳响应器
     **/
    private BeatReactor beatReactor;

    /**
     * 事件调度器
     **/
    private EventDispatcher eventDispatcher;

    /**
     * 命名代理
     **/
    private NamingProxy serverProxy;

    public NacosNamingService(String serverList) {
   
        Properties properties = new Properties();
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);
        init(properties);
    }

    public NacosNamingService(Properties properties) {
   
        init(properties);
    }

    private void init(Properties properties) {
   
        ValidatorUtils.checkInitParam(properties);
        // 初始化命名空间
        namespace = InitUtils.initNamespaceForNaming(properties);
        // 初始化服务地址
        initServerAddr(properties);
        // 初始化根上下文
        InitUtils.initWebRootContext();
        // 初始化缓存文件地址
        initCacheDir();
        // 初始化日志文件名称
        initLogName(properties);
        // 实例化时间调度器
        eventDispatcher = new EventDispatcher();
        // 实例化命名代理程序
        serverProxy = new NamingProxy(namespace, endpoint, serverList, properties);
        // 实例化心跳响应器
        beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
        // 实例化本机响应器
        hostReactor = new HostReactor(
            eventDispatcher,
            serverProxy,
            cacheDir,
            // 启动时就加载缓存【False】
            isLoadCacheAtStart(properties),
            // 初始化线程池的核心线程数
            initPollingThreadCount(properties)
        );
    }
    ......
}

**init()**方法进行一系列的初始化工作,BeatReactor用来处理心跳上报,HostReactor处理故障转移和接收服务端推送的服务列表数据。看一下实例化HostReactor时都做了哪些事情

public class HostReactor {
   
	
	/**
     * 从服务器拉取最新数据延时时间
     **/
    private static final long DEFAULT_DELAY = 1000L;

    /**
     * 等待更新完成时间
     **/
    private static final long UPDATE_HOLD_INTERVAL = 5000L;

    private final Map<String, ScheduledFuture<?>> futureMap = new HashMap<>();

    /**
     * 本地的服务注册表
     **/
    private Map<String, ServiceInfo> serviceInfoMap;

    /**
     * 需要更新的集合
     **/
    private Map<String, Object> updatingMap;

    /**
     * 推送接收器
     **/
    private PushReceiver pushReceiver;

    /**
     * 事件调度程序
     **/
    private EventDispatcher eventDispatcher;

    /**
     * 命名代理
     **/
    private NamingProxy serverProxy;

    /**
     * 故障转移响应器
     **/
    private FailoverReactor failoverReactor;

    private String cacheDir;

    private ScheduledExecutorService executor;

    public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, String cacheDir) {
   
        this(eventDispatcher, serverProxy, cacheDir, false, UtilAndComs.DEFAULT_POLLING_THREAD_COUNT);
    }

    public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, String cacheDir,
                       boolean loadCacheAtStart, int pollingThreadCount) {
   

        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.eventDispatcher = eventDispatcher;
        this.serverProxy = serverProxy;
        this.cacheDir = cacheDir;
        if (loadCacheAtStart) {
   
            this.serviceInfoMap = new ConcurrentHashMap<>(DiskCache.read(this.cacheDir));
        }
        else {
   
            this.serviceInfoMap = new ConcurrentHashMap<>(16);
        }

        this.updatingMap = new ConcurrentHashMap<>();
        // 实例化故障转移响应器
        this.failoverReactor = new FailoverReactor(this, cacheDir);
        /**
         * 实例化推送接收器【重要】
         **/
        this.pushReceiver = new PushReceiver(this);
    }
}

FailoverReactor用来处理故障转移,PushReceiver用来和服务端保持通信,接收推送的服务列表数据

public class PushReceiver implements Runnable {
   
	
	private ScheduledExecutorService executorService;

    private static final int UDP_MSS = 64 * 1024;

    private DatagramSocket udpSocket;

    private HostReactor hostReactor;

    public PushReceiver(HostReactor hostReactor) {
   
        try {
   
            this.hostReactor = hostReactor;
            // 初始化套接字
            udpSocket = new DatagramSocket();
            // 初始化执行器
            executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
   
                @Override
                public Thread newThread(Runnable r) {
   
                    Thread thread = new Thread(r);
                    thread.setDaemon(true);
                    thread.setName("com.alibaba.nacos.naming.push.receiver");
                    return thread;
                }
            }
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

从入门到脱发

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

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

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

打赏作者

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

抵扣说明:

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

余额充值