源码: Nocos 名字服务的服务发现(一)

Nocos名字服务

提供分布式系统中所有对象(Object)、实体(Entity)的“名字”到关联的元数据之间的映射管理服务,例如 ServiceName -> Endpoints Info, Distributed Lock Name -> Lock Owner/Status Info, DNS Domain Name -> IP List, 服务发现和 DNS 就是名字服务的2大场景。

客户端部分

启动服务

com.alibaba.nacos.api.naming.NamingFactory

  1. 创建 com.alibaba.nacos.api.naming.NamingService
1. 工厂创建NamingService
NamingFactory.createNamingService(properties)

2. 创建服务实现
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);
        }
}
复制代码

com.alibaba.nacos.client.naming.NacosNamingService

  1. 创建NacosNamingService##init()
private void init(Properties properties) throws NacosException {
        //参数校验
        ValidatorUtils.checkInitParam(properties);
        // 通过配置初始化namespace
        this.namespace = InitUtils.initNamespaceForNaming(properties);
        InitUtils.initSerialization();
        initServerAddr(properties);
        InitUtils.initWebRootContext(properties);
        // 初始化缓存地址
        initCacheDir();
        // 初始化日志名称
        initLogName(properties);
        
        // 实例变更通知
        this.changeNotifier = new InstancesChangeNotifier();
        NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
        NotifyCenter.registerSubscriber(changeNotifier);
        
        this.serviceInfoHolder = new ServiceInfoHolder(namespace, properties);
        // 客户端代理
        this.clientProxy = new NamingClientProxyDelegate(this.namespace,serviceInfoHolder, properties, changeNotifier);
}
复制代码

相关字段说明

  • namespace:

用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

服务注册

NacosNamingService##registerInstance

    /**
     * register a instance to service with specified cluster name.
     *
     * @param serviceName 服务名称 
     * @param ip          IP
     * @param port        端口
     * @param clusterName 集群名称
     * @throws NacosException nacos exception
     */
    void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;
复制代码

registerInstance方法:

    @Override
    public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName)
            throws NacosException {
        // 实例对象赋值
        Instance instance = new Instance();
        instance.setIp(ip);
        instance.setPort(port);
        instance.setWeight(1.0);
        instance.setClusterName(clusterName);
        // 真正的注册服务
        registerInstance(serviceName, groupName, instance);
    }
    
    @Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
        NamingUtils.checkInstanceIsLegal(instance);
        // 客户端代理注册服务
        clientProxy.registerService(serviceName, groupName, instance);
    }
复制代码

void registerService(String serviceName, String groupName, Instance instance)

服务向注册中心注册时,真正调用的方法:

    @Override
    public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        
        // 检测实例参数
        NamingUtils.checkInstanceIsLegal(instance);
        // 通过服务名和组名组装name,格式:groupName@@serviceName
        String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
        if (instance.isEphemeral()) {
            // 构建心跳信息
            BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
            // 添加心跳信息, 内部是添加心跳检测任务
            beatReactor.addBeatInfo(groupedServiceName, beatInfo);
        }
        
        final Map<String, String> params = new HashMap<String, String>(16);
        // 省略参数构建...
        
        reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
        
    }
复制代码

serverProxy.registerService注册链路

  1. NameProxy##reqApi的 callServer 方法完成服务的注册;
  2. reqApi方法的入参 servers 通过getServerList()获取到,本质是从 serversFromEndpoint 属性中获取
  3. serversFromEndpoint初始化赋值 来在方法 getServerListFromEndpoint(), 通过Http请求 http://" + endpoint + "/nacos/serverlist 获取服务列表。

扩展

String##intern():
tech.meituan.com/2014/03/06/…


服务端部分

Nocos 服务API采用了 restful 风格的方式,通过请求的shareMethod方式来定位使用具体哪种实现。

  1. 注册服务接口: POST /nacos/v1/ns/instance
    @CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {
        /* 
         * 省略 从request中获取 对应参数值 ......
        */
        // 核心方法
        serviceManager.registerInstance(namespaceId, serviceName, instance);
        return "ok";
}
复制代码
  1. 具体的创建实例
    /**
     * Register an instance to a service in AP mode.
     *
     * <p>This method creates service or cluster silently if they don't exist.
     *
     * @param namespaceId id of namespace
     * @param serviceName service name
     * @param instance    instance to register
     * @throws Exception any error occurred in the process
     */
    public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
        // 创建空的服务, 若服务不存在则创建
        createEmptyService(namespaceId, serviceName, instance.isEphemeral());
          
        Service service = getService(namespaceId, serviceName);
        if (service == null) {
            throw new NacosException(NacosException.INVALID_PARAM,
                    "service not found, namespace: " + namespaceId + ", service: " + serviceName);
        }
        // 添加服务对应的实例
        addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
    }
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享