nacos源码服务注册

nacos服务注册

  • 序言
  • 1.源码环境搭建
    • 1.1idea运行源码
    • 1.2 登录nacos
  • 2.服务注册分析
    • 2.1 客户端
      • 2.1.1容器启动监听
      • 2.1.2注册前初始化
      • 2.1.3注册服务
    • 2.2 服务端
      • 2.2.1注册
      • 2.2.2重试机制
  • 3.注意事项

序言

本文章是分析的是nacos版本2.2
这次版本是一次重大升级优化,由原来(临时服务)服务注册tcp改成grpc方式,减少资源消耗,服务推送由udp也改为grpc

1.源码环境搭建

  • 源码下载

1.1idea运行源码

  • 导入
  • clean-install-reimport
    nacos源码服务注册_第1张图片
  • 配置
    nacos源码服务注册_第2张图片
  • 启动
    单机运行 启动项配置 VM option
    nacos源码服务注册_第3张图片

1.2 登录nacos

nacos源码服务注册_第4张图片

2.服务注册分析

2.1 客户端

首先让我去实现一个服务注册,我们如何去做?
思考一:
是不是项目启动的时候进行服务注册
思考二:
springboot自动装载,是不是引入nacos依赖,就可以实现服务注册功能

按照上面的思路去分析注册大概过程就很清晰了

2.1.1容器启动监听

  • 自动加载入口
 @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
    }
    
 public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        super(serviceRegistry, autoServiceRegistrationProperties);
        this.registration = registration;
    }
  • web容器监听
public abstract class AbstractAutoServiceRegistration<R extends Registration> implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {

  • 綁定事件
    public void onApplicationEvent(WebServerInitializedEvent event) {
        this.bind(event);
    }

    /** @deprecated */
    @Deprecated
    public void bind(WebServerInitializedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
            this.port.compareAndSet(0, event.getWebServer().getPort());
            this.start();
        }
    }
  public void start() {
        if (!this.isEnabled()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Discovery Lifecycle disabled. Not starting");
            }

        } else {
            if (!this.running.get()) {
                this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
                this.register();
                if (this.shouldRegisterManagement()) {
                    this.registerManagement();
                }

                this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
                this.running.compareAndSet(false, true);
            }

        }
    }

2.1.2注册前初始化

  • 获取namingservice服务
 public void register(Registration registration) {
       //创建namingservice服务
       NamingService namingService = this.namingService();
       //注册
       namingService.registerInstance(serviceId, group, instance);  
    }
  • 判断服务是否存在
  public NamingService getNamingService() {
        //不存在进行创建
        if (Objects.isNull(this.namingService)) {
            this.buildNamingService(this.nacosDiscoveryProperties.getNacosProperties());
        }

        return this.namingService;
    }
  • 创建NamingService服务
 NamingService naming = NamingFactory.createNamingService(properties);
 public NacosNamingService(Properties properties) throws NacosException {
        //nacosNamingService 客户端初始化
        init(properties);
    }
    
    private void init(Properties properties) throws NacosException {
        final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties);
        
        ValidatorUtils.checkInitParam(nacosClientProperties);
        //获取命名空间
        this.namespace = InitUtils.initNamespaceForNaming(nacosClientProperties);
        InitUtils.initSerialization();
        InitUtils.initWebRootContext(nacosClientProperties);
        initLogName(nacosClientProperties);
        //通知时间范围
        this.notifierEventScope = UUID.randomUUID().toString();
        //改变消息通知者
        this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope);
        //消息中心注册事件
        NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
        //注册订阅者
        NotifyCenter.registerSubscriber(changeNotifier);
        //服务信息持有者
        this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties);
        //创建客户端代理 去委托NamingClientProxyDelegate
        this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties, changeNotifier);
    }

2.1.3注册服务

   public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
                instance);
        //缓存实例数据,增强可靠性
        redoService.cacheInstanceForRedo(serviceName, groupName, instance);
        //注册
        doRegisterService(serviceName, groupName, instance);
    }
 public void doRegisterService(String serviceName, String groupName, Instance instance) throws NacosException {
        InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
                NamingRemoteConstants.REGISTER_INSTANCE, instance);
        //请求注册
        requestToServer(request, Response.class);
        //设置缓存数据状态为已经注册状态
        redoService.instanceRegistered(serviceName, groupName);
    }

2.2 服务端

2.2.1注册

    private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta)
            throws NacosException {
        clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());
        NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),
                meta.getClientIp(), true, service.getNamespace(), service.getGroup(), service.getName(),
                request.getInstance().getIp(), request.getInstance().getPort()));
        return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
    }
    @Override
    public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
        NamingUtils.checkInstanceIsLegal(instance);
        //获取当前的 Service (没有就新创建)
        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();
        // 发布通知注册时间
        NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
        NotifyCenter
                .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
    }
    

2.2.2重试机制

为了确保注册成功,会从缓存中,把注册状态为失败的注册实例进行重新注册

   public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy) {
        this.redoExecutor = new ScheduledThreadPoolExecutor(REDO_THREAD, new NameThreadFactory(REDO_THREAD_NAME));
        this.redoExecutor.scheduleWithFixedDelay(new RedoScheduledTask(clientProxy, this), DEFAULT_REDO_DELAY,
                DEFAULT_REDO_DELAY, TimeUnit.MILLISECONDS);
    }
    public void run() {
        if (!redoService.isConnected()) {
            LogUtils.NAMING_LOGGER.warn("Grpc Connection is disconnect, skip current redo task");
            return;
        }
        try {
            //重新注册
            redoForInstances();
            //重新订阅
            redoForSubscribes();
        } catch (Exception e) {
            LogUtils.NAMING_LOGGER.warn("Redo task run with unexpected exception: ", e);
        }
    }

3.注意事项

  • idea 运行源码找不到类
    这时,你需要clean 再install 最后reimport,就不会问题
    nacos源码服务注册_第5张图片

  • 启动时nacos没有以单例模式启动也会报错

你可能感兴趣的:(源码分析,java,spring,boot,开发语言)