文章 62
浏览 15135
Alibaba Nacos注册中心源码剖析

Alibaba Nacos注册中心源码剖析

nacos 简介

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

下载源码(1.4)

从 GitHub 下载 nacos 的 1.4 版本的源代码

流程架构图

image.png

架构原理

1、微服务系统在启动时将自己注册到服务注册中心,同时外发布 Http 接口供其它系统调用(一般都是基于 Spring MVC)

2、服务消费者基于 Feign 调用服务提供者对外发布的接口,先对调用的本地接口加上注解 @FeignClient,Feign 会针对加了该注解的接口生成动态代理,服务消费者针对 Feign 生成的动态代理去调用方法时,会在底层生成 Http 协议格式的请求,类似 /stock/deduct?productId=100

3、Feign 最终会调用 Ribbon 从本地的 Nacos 注册表的缓存里根据服务名取出服务提供在机器的列表,然后进行负载均衡并选择一台机器出来,对选出来的机器 IP 和端口拼接之前生成的 url 请求,生成调用的 Http 接口地址 http://192.168.0.60/stock/deduct?productId=100,最后基于 HTTPClient 调用请求

Nacos 架构图

image.png

nacos 的核心功能点

服务注册

Nacos Client 会通过发送 REST 请求的方式向 Nacos Server 注册自己的服务,提供自身的元数据,比如 ip 地址、端口等信息。Nacos Server 接收到注册请求后,就会把这些元数据信息存储在一个双层的内存 Map 中。

@CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {
    
        final String namespaceId = WebUtils
                .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
        final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        NamingUtils.checkServiceNameFormat(serviceName);
    
        final Instance instance = parseInstance(request);
    
        serviceManager.registerInstance(namespaceId, serviceName, instance);
        return "ok";
    }

nacos 客户端在程序启动的时候,会向 nacos 服务器发送 http 请求,封装 service 对象,创建一个空的 service,然后根据客户端配置的 ephemeral 是否是临时节点,对应的是 AP 还是 CP,在 init 方法中,将元数据信息放在

Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();

最后异步阻塞队列,在 addTask 不断获取,将元数据信息更新到注册表中

服务心跳

在服务注册后,Nacos Client 会维护一个定时心跳来持续通知 Nacos Server,说明服务一直处于可用状态,防止被剔除。默认 5s 发送一次心跳。

服务健康检测

Nacos Server 会开启一个定时任务用来检查注册服务实例的健康情况,对于超过 15s 没有收到客户端心跳的实例会将它的 healthy 属性置为 false(客户端服务发现时不会发现),如果某个实例超过 30 秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)

服务同步

Nacos Server 集群之间会互相同步服务实例,用来保证服务信息的一致性。

public void sync(DistroKey distroKey, DataOperation action, long delay) {
        for (Member each : memberManager.allMembersWithoutSelf()) {
            DistroKey distroKeyWithTarget = new DistroKey(distroKey.getResourceKey(), distroKey.getResourceType(),
                    each.getAddress());
            DistroDelayTask distroDelayTask = new DistroDelayTask(distroKeyWithTarget, action, delay);
            distroTaskEngineHolder.getDelayTaskExecuteEngine().addTask(distroKeyWithTarget, distroDelayTask);
            if (Loggers.DISTRO.isDebugEnabled()) {
                Loggers.DISTRO.debug("[DISTRO-SCHEDULE] {} to {}", distroKey, each.getAddress());
            }
        }
    }

服务发现

服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个 REST 请求给 Nacos Server,获取上面注册的服务清单,并且缓存在 Nacos Client 本地,同时会在 Nacos Client 本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存

public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
  
        NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
        String key = ServiceInfo.getKey(serviceName, clusters);
        if (failoverReactor.isFailoverSwitch()) {
            return failoverReactor.getService(key);
        }
  
        ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
  
        if (null == serviceObj) {
            serviceObj = new ServiceInfo(serviceName, clusters);
  
            serviceInfoMap.put(serviceObj.getKey(), serviceObj);
  
            updatingMap.put(serviceName, new Object());
            updateServiceNow(serviceName, clusters);
            updatingMap.remove(serviceName);
  
        } else if (updatingMap.containsKey(serviceName)) {
  
            if (UPDATE_HOLD_INTERVAL > 0) {
                // hold a moment waiting for update finish
                synchronized (serviceObj) {
                    try {
                        serviceObj.wait(UPDATE_HOLD_INTERVAL);
                    } catch (InterruptedException e) {
                        NAMING_LOGGER
                                .error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
                    }
                }
            }
        }
  
        scheduleUpdateIfAbsent(serviceName, clusters);
  
        return serviceInfoMap.get(serviceObj.getKey());
    }

Nacos 服务注册表结构:

Map< nameSpace ,Map < group:serviceName,Service>>

image.png

nacos 全架构图

image.png


标题:Alibaba Nacos注册中心源码剖析
作者:xiaohugg
地址:https://xiaohugg.top/articles/2023/11/04/1699108367942.html

人民有信仰 民族有希望 国家有力量