IT袋

当前位置:主页 > 经验教程 > 建站编程 >

Nacos源码分析

Nacos源码分析 "Nacos源码解析"

时间:2023-09-23 15:11:19 来源:IT袋 作者:苏晓敏
导读:Nacos源码分析,一个电脑小知识,为大家介绍Nacos源码分析的介绍,下面小编为您详细解答 看Nacos源码的不难发现,Nacos实际上就是一个基于Spring Boot的web应用,不管是服务注册还是发送心

Nacos源码分析

一个电脑小知识,为大家介绍Nacos源码分析的介绍,下面小编为您详细解答

看Nacos源码的不难发现,Nacos实际上就是一个基于Spring Boot的web应用,不管是服务注册还是发送心跳都是通过给Nacos服务端发送http请求实现的。下载并编译Nacos源码就不过多赘述了,首先需要搭建一个微服务作为Nacos的客户端。

Nacos源码分析

Nacos客户端注册

Nacos客户端也是个Spring Boot项目,当客户端服务启动时Spring Boot项目启动时自动加载
spring-cloud-starter-alibaba-nacos-discovery包的META-INF/spring.factories中包含自动装配的配置信息,并将文件中的类加载成bean放入Spring容器中,我们可以先看一下spring.factories文件:

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注册中心的自动配置类:
NacosServiceRegistryAutoConfiguration。


NacosServiceRegistryAutoConfiguration这个类是Nacos客户端启动时的一个入口类,代码如下:

@Configuration(
    proxyBeanMethods = false
)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(
    value = {"spring.cloud.service-registry.auto-registration.enabled"},
    matchIfMissing = true
)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,
                     AutoServiceRegistrationAutoConfiguration.class, 
                     NacosDiscoveryAutoConfiguration.class})
public class NacosServiceRegistryAutoConfiguration {
    public NacosServiceRegistryAutoConfiguration() {
    }
    @Bean
    public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        return new NacosServiceRegistry(nacosDiscoveryProperties);
    }
    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosRegistration nacosRegistration(ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers, NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
        return new NacosRegistration((List)registrationCustomizers.getIfAvailable(), nacosDiscoveryProperties, context);
    }
    @Bean
    @ConditionalOnBean({AutoServiceRegistrationProperties.class})
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
    }
}


NacosServiceRegistryAutoConfiguration配置类有3个@Bean注解。

  1. nacosServiceRegistry()方法: 定义了NacosServiceRegistry的bean,并且为其属性nacosDiscoveryProperties赋值,即将从配置文件中读取到的配置信息赋值进去待用;
  2. nacosRegistration()方法主要就是定义了NacosRegistration的bean,后面会用到这个bean;
  3. nacosAutoServiceRegistration:该方法比较核心它的参数中有2个就是前面定义的两个bean,其实就是为了这个方法服务的,由NacosAutoServiceRegistration类的构造器传入NacosAutoServiceRegistration类中:NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration),后面的流程都是以这句代码作为入口。

Nacos源码分析

利用IDEA查看类结构,如上图所示,
NacosAutoServiceRegistration继承AbstractAutoServiceRegistration类,而AbstractAutoServiceRegistration类又实现了AutoServiceRegistration和ApplicationListener接口。

ApplicationListener接口是Spring提供的事件监听接口,Spring会在所有bean都初始化完成之后发布一个事件,ApplicationListener会监听所发布的事件,这里的事件是Spring Boot自定义的WebServerInitializedEvent事件,主要是项目启动时就会发布WebServerInitializedEvent事件,然后被
AbstractAutoServiceRegistration监听到,从而就会执行onApplicationEvent方法,在这个方法里就会进行服务注册。

这里
AbstractAutoServiceRegistration类实现了Spring监听器接口ApplicationListener,并重写了该接口的onApplicationEvent方法

public void onApplicationEvent(WebServerInitializedEvent event) {
     this.bind(event);
}

继续点下去看bind方法

 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());
            //start方法
            this.start();
        }
    }

看到这里发现了bind方法里有个非常重要的start()方法,继续看该方法的register()就是真正的客户端注册方法

  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);
            }
        }
    }

跳过一些中间非关键性的代码,可以直接看该注册方法

protected void register() {
   this.serviceRegistry.register(getRegistration());
}

这里的serviceRegistry就是
NacosServiceRegistryAutoConfiguration类中第一个@Bean定义的bean,第一个@Bean就是这里的serviceRegistry对象的实现;其中getRegistration()获取的就是第二个@Bean定义的NacosRegistration的实例,这两个bean实例都是通过第3个@Bean传进来的,所以这里就可以把NacosServiceRegistryAutoConfiguration类中那3个@Bean给串起来了。

public void register(Registration registration) {        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
        } else {
            NamingService namingService = this.namingService();
            String serviceId = registration.getServiceId();
            String group = this.nacosDiscoveryProperties.getGroup();
            //构建客户端参数ip,端口号等
            Instance instance = this.getNacosInstanceFromRegistration(registration);
            try {
                //调用注册方法
                namingService.registerInstance(serviceId, group, instance);
                log.info("nacos registry, {} {} {}:{} register finished", new Object[]{group, serviceId, instance.getIp(), instance.getPort()});
            } catch (Exception var7) {
                log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
                ReflectionUtils.rethrowRuntimeException(var7);
            }
        }
    }
        //构建客户端注册参数
    private Instance getNacosInstanceFromRegistration(Registration registration) {
        Instance instance = new Instance();
        instance.setIp(registration.getHost());
        instance.setPort(registration.getPort());
        instance.setWeight((double)this.nacosDiscoveryProperties.getWeight());
        instance.setClusterName(this.nacosDiscoveryProperties.getClusterName());
        instance.setEnabled(this.nacosDiscoveryProperties.isInstanceEnabled());
        instance.setMetadata(registration.getMetadata());
        instance.setEphemeral(this.nacosDiscoveryProperties.isEphemeral());
        return instance;
    }

不得不说,阿里巴巴开发的中间件,其底层源码的命名还是很规范的,register()方法从命名上来看就可以知道这是注册的方法,事实也确实是注册的方法,这个方法中会通过nacos-client包来调用nacos-server的服务注册接口来实现服务的注册功能。下面我看一下调用Nacos注册接口方法:

 public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
        NamingUtils.checkInstanceIsLegal(instance);
        String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
        if (instance.isEphemeral()) {
            //开启一个异步线程向服务端发送心跳
            BeatInfo beatInfo = this.beatReactor.buildBeatInfo(groupedServiceName, instance);
            this.beatReactor.addBeatInfo(groupedServiceName, beatInfo);
        }
            //调用服务端提供的注册api实现注册
        this.serverProxy.registerService(groupedServiceName, groupName, instance);
    }
    
     public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        LogUtils.NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", new Object[]{this.namespaceId, serviceName, instance});
        //构建客户端参数
        Map<String, String> params = new HashMap(16);
        params.put("namespaceId", this.namespaceId);
        params.put("serviceName", serviceName);
        params.put("groupName", groupName);
        params.put("clusterName", instance.getClusterName());
        params.put("ip", instance.getIp());
        params.put("port", String.valueOf(instance.getPort()));
        params.put("weight", String.valueOf(instance.getWeight()));
        params.put("enable", String.valueOf(instance.isEnabled()));
        params.put("healthy", String.valueOf(instance.isHealthy()));
        params.put("ephemeral", String.valueOf(instance.isEphemeral()));
        params.put("metadata", JacksonUtils.toJson(instance.getMetadata()));
        //调用Nacos提供的api实现注册
        this.reqApi(UtilAndComs.nacosUrlInstance, params, "POST");
    }

根据源码可以知道beatReactor.addBeatInfo()方法作用在于创建心跳信息实现健康检测,Nacos 服务端必须要确保注册的服务实例是健康的,而心跳检测就是服务健康检测的手段。而
serverProxy.registerService()实现服务注册,综上可以分析出Nacos客户端注册流程:

Nacos源码分析

到此为止还没有真正的实现服务的注册,但是至少已经知道了Nacos客户端的自动注册原理是借助了Spring Boot的自动配置功能,在项目启动时通过自动配置类。
NacosServiceRegistryAutoConfiguration将NacosServiceRegistry注入进来,通过Spring的事件监听机制,调用该类的注册方法register(registration)实现服务的自动注册。

Nacos服务发现

1 Nacos客户端客户端服务发现

当Nacos服务端启动后,会先从本地缓存的serviceInfoMap中获取服务实例信息,获取不到则通过NamingProxy调用Nacos服务端获取服务实例信息,最后开启定时任务每秒请求服务端获取实例信息列表进而更新本地缓存serviceInfoMap,服务发现拉取实例信息流程图如下:

相关阅读

  • 做网页的素材有哪些 网页设计制作网站

    做网页的素材有哪些 网页设计制作网站

    关于做网页的素材有哪些和网页设计制作网站的电脑小知识,接下来分享详细内容。 很多小伙伴们在准备做网站的时候通常是先找网站建设公司洽谈需求然后再根据服务商的要求提供对应素材

  • 虚拟DOM的优缺点:深入探讨前端开发工具

    虚拟DOM的优缺点:深入探讨前端开发工具

    小编为大家解答虚拟DOM的优缺点的相关经验,具体内容如下: 虚拟DOM的优点 性能提升 虚拟DOM的最大优点是它能显著提高浏览器的渲染性能。 由于只需要更新差异部分,因此可以减少不必要的

  • 域名ping不通是什么原因 为什么网站ping不通却能访问详解

    域名ping不通是什么原因 为什么网站ping不通却能访问详解

    我们在Ping域名(网址)的时候无法ping通,很多人搞不清楚 域名ping不通是什么原因 引起的,这个有好几种原因,下面IT袋小编就给大家解答下哪些原因会引起网址无法ping通。 疑问解答: 1、域

  • 什么是K8s Kubernetes是什么

    什么是K8s Kubernetes是什么

    小编为大家说一说什么是K8s的相关介绍,具体详情如下: Kubernetes 是一个开源的容器编排引擎,用来对容器化应用进行自动化部署、 扩缩和管理。 该项目托管在 CNCF。 Kubernetes 提供了部署容器