Nacos客户端namespace初始化及注册流程


很多人在学习开源的时候,无从下手,代码那么多,从哪个地方开始呢?我们学习nacos,首先去到nocas的github源码的地方链接: https://github.com/alibaba/nacos下载源码到我们的idea,打开example项目,
在这里插入图片描述
进入APP,可以看到如下代码:

   public static void main(String[] args) throws NacosException {
   
        Properties properties = new Properties();
        properties.setProperty("serverAddr", "21.34.53.5:8848,21.34.53.6:8848");
        properties.setProperty("namespace", "quickStart");
        NamingService naming = NamingFactory.createNamingService(properties);
        naming.registerInstance("nacos.test.3", "11.11.11.11", 8888, "TEST1");
        naming.registerInstance("nacos.test.3", "2.2.2.2", 9999, "DEFAULT");
        System.out.println(naming.getAllInstances("nacos.test.3"));
    }

这里我们可以看到,这里构建了一个NamingService实例,同时设置了我们的nacos服务端的地址和端口,设置namespace。
我们进入createNamingService方法

NamingService

    /**
     * Create a new naming service.
     *
     * @param properties naming service properties
     * @return new naming service
     * @throws NacosException nacos exception
     */
    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);
        }
    }

这里通过反射创建了一个NamingService实例,实际的实现类是在api项目里面的NacosNamingService,随后我们进入NacosNamingService看看

NacosNamingService

1.先看里面的属性
namespace 名字空间
endpoint 服务管理服务端地址管理服务器地址,获取服务管理服务端地址(当 nacos server 集群需要扩缩容时,客户端需要有一种能力能够及时感知到集群发生变化,及时感知到集群的变化是通过 endpoint 来实现的。也即客户端会定时的向endpoint发送请求来更新客户端内存中的集群列表。)
serverList 服务管理服务端地址,可直接配置,或从endpoint获取
cacheDir 调用服务信息本地文件缓存地址
logName 暂未使用
HostReactor 客户端关心的服务的实例信息,推拉模式的更新,failover服务实例信息读写管理
BeatReactor 本地实例信息心跳
EventDispatcher 服务信息变更监听回调处理
NamingProxy 服务管理服务端地址列表更新管理,接口调用负载均衡,失败重试

 /**
     * Each Naming service should have different namespace.
     * 名字空间
     */
    private String namespace;

    /**
     * 当 nacos server 集群需要扩缩容时,客户端需要有一种能力能够及时感知到集群发生变化。
     * 及时感知到集群的变化是通过 endpoint 来实现的。也即客户端会定时的向 endpoint 发送请求来更新客户端内存中的集群列表。
     * 服务管理服务端地址管理服务器地址,获取服务管理服务端地址
     */
    private String endpoint;

    /**
     * 服务管理服务端地址管理服务器地址,获取服务管理服务端地址
     */
    private String serverList;

    /**
     * 服务管理服务端地址管理服务器地址,获取服务管理服务端地址
     */
    private String cacheDir;

    private String logName;

    /**
     * 客户端关心的服务的实例信息,推拉模式的更新,failover服务实例信息读写管理
     */
    private HostReactor hostReactor;

    /**
     * 本地实例信息心跳
     */
    private BeatReactor beatReactor;

    /**
     * 服务信息变更监听回调处理
     */
    private EventDispatcher eventDispatcher;

    /**
     * 服务管理服务端地址列表更新管理,接口调用负载均衡,失败重试
     */
    private NamingProxy serverProxy;

了解了相关字段的意思我们来看看构造方法

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

这里其实就是执行init初始化方法

   private void init(Properties properties) throws NacosException {
   
        ValidatorUtils.checkInitParam(properties); //检查contextPath格式 可为空
        this.namespace = InitUtils.initNamespaceForNaming(properties); //初始化命名空间
        //子类实现类中的静态代码串中已经向Jackson进行了注册,但是由于classloader的原因,只有当 该子类被使用的时候,才会加载该类。
        // 这可能会导致Jackson先进性反序列化,再注册子类,从而导致 反序列化失败。
        //所以这里将NoneSelector、ExpressionSelector这两个类进行注册或者销毁
        InitUtils.initSerialization();

        //这里进行nacos服务端地址初始化
        //这里面会涉及到是否启用endpoint
        initServerAddr(properties);

        //如果应用由EDAS部署,则支持阿里云的web上下文
        InitUtils.initWebRootContext();

        //这里初始化本地缓存的路径及存放的registerInstance的内容
        initCacheDir();

        //初始化LogName,未设置用naming.log
        initLogName(properties);

        /**
         *初始化ExecutorService线程池,创建名字为com.alibaba.nacos.naming.client.listener的daemon线程Notifier
         * EventDispatcher中有一个LinkedBlockingQueue队列,放的是ServiceInfo
         * EventDispatcher中有ConcurrentMap<String, List<EventListener>>放入的是EventListener
         *Notifier中run方法解析
         *                  先去队列中弹出队顶元素(poll方法)
         *                  如果为空进行下一次循环
         *                  如果不为空则去ConcurrentMap取listeners
         *                  去除listener去监听NamingEvent
         *
        */
        this.eventDispatcher = new EventDispatcher();

        /**
         * 初始化服务代理,用户名密码服务地址及initRefreshTask任务的线程池,创建com.alibaba.nacos.client.naming.updater名字的daemon线程
         */
        this.serverProxy = new NamingProxy(this.namespace, this.endpoint, this.serverList, properties);

        /**
         * initClientBeatThreadCount(properties):Runtime.getRuntime().availableProcessors()返回到Java虚拟机的可用的处理器数量
         * 创建一个此案城池com.alibaba.nacos.naming.beat.sender的daemon线程
         */
        this.beatReactor = new BeatReactor(this.serverProxy, initClientBeatThreadCount(properties));
        /**
         * 同上
         */
        this.hostReactor = new HostReactor(this.eventDispatcher, this.serverProxy, beatReactor, this.cacheDir,
                isLoadCacheAtStart(properties), initPollingThreadCount(properties));
    }

innit方法里面在初始化各个模块,具体的步骤是
1.检查contextPath格式
2.将NoneSelector、ExpressionSelector这两个类进行注册或者销毁
3.nacos服务端地址初始化
4.如果应用由EDAS部署,则支持阿里云的web上下文
5.这里初始化本地缓存
6.初始化LogName
7.初始化服务信息变更监听回调处理
8.初始化服务管理服务端地址列表更新管理,接口调用负载均衡,失败重试
9.初始化本地实例信息心跳
10.初始化客户端关心的服务的实例信息
说明:7-10都会初始化线程池,创建daemon线程
总的来说,init方法为我们初始化各种本地信息,下面来看具体初始化方法

ValidatorUtils.checkInitParam(properties)

public static final String CONTEXT_PATH = "contextPath";
   
    private static final Pattern CONTEXT_PATH_MATCH = Pattern.compile("(\\/)\\1+");
    
    public static void checkInitParam(Properties properties) throws NacosException {
   
        checkContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH));
    }
    
    /**
     * Check context path.
     *
     * @param contextPath context path
     */
    public static void checkContextPath(String contextPath) {
   
        if (contextPath == null) {
   
            return;
        }
        Matcher matcher = CONTEXT_PATH_MATCH.matcher(contextPath);
        if (matcher.find()) {
   
            throw new IllegalArgumentException("Illegal url path expression");
        }
    }
    

这里的代码比较简单,只是检查了一下contextPath

InitUtils.initNamespaceForNaming(properties)

    /**
     * Add a difference to the name naming. This method simply initializes the namespace for Naming. Config
     * initialization is not the same, so it cannot be reused directly.
     *
     * 为名称命名添加差异。此方法简单地初始化命名空间以进行命名。配置初始化不一样,所以不能直接重用。
     *
     * @param properties properties
     * @return namespace
     */
    public static String initNamespaceForNaming(Properties properties) {
   
        String tmpNamespace = null;

        String isUseCloudNamespaceParsing = properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING,
                System.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING,
                        String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING)));//默认是true
        System.out.println("isUseCloudNamespaceParsing:" + isUseCloudNamespaceParsing);
        if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) {
   

            tmpNamespace = TenantUtil.getUserTenantForAns();//这里是ans,据说是注册中心,未设置tenant.id和ans.namespace 返回为空
            /**
             * 这里检查是否为空,如果不为空发返回tmpNamespace,如果为空执行Callable.call()方法,
             * call()方法里面去取ans.namespace属性,返回namespace
             */
            tmpNamespace = TemplateUtils.stringEmptyAndThenExecute(tmpNamespace, new Callable<String>() {
   
                @Override
                public String call() {
   
                    String namespace = System.getProperty(SystemPropertyKeyConst.ANS_NAMESPACE);
                    LogUtils.NAMING_LOGGER.info("initializer namespace from System Property :" + namespace);

                    return namespace;
                }
            });

            /**
             * 这里检查是否为空,如果不为空发返回tmpNamespace,如果为空执行Callable.call()方法,
             * call()方法里面去取ALIBABA_ALIWARE_NAMESPACE环境变量
             */
            tmpNamespace = TemplateUtils.stringEmptyAndThenExecute(tmpNamespace, new Callable<String>() {
   
                @Override
                public String call() {
   
                    String namespace = System.getenv(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE);
                    LogUtils.NAMING_LOGGER.info("initializer namespace from System Environment :" + namespace);
                    return namespace;
                }
            });
        }

        /**
         * 这里检查是否为空,如果不为空发返回tmpNamespace,如果为空执行Callable.call()方法,
         * call()方法里面去取NAMESPACE属性
         */
        tmpNamespace = TemplateUtils.stringEmptyAndThenExecute(tmpNamespace, new Callable<String>() {
   
            @Override
            public String call() {
   
                String namespace = System.getProperty(PropertyKeyConst.NAMESPACE);
                LogUtils.NAMING_LOGGER
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值