Nacos服务注册源码解析

二、Nacos源码系列:Nacos服务注册流程(一) 本篇文章中,我们从Nacos客户端自动注册在何时触发开始,然后分析了客户端向Nacos服务端提交服务注册请求,最后到Nacos服务端处理这个注册请求,整个流程大概梳理了一下,下一篇文章,我们将会详细分析NotifyCenter这个事件-发布机制的原理,然后继续分析服务端处理注册请求过程中涉及到的后续流程处理。 阅读详情

本章内容

服务注册客户端入口

Nacos客户端依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

基于Springboot自动装备原理,服务启动时会自动加载Nacos客户端相关的配置类。

如图所示:

其中,与服务注册相关的配置类为:NacosServiceRegistryAutoConfiguration。

如图所示:

NacosServiceRegistryAutoConfiguration是Nacos服务注册的自动配置类,该类的主要功能是按条件注入NacosAutoServiceRegistration、NacosServiceRegistry、NacosRegistration等服务注册相关的Bean。

客户端发送服务注册请求

服务注册客户端交互逻辑,如图所示:

NacosAutoServiceRegistration

NacosAutoServiceRegistration继承了AbstractAutoServiceRegistration,该类实现了ApplicationListener接口的onApplicationEvent()方法。

服务启动,Spring初始化完成后,发布对应的初始化完成事件(WebServerInitializedEvent),触发AbstractAutoServiceRegistration.onApplicationEvent()方法调用逻辑,开启注册流程。

源码如下:

@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
   bind(event);
}

@Deprecated
public void bind(WebServerInitializedEvent event) {
   ...
   // 通过CAS方式设置服务端口
   this.port.compareAndSet(0, event.getWebServer().getPort());
   // 开启服务注册流程
   this.start();
}

start()方法主要做以下事情:

  • 发布服务注册相关事件。
  • 执行服务注册。
  • 发布服务注册完成事件。
  • 更新服务运行状态。

源码如下:

public void start() {
   ...
   // 服务启动时,服务运行状态为false
   if (!this.running.get()) {
      // 发布服务预注册事件
      this.context.publishEvent(
            new InstancePreRegisteredEvent(this, getRegistration()));
      // 执行服务注册   
      register();
      if (shouldRegisterManagement()) {
         registerManagement();
      }
      // 发布服务注册完成事件
      this.context.publishEvent(
            new InstanceRegisteredEvent<>(this, getConfiguration()));
      // 更新服务运行状态为true
      this.running.compareAndSet(false, true);
   }
}

register()方法主要做以下事情:

  • 获取服务注册基本信息(如:服务名、服务IP、端口等)。
  • 调用NacosServiceRegistry#register方法执行服务注册。

源码如下:

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

其中,getRegistration()主要作用是从NacosRegistration中获取Nacos服务注册基本信息。

NacosRegistration

NacosRegistration主要用于管理Nacos服务注册基本信息,该类主要封装了NacosDiscoveryProperties的配置信息(application.yml),同时,该类实现了Registration接口(继承自ServiceInstance),Registration是Springcloud提供的服务注册信息通用接口,用于管理服务注册的基本信息(如:服务ID、服务IP、端口等)。

NacosServiceRegistry

NacosServiceRegistry类实现了ServiceRegistry接口,该接口是Springcloud提供的服务注册通用接口,声明了服务注册、取消注册等方法。

NacosServiceRegistry#register方法主要做以下事情:

  • 创建服务实例封装服务注册信息。
  • 调用NamingService#registerInstance方法发起Nacos服务注册。

源码如下:

// com.alibaba.cloud.nacos.registry.NacosServiceRegistry.register
@Override
public void register(Registration registration) {
    // ...
    // 获取Nacos命名服务
    NamingService namingService = namingService();
    // 获取服务名,默认为应用名
    String serviceId = registration.getServiceId();
    // 获取服务分组名,默认为DEFAULT_GROUP
    String group = nacosDiscoveryProperties.getGroup();
    // 创建服务实例,封装服务注册信息(如:服务IP、端口、权重、服务节点类型等)
    Instance instance = getNacosInstanceFromRegistration(registration);
    try {
        // 调用NamingService.registerInstance()方法进行服务注册
        namingService.registerInstance(serviceId, group, instance);
        // ...
    }catch (Exception e) {
        // ...
    }
}

NamingService

NamingService接口定义了服务注册、服务发现等服务注册相关的核心方法,它的默认实现为NacosNamingService。

NacosNamingService#registerInstance方法主要做以下事情:

  • 构建心跳信息,并向Nacos服务端定时(5秒/次)发送心跳请求(临时实例走的是Nacos的AP模式,Nacos服务端需要检测服务的可用性)。
  • 调用NamingProxy#registerService方法向Nacos服务端注册服务实例。

源码如下:

// com.alibaba.nacos.client.naming.NacosNamingService#registerInstance
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 = beatReactor.buildBeatInfo(groupedServiceName, instance);
        // 构建心跳信息,并向Nacos服务端定时(5秒/次)发送心跳请求
        beatReactor.addBeatInfo(groupedServiceName, beatInfo);
    }
    // 向Nacos服务端注册服务实例
    serverProxy.registerService(groupedServiceName, groupName, instance);
}

NamingProxy#registerService方法主要做以下事情:

  • 组装请求参数。
  • 调用服务注册接口(/v1/ns/instance)向Nacos服务端注册服务实例。

源码如下:

// com.alibaba.nacos.client.naming.net.NamingProxy.registerService
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
    // ...
    // 组装请求参数
    final Map<String, String> params = new HashMap<String, String>(16);
    params.put(CommonParams.NAMESPACE_ID, namespaceId);
    params.put(CommonParams.SERVICE_NAME, serviceName);
    params.put(CommonParams.GROUP_NAME, groupName);
    params.put(CommonParams.CLUSTER_NAME, 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服务端服务注册接口(/v1/ns/instance)注册服务实例。
    reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
}

public String reqApi(String api, Map<String, String> params, String method) throws NacosException {
    return reqApi(api, params, Collections.EMPTY_MAP, method);
}
public String reqApi(String api, Map<String, String> params, Map<String, String> body, String method)
throws NacosException {
    return reqApi(api, params, body, getServerList(), method);
}
public String reqApi(String api, Map<String, String> params, Map<String, String> body, List<String> servers,
        String method) throws NacosException {
    // ...
    if (StringUtils.isNotBlank(nacosDomain)) {
        for (int i = 0; i < maxRetry; i++) {
            try {
                // 调用服务注册接口(/v1/ns/instance)向Nacos服务端注册服务实例
                return callServer(api, params, body, nacosDomain, method);
            } catch (NacosException e) {
                // ...
            }
        }
    } else {
        Random random = new Random(System.currentTimeMillis());
        int index = random.nextInt(servers.size());
        for (int i = 0; i < servers.size(); i++) {
            String server = servers.get(index);
            try {
                // 调用服务注册接口(/v1/ns/instance)向Nacos服务端注册服务实例
                return callServer(api, params, body, server, method);
            } catch (NacosException e) {
                // ...
            }
            index = (index + 1) % servers.size();
        }
    }
    // ...
}

服务端服务注册入口

Nacos服务端启动类为nacos-console模块中的com.alibaba.nacos.Nacos,在nacos-console模块中引入了nacos-naming模块,该模块中的com.alibaba.nacos.naming.controllers包中提供了服务注册、服务发现、健康检查等核心接口。如图所示:

服务注册与同步

获取服务最新服务实例列表交互逻辑,如图所示:

InstanceController

Nacos服务端接收客户端发送的服务注册请求后,调用InstanceController#register方法处理服务注册请求。

InstanceController#register方法主要做以下事情:

  • 根据请求参数获取namespaceId,默认为:public。
  • 根据请求参数获取serviceName,格式:groupName@@serviceName。
  • 根据请求参数构建服务实例。
  • 调用ServiceManage#registerInstance方法注册服务实例。

源码如下:

// com.alibaba.nacos.naming.controllers.InstanceController.register
@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
    // 根据客户端请求参数获取namespaceId,默认为:public
    final String namespaceId = WebUtils
    .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
    // 根据客户端请求参数获取serviceName,格式:groupName@@serviceName
    final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    NamingUtils.checkServiceNameFormat(serviceName);
    // 根据客户端请求参数构建服务实例
    final Instance instance = parseInstance(request);
    // 调用ServiceManage#registerInstance方法注册服务实例
    serviceManager.registerInstance(namespaceId, serviceName, instance);
    return "ok";
}

ServiceManage

ServiceManage是Nacos服务的核心管理类,其中定义了服务注册表以及服务相关的各种核心API。如图所示:

ServiceManager#registerInstance方法主要做以下事情:

  • 创建并初始化服务,将其添加到注册表中(第一次注册服务)。
  • 根据namespaceId,、serviceName获取对应的服务。
  • 向服务中添加要注册的服务实例。

源码如下:

public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
    // 创建并初始化服务,将其添加到注册表中(第一次注册服务)
    createEmptyService(namespaceId, serviceName, instance.isEphemeral());
    // 根据namespaceId,、serviceName获取对应的服务
    Service service = getService(namespaceId, serviceName);
    // ...
    // 向服务中添加要注册的服务实例
    addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}

ServiceManager#createEmptyService方法主要做以下事情:

  • 判断服务在注册表中是否存在:
    • 存在,则不做处理。
    • 不存在,则创建服务:
      • 创建服务并将服务添加到注册表中。
      • 开启服务心跳检测。
      • 监听服务实例变更。
      • 如果不是创建本地服务,则根据服务实例类型(临时、持久)选择不同协议进行服务注册与同步。

源码如下:

public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
    createServiceIfAbsent(namespaceId, serviceName, local, null);
}

// com.alibaba.nacos.naming.core.ServiceManager#createServiceIfAbsent
public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
        throws NacosException {
    // 根据namespaceId、serviceName从注册表中获取对应的服务
    Service service = getService(namespaceId, serviceName);
    // 如果服务不存在,则创建并初始化服务
    if (service == null) {
        Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
        // 创建服务
        service = new Service();
        // 设置服务名
        service.setName(serviceName);
        // 设置namespaceId
        service.setNamespaceId(namespaceId);
        // 设置分组名
        service.setGroupName(NamingUtils.getGroupName(serviceName));
        // now validate the service. if failed, exception will be thrown
        service.setLastModifiedMillis(System.currentTimeMillis());
        // 根据服务实例列表生成MD5校验值(如:3c84b68316cb8449ca2842a990e2dd7d)
        service.recalculateChecksum();
        if (cluster != null) {
            cluster.setService(service);
            service.getClusterMap().put(cluster.getName(), cluster);
        }
        // 校验服务名和服务集群名格式
        service.validate();
        // 该方法主要做一下事情:
        //     1.将服务添加到注册表中
        //     2.初始化服务并启动心跳检测(5秒/次)
        //     3.监听服务实例变更
        putServiceAndInit(service);
        // 不是创建本地服务
        if (!local) {
            // 根据服务实例类型(临时、持久)选择不同协议进行服务注册与同步:
            //     临时实例:基于Distro协议将服务信息同步给集群中的其他Nacos节点(即:AP模式)
            //     持久实例:基于Raft协议将服务信息同步给集群中的其他Nacos节点(即:CP模式)
            addOrReplaceService(service);
        }
    }
}

ServiceManager#addInstance方法主要做以下事情:

  • 根据namespaceId、serviceName、实例类型(临时、持久)生成服务唯一标识。
  • 根据namespaceId,、serviceName获取对应的服务。
  • 对服务对象添加同步锁:
    • 获取服务最新的服务实例列表(旧实例列表+待注册实例列表)。
    • 根据服务实例类型(临时、持久)选择不同协议进行服务注册与同步:
      • 临时实例:基于Distro协议将服务信息同步给集群中的其他Nacos节点(即:AP模式)。
      • 持久实例:基于Raft协议将服务信息同步给集群中的其他Nacos节点(即:CP模式)。

源码如下:

// com.alibaba.nacos.naming.core.ServiceManager#addInstance
public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
        throws NacosException {
    // 根据namespaceId、serviceName、实例类型(临时、持久)构建服务唯一标识:
    //      临时实例:com.alibaba.nacos.naming.iplist.ephemeral.namespaceId##groupName@@serviceName
    //      持久实例:com.alibaba.nacos.naming.iplist.namespaceId##groupName@@serviceName
    String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
    // 根据namespaceId,、serviceName获取对应的服务
    Service service = getService(namespaceId, serviceName);
    // 对服务对象添加同步锁
    synchronized (service) {
        // 通过对比待注册实例列表与旧实例列表,获取最新的实例列表(旧实例列表+待注册实例列表)。
        // 注意:此处会将当前服务实例列表拷贝到一个新的服务实例列表中,基于CopyOnWrite思想
        List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
        // 将新的服务实例列表封装为Instances
        Instances instances = new Instances();
        instances.setInstanceList(instanceList);
        // 根据服务实例类型(临时、持久)选择不同协议进行服务注册与同步:
        //      临时实例:基于Distro协议将服务信息同步给集群中的其他Nacos节点(即:AP模式)
        //      持久实例:基于Raft协议将服务信息同步给集群中的其他Nacos节点(即:CP模式)
        consistencyService.put(key, instances);
    }
}

ServiceManager#addIpAddresses->updateIpAddresses方法主要做以下事情:

  • 根据服务信息尝试从数据集(DataStore)中获取旧的服务实例列表。
  • 对比待处理的服务实例列表和旧的服务实例列表:
    • 如果是删除操作,则将服务实例从最新的服务实例列表中移除。
    • 如果是新增或更新操作,则将服务实例加入或更新到最新的服务实例列表中。

源码如下:

private List<Instance> addIpAddresses(Service service, boolean ephemeral, Instance... ips) throws NacosException {
    return updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, ephemeral, ips);
}

public List<Instance> updateIpAddresses(Service service, String action, boolean ephemeral, Instance... ips)
        throws NacosException {
    // 根据服务唯一标识获取当前数据集(dataStore)中旧的服务实例列表,返回值是Datum(key:服务唯一标识,value:服务实例列表)
    Datum datum = consistencyService
            .get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));
    // 根据实例类型(临时、持久)获取现有服务实例列表
    List<Instance> currentIPs = service.allIPs(ephemeral);
    // 创建Map来保存现有服务实例列表,key:服务实例IP+Port,value:服务实例
    Map<String, Instance> currentInstances = new HashMap<>(currentIPs.size());
    // 创建Set来保存现有服务实例的instanceId(实例唯一标识)
    Set<String> currentInstanceIds = Sets.newHashSet();
    // 遍历现有服务实例列表
    for (Instance instance : currentIPs) {
        // 将现有服务实例保存到新创建的Map中
        currentInstances.put(instance.toIpAddr(), instance);
        // 将现有服务实例的instanceId保存到新创建的Set中
        currentInstanceIds.add(instance.getInstanceId());
    }
    // 定义新的Map来保存更新后的服务实例列表
    Map<String, Instance> instanceMap;
    if (datum != null && null != datum.value) {
        // 如果服务存在旧的实例列表,则更新旧实例的健康状态和心跳时间并将其添加到新的Map中
        instanceMap = setValid(((Instances) datum.value).getInstanceList(), currentInstances);
    } else {
        // 如果服务不存在旧的实例列表,则创建新Map
        instanceMap = new HashMap<>(ips.length);
    }
    // 遍历待处理服务实例列表
    for (Instance instance : ips) {
        // 如果待处理服务对应的集群信息不存在,则创建并初始化服务对应的集群信息
        if (!service.getClusterMap().containsKey(instance.getClusterName())) {
            Cluster cluster = new Cluster(instance.getClusterName(), service);
            cluster.init();
            service.getClusterMap().put(instance.getClusterName(), cluster);
            // ... 
        }
        
        if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
            // 如果是删除操作,则将待处理服务实例从Map中移除
            instanceMap.remove(instance.getDatumKey());
        } else {
            // 根据待处理服务信息组成服务唯一标识从旧的服务实例列表中查找
            Instance oldInstance = instanceMap.get(instance.getDatumKey());
            // 如果是旧的服务实例,则更新服务实例信息
            if (oldInstance != null) {
                instance.setInstanceId(oldInstance.getInstanceId());
            } else {
                // 如果是新的服务实例,则新增服务实例信息(即:生成新的instanceId)
                instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
            }
            // 将新的服务实例加入Map中
            instanceMap.put(instance.getDatumKey(), instance);
        }
    }
    // ...
    // 将最新服务实例列表对应的Map转为对应的List,并返回
    return new CopyOnWriteArrayList<>(instanceMap.values());
}

ConsistencyService#put方法为服务注册与同步核心逻辑,该方法主要做以下事情:

根据服务唯一标识前缀判断服务实例类型(临时、持久):

  • com.alibaba.nacos.naming.iplist.ephemeral:临时实例,则基于Distro协议将服务信息同步给集群中的其他Nacos节点(即:AP模式)。
  • com.alibaba.nacos.naming.iplist:持久实例,则基于Raft协议将服务信息同步给集群中的其他Nacos节点(即:CP模式)。
  • 以临时实例为例,处理步骤:
    • 将服务对应的实例列表信息封装成Datum加入到DataStore(作用是根据服务唯一标识获取对应的服务实例列表)中。
    • 将服务唯一标识、操作类型封装成一个Task任务加入到DistroConsistencyServiceImpl.Notifier的任务队列中。
    • 遍历集群中除自己以外的其他Nacos节点:
      • 先将服务信息(资源)、资源类型(如:服务信息同步、元数据同步)以及目标节点封装成DistroKey。
      • 再将DistroKey、操作类型(如:新增、删除实例)封装成延时任务DelayTask(延时时长:1秒)添加到Nacos延时任务执行器的任务集合中。

源码如下:

// com.alibaba.nacos.naming.consistency.DelegateConsistencyServiceImpl#put
public void put(String key, Record value) throws NacosException {
    // 根据服务唯一标识前缀判断服务实例类型(临时、持久)
    mapConsistencyService(key).put(key, value);
}

// com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl#put
@Override
public void put(String key, Record value) throws NacosException {
    // 1.将服务对应的实例列表信息封装成Datum加入到DataStore(作用是根据服务唯一标识获取对应的服务实例列表)中。
    // 2.将服务唯一标识、操作类型封装成一个Task任务加入到DistroConsistencyServiceImpl.Notifier的任务队列中。
    onPut(key, value);
    // 遍历集群中除自己以外的其他Nacos节点:
    //     先将服务信息(资源)、资源类型(如:服务信息同步、元数据同步)以及目标节点封装成DistroKey。
    //     再将DistroKey、操作类型(如:新增、删除实例)封装成延时任务DelayTask(延时时长:1秒)添加到Nacos延时任务执行器的任务集合中。
    distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,
            globalConfig.getTaskDispatchPeriod() / 2);
}

public void onPut(String key, Record value) {
    // 判断是否为临时实例
    if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
        // 将服务信息封装成Datum(key:服务唯一标识,value:服务实例列表)
        Datum<Instances> datum = new Datum<>();
        datum.value = (Instances) value;
        datum.key = key;
        datum.timestamp.incrementAndGet();
        // 将Datum添加到DataStore中
        dataStore.put(key, datum);
    }
    
    if (!listeners.containsKey(key)) {
        return;
    }
    // 向Notifier的任务队列中添加任务
    notifier.addTask(key, DataOperation.CHANGE);
}

public void addTask(String datumKey, DataOperation action) {
    // 如果服务集合中存在服务并且服务操作类型为更新,则直接返回
    if (services.containsKey(datumKey) && action == DataOperation.CHANGE) {
        return;
    }
    // 如果服务操作类型为更新,则将服务添加到服务集合中
    if (action == DataOperation.CHANGE) {
        services.put(datumKey, StringUtils.EMPTY);
    }
    // 向Notifier的任务队列中添加任务
    tasks.offer(Pair.with(datumKey, action));
}

public void sync(DistroKey distroKey, DataOperation action, long delay) {
    // 遍历集群中除自己以外的其他Nacos节点
    for (Member each : memberManager.allMembersWithoutSelf()) {
        // 构建DistroKey,其中:
        //     resourceKey为服务唯一标识
        //     resourceType为com.alibaba.nacos.naming.iplist.(表示同步服务信息)
        //     targetServer为Nacos节点
        DistroKey distroKeyWithTarget = new DistroKey(distroKey.getResourceKey(), distroKey.getResourceType(),
                each.getAddress());
        // 根据创建的DistroKey和操作类型构建Distro延时任务(延时时长:1秒)
        DistroDelayTask distroDelayTask = new DistroDelayTask(distroKeyWithTarget, action, delay);
        // 向延时任务执行器(NacosDelayTaskExecuteEngine)中添加延时任务
        distroTaskEngineHolder.getDelayTaskExecuteEngine().addTask(distroKeyWithTarget, distroDelayTask);
        if (Loggers.DISTRO.isDebugEnabled()) {
            Loggers.DISTRO.debug("[DISTRO-SCHEDULE] {} to {}", distroKey, each.getAddress());
        }
    }
}

服务注册

服务注册交互逻辑,如图所示:

DistroConsistencyServiceImpl初始化时会开启一个线程池DISTRO_NOTIFY_EXECUTOR,其中只有一个线程(即:DistroConsistencyServiceImpl.Notifier),主要作用是通过死循环的方式从任务队列(Notifier.tasks)中取出任务进行处理(即:将服务实例列表更新到注册表中)。

源码如下:

// com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl#init
@PostConstruct
public void init() {
    GlobalExecutor.submitDistroNotifyTask(notifier);
}

// com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.Notifier
public class Notifier implements Runnable {   
    private ConcurrentHashMap<String, String> services = new ConcurrentHashMap<>(10 * 1024);
    private BlockingQueue<Pair<String, DataOperation>> tasks = new ArrayBlockingQueue<>(1024 * 1024);
    // ...
    @Override
    public void run() {
        Loggers.DISTRO.info("distro notifier started");
        for (; ; ) {
            try {
                // 从任务队列中取出任务
                Pair<String, DataOperation> pair = tasks.take();
                // 任务处理
                handle(pair);
            } catch (Throwable e) {
                Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);
            }
        }
    }
    //...
}

DistroConsistencyServiceImpl.Notifier#handle方法主要作用是遍历任务中的服务信息:

  • 如果是更新操作,则更新服务对应的实例列表。
  • 如果是删除操作,则从服务对应的实例列表中删除实例。

源码如下:

private void handle(Pair<String, DataOperation> pair) {
    try {
        // ...
        // 遍历任务中服务信息(Service继承来RecordListener)
        for (RecordListener listener : listeners.get(datumKey)) {
            count++;
            try {
                // 更新操作
                if (action == DataOperation.CHANGE) {
                    // 更新服务对应的实例列表
                    listener.onChange(datumKey, dataStore.get(datumKey).value);
                    continue;
                }
                // 删除操作
                if (action == DataOperation.DELETE) {
                    // 从服务对应的实例列表中删除实例
                    listener.onDelete(datumKey);
                    continue;
                }
            } catch (Throwable e) {
                // ...
            }
        }
        // ...
    } catch (Throwable e) {
        // ...
    }
}

更新操作

更新操作由Service#onChange方法进行处理,该方法主要做以下事情:

  • 更新服务对应的实例列表信息。
  • 更新服务MD5校验码。

源码如下:

public void onChange(String key, Instances value) throws Exception {
    // ...
    // 更新服务对应的实例列表信息
    updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key));
    // 更新服务MD5校验码
    recalculateChecksum();
}

public void updateIPs(Collection<Instance> instances, boolean ephemeral) {
    // 创建Map:用于保存服务对应的Cluster信息
    Map<String, List<Instance>> ipMap = new HashMap<>(clusterMap.size());
    for (String clusterName : clusterMap.keySet()) {
        ipMap.put(clusterName, new ArrayList<>());
    }
    // 遍历服务实例列表
    for (Instance instance : instances) {
        try {
            if (instance == null) {
                continue;
            }
            if (StringUtils.isEmpty(instance.getClusterName())) {
                instance.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);
            }
            // 如果服务实例对应的集群信息不存在,则创建并初始化集群信息
            if (!clusterMap.containsKey(instance.getClusterName())) {
                // ... 日志信息
                Cluster cluster = new Cluster(instance.getClusterName(), this);
                cluster.init();
                getClusterMap().put(instance.getClusterName(), cluster);
            }
            // 如果服务对应的集群实例列表不存在,则创建集群实例列表
            List<Instance> clusterIPs = ipMap.get(instance.getClusterName());
            if (clusterIPs == null) {
                clusterIPs = new LinkedList<>();
                ipMap.put(instance.getClusterName(), clusterIPs);
            }
            // 将服务实例添加到服务集群实例列表中
            clusterIPs.add(instance);
        } catch (Exception e) {
            // ... 日志信息
        }
    }
    
    for (Map.Entry<String, List<Instance>> entry : ipMap.entrySet()) {
        //make every ip mine
        List<Instance> entryIPs = entry.getValue();
        // 更新集群实例列表
        clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral);
    }
    // 设置服务最后更新时间
    setLastModifiedMillis(System.currentTimeMillis());
    // 发布服务信息变更事件(即:将服务变更通知订阅该服务的客户端)
    getPushService().serviceChanged(this);
    // ...
}

// com.alibaba.nacos.naming.core.Cluster#updateIps
public void updateIps(List<Instance> ips, boolean ephemeral) {
    // 根据实例类型(临时、持久)获取旧的服务实例列表
    Set<Instance> toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances;
    // 将旧的服务实例列表转储到新创建Map中(key:服务唯一标识,value:服务实例)
    HashMap<String, Instance> oldIpMap = new HashMap<>(toUpdateInstances.size());
    for (Instance ip : toUpdateInstances) {
        oldIpMap.put(ip.getDatumKey(), ip);
    }
    // 对比待处理服务实例列表和旧服务实例列表,获取需要更新的服务实例列表
    List<Instance> updatedIPs = updatedIps(ips, oldIpMap.values());
    if (updatedIPs.size() > 0) {
        for (Instance ip : updatedIPs) {
            Instance oldIP = oldIpMap.get(ip.getDatumKey());
            if (!ip.isMarked()) {
                ip.setHealthy(oldIP.isHealthy());
            }
            // ...
        }
    }
    // 获取待处理服务实例列表中不存在于旧服务实例列表中的实例(即:新增实例),更新该实例的健康状态为健康
    List<Instance> newIPs = subtract(ips, oldIpMap.values());
    if (newIPs.size() > 0) {
        // ... 日志信息
        for (Instance ip : newIPs) {
            // 更新服务实例健康状态为健康
            HealthCheckStatus.reset(ip);
        }
    }
    // 获取旧服务实例列表中不存在于待处理实例列表中的实例(即:移除实例),更新该实例的健康状态为死亡
    List<Instance> deadIPs = subtract(oldIpMap.values(), ips);
    
    if (deadIPs.size() > 0) {
        // ... 日志信息
        for (Instance ip : deadIPs) {
            // 更新服务实例健康状态为死亡
            HealthCheckStatus.remv(ip);
        }
    }
    
    toUpdateInstances = new HashSet<>(ips);
    // 用最新的服务实例列表覆盖旧的服务实例列表(CopyOnWrite思想)
    if (ephemeral) {
        ephemeralInstances = toUpdateInstances;
    } else {
        persistentInstances = toUpdateInstances;
    }
}
/**
 * 获取需要更新的服务实例列表
 * @param newInstance 待处理服务实例列表
 * @param oldInstance 旧服务实例列表
 * @return
 */
private List<Instance> updatedIps(Collection<Instance> newInstance, Collection<Instance> oldInstance) {
    // 获得待处理服务实例列表和旧服务实例列表的交集
    List<Instance> intersects = (List<Instance>) CollectionUtils.intersection(newInstance, oldInstance);
    // 将交集中的实例存储到stringIpAddressMap中,key:ip+port,value:instance
    Map<String, Instance> stringIpAddressMap = new ConcurrentHashMap<>(intersects.size());
    for (Instance instance : intersects) {
        stringIpAddressMap.put(instance.getIp() + ":" + instance.getPort(), instance);
    }
    // 定义交集Map,key:服务实例,value:标识
    //     1-表示实例只存在于待处理服务实例列表或者旧服务实例列表中
    //     2-表示实例同时存在于待处理服务实例列表和旧服务实例列表中
    Map<String, Integer> intersectMap = new ConcurrentHashMap<>(newInstance.size() + oldInstance.size());
    // 定义需要更新的服务实例Map
    Map<String, Instance> updatedInstancesMap = new ConcurrentHashMap<>(newInstance.size());
    // 定义待处理的服务实例Map
    Map<String, Instance> newInstancesMap = new ConcurrentHashMap<>(newInstance.size());
    // 将旧服务实例列表中实例加入交集Map中,并设置实例的标识为1
    for (Instance instance : oldInstance) {
        if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) {
            intersectMap.put(instance.toString(), 1);
        }
    }
    // 将待处理服务实例列表中实例加入交集Map中,如果实例在交集Map中已经存在,则设置为2(即:替换),否则设置为1(即:新增)
    for (Instance instance : newInstance) {
        if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) {
            
            if (intersectMap.containsKey(instance.toString())) {
                intersectMap.put(instance.toString(), 2);
            } else {
                intersectMap.put(instance.toString(), 1);
            }
        }
        // 将待处理实例加入newInstancesMap中(这一步很重要,后续用于新实例覆盖旧实例)
        newInstancesMap.put(instance.toString(), instance);
    }
    // 遍历交集Map
    for (Map.Entry<String, Integer> entry : intersectMap.entrySet()) {
        String key = entry.getKey();
        Integer value = entry.getValue();
        // 将交集Map中所有标识为1(注意:标识为1的实例包含待处理实例和旧实例)且存在于待处理实例列表中的实例加入最新的服务实例列表中(即:需要更新的旧实例+新实例)
        if (value == 1) {
            if (newInstancesMap.containsKey(key)) {
                updatedInstancesMap.put(key, newInstancesMap.get(key));
            }
        }
    }
    return new ArrayList<>(updatedInstancesMap.values());
}

删除操作

更新操作由Service#onDelete方法进行处理,该方法主要做以下事情:

  • 更新服务对应的实例列表信息(即:从服务实例列表中移除实例)。

源码如下:

@Override
public void onDelete(String key) throws Exception {
    boolean isEphemeral = KeyBuilder.matchEphemeralInstanceListKey(key);
    for (Cluster each : clusterMap.values()) {
        each.updateIps(Collections.emptyList(), isEphemeral);
    }
}
// 其中each.updateIps方法就是更新操作源码中的updateIps方法。

服务信息同步

在前面分析ConsistencyService#put方法时,会将服务信息封装成延时任务DelayTask(延时时长:1秒)添加到Nacos延时任务执行器(NacosDelayTaskExecuteEngine)的任务集合中。

NacosDelayTaskExecuteEngine在创建时会创建一个定时任务去执行NacosDelayTaskExecuteEngine#processTasks方法。

源码如下:

public NacosDelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) {
    super(logger);
    tasks = new ConcurrentHashMap<Object, AbstractDelayTask>(initCapacity);
    // 创建一个单线程的线程池
    processingExecutor = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory(name));
    // 创建定时任务处理延时任务(延时时长:100ms,时间间隔:100ms)
    processingExecutor
            .scheduleWithFixedDelay(new ProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS);
}

// com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine.ProcessRunnable
// 延时任务处理线程
private class ProcessRunnable implements Runnable {
    
    @Override
    public void run() {
        try {
            // 处理任务,如:Nacos集群中节点之间的数据同步
            processTasks();
        } catch (Throwable e) {
            getEngineLog().error(e.toString(), e);
        }
    }
}

NacosDelayTaskExecuteEngine#processTasks方法主要做以下事情:

  • 根据DistroKey获取对应的延时任务。
  • 根据taskKey获取对应的任务处理器。
  • 执行任务处理器的processor()方法对任务进行处理。
  • 任务处理失败,则将任务重新加入到任务集合中进行重试。

源码如下:

protected void processTasks() {
    Collection<Object> keys = getAllTaskKeys();
    for (Object taskKey : keys) {
        // 根据taskKey(DistroKey)获取对应的延时任务
        AbstractDelayTask task = removeTask(taskKey);
        if (null == task) {
            continue;
        }
        // 根据taskKey获取对应的任务处理器,默认为:TaskExecuteWorker
        NacosTaskProcessor processor = getProcessor(taskKey);
        if (null == processor) {
            getEngineLog().error("processor not found for task, so discarded. " + task);
            continue;
        }
        try {
            // ReAdd task if process failed
            // 任务处理失败,则将任务重新加入到任务集合中进行重试。
            if (!processor.process(task)) {
                retryFailedTask(taskKey, task);
            }
        } catch (Throwable e) {
            getEngineLog().error("Nacos task execute error : " + e.toString(), e);
            retryFailedTask(taskKey, task);
        }
    }
}

private void retryFailedTask(Object key, AbstractDelayTask task) {
    task.setLastProcessTime(System.currentTimeMillis());
    addTask(key, task);
}

【作者简介】

一枚热爱技术和生活的老贝比,专注于Java领域,关注【南秋同学】带你一起学习成长~

WPF中使用TreeView封装组合控件TreeView+DataGrid wpf的功能非常强大,很多控件都是原生的,但是要使用TreeView+DataGrid的组合,就需要我们自己去封装实现。功能如上图所示, 目前基本上把常用的样式都实现了,购买源码后,可以自行修改样式。本文源码效果如下,(搞了好几天,的确有难度,所以源码也收费,便宜,赚点辛苦费)第一种:技术的选择是TreeView(也就是本文的演示)。这2个图都是第三方控件自带的,并且都是收费使用。首先说明一下,实现上面的效果,有3种方法。本文演示的是使用TreeView的实现。第二种:技术的选择是DataGrid。 阅读详情

相关推荐

通达信抓波段指标(主图)

VAR3:=IF(INBLOCK('科创板'),VART20,IF(INBLOCK('创业板') AND DATE>1200823,VART20,IF(INBLOCK('ST板块'),VART5,VART10)));VAR5:=IF(INBLOCK('科创板'),CT20,IF(INBLOCK('创业板') AND DATE>1200823,CT20,IF(INBLOCK('ST板块'),CT5,CT10)));曾涨停/紫色细K线:青字"炸",涨停未封住。曾跌停/蓝色细K线:红字"撬",跌停被打开。

q125500612的博客 2135

spring cloud alibaba 学习(二)nacos服务自动注册流程

目录一、NacosServiceRegistryAutoConfiguration注册流程 一、NacosServiceRegistryAutoConfiguration @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties @ConditionalOnNacosDiscoveryEnabled # 默认开启服务自动注册 @ConditionalOnProperty(value = "spring.cloud.s..

码易的博客 1284

Android应用加固脱壳实战:从爱加密原理到Frida内存Dump与修复

在移动应用安全领域,代码加固与动态加载是保护核心逻辑的关键技术。其原理在于通过修改应用启动流程,将原始DEX加密隐藏,并注入包含解密逻辑的壳DEX,实现运行时动态加载。这项技术的核心价值在于防止应用被逆向分析、二次打包或恶意篡改,广泛应用于金融、游戏等高安全要求场景。对于安全研究、漏洞挖掘或历史版本审计,掌握脱壳技能至关重要。本文聚焦于**爱加密**等主流加固方案,深入探讨如何利用**Frida**框架在内存中精准捕获解密后的DEX数据,并解决脱壳后常见的签名校验等运行问题,提供一套从原理到实践的完整攻防思

weixin_28716443的博客 490

Nacos整合SpringCloud的自动注册原理

一.问题引出 单独的nacos项目中分为client端和server端,client端会调用register去向server端注册,但是我们在做微服务的时候引入的并不是单独的nacos客户端的jar包,而是springcloud alibaba nacos这个jar包,当我们引入这个jar包之后,就可以在项目启动的时候自动地往nacos服务端去注册这个服务了,那么springcloud alibaba nacos这个包能做到自动注册的原理是什么呢? 二.nacos,springcloud ali

我神级欧文的博客 1971

Keil5安装包别乱下!手把手教你从官网精准下载C51/V960和MDK5,并完成2032年有效注册(避坑网络资源失效)

本文详细介绍了如何从Keil官网安全下载C51 V9.60和MDK5开发环境,并提供2032年有效注册的完整指南。通过验证安装包完整性、自定义安装路径和长期激活方案,帮助开发者避开网络资源陷阱,搭建稳定的STM32开发环境。

weixin_26886855的博客 530

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(四)-无人机系统(UAS)命令与控制(C2)通信用例

UTM向5G网络提供无人机系统C2通信所需的信息,例如服务类别、无人机系统服务的流量类型、授权的无人机系统服务所需的QoS以及无人机系统服务的订阅。遵循模型B的网络辅助C2模型,为了确保UAS操作的C2通信的服务可用性和可靠性,特别是当无人机在操作员的视线之外(BLOS)飞行时,为无人机提供支持冗余的C2通信链路。如果需要创建额外的C2通信连接或更改现有数据连接的配置以进行C2通信,则5G网络会根据无人机系统的授权服务信息和C2通信中所需的QoS和优先级,修改或分配一个或多个C2通信流量的QoS流。

u011376987的博客 2051

Nacos 注册中心 - 服务注册源码

订阅服务的意思是:订阅服务的变更,如果数据变更了,NacosServer 端会推送事件过来,这样 Client 的数据就会是最新的了。好了,这里已经完成了可靠性的保证,定时任务 RedoScheduledTask 会定时扫描 缓存 map 里的数据并做处理。在微服务架构中,服务之间的调用非常频繁,而且调用链路很长,因此需要对服务之间的调用进行跟踪和监控,以便及时发现和解决问题。这里也就是说,RPC 请求发送完成后,将缓存中的数据做了一个改动,意思就是代表 缓存的这条数据 是正常的(不需要重试了)

深度 1518

[OrCad电路板设计系统].Orcad.Pspice.9.2下载安装

Orcad.Pspice.9.2详细下载安装图文步骤1 安装前准备工作1.1 下载安装包1.2 解压2 安装时步骤2.1 安装文件2.2 破解文件3 安装完成 1 安装前准备工作 1.1 下载安装包 打开百度网盘APP 链接:https://pan.baidu.com/s/1cMFxHUvuh5iEhFVT-lvciQ 提取码:7m65 1.2 解压 解压下载好的安装包 2 安装时步骤 2.1 安装文件 1.点击Setup安装文件,出现警告对话框,告诉你应该关闭所有病毒防火墙,点击确定 2.进入安装界面,

weixin_48924581的博客 1万+

Nacos服务端源码分析——服务注册

【代码】Nacos源码——服务端注册。

weixin_45270863的博客 697

深度强化学习训练结果曲线绘制教程

使用python语言,只调用matplotlib库,绘制训练过程中保存的强化学习训练曲线。模仿经典论文中的训练曲线,多次训练取平均,并绘制出阴影区域。

加斯顿工程师的博客 910

【ComfyUI】Wan2.2 Animate 背景保留动作迁移视频生成

本文介绍了一个基于ComfyUI的高精度视频生成工作流"Wan2.2 Animate",该系统融合动作捕捉、图像拼接和LoRA加权等技术,能自动识别人脸并完成精准动作迁移,同时保持背景稳定。工作流采用模块化设计,包含输入提取、图像融合、模型加载等关键节点,核心模型Wan2.2-Animate-14B支持fp8精度与BlockSwap控制。流程分为图像输入、动作提取、多图合成等五个阶段,通过文本提示编码器和图像语义编码器实现语义驱动的动作生成,最终输出高质量视频,适用于虚拟形象驱动和

Mr数据杨 2260

Nacos:Nacos服务注册与服务发现超详细的源码解析(二)

客户端:启动则获取自身配置信息,发起http请求注册,临时实例同时会开启心跳机制(下面会说),服务端是单机的情况下请求失败会重试三次,服务端是单机的集群的情况下请求失败会轮询请求服务端本地通过一个Map保存所有服务信息,注册的实质就是往map里面添加信息会先创建空的服务,后更新服务中的实例信息服务创建后会初始化服务,启动心跳检测往服务中添加实例的时候会判断实例是永久实例还是临时实例,不同类型的实例有不同的处理方式注册后同时会发布服务变更事件(后面说,先记着这个事件)

weixin_51360020的博客 1170

【测开求职】面试题:数据结构 吐血整理

本篇文章是笔者为准备研究生复试以及校招而写,因此主要针对问答,注重理解,而不在于细节计算。涵盖考研范围内的所有知识内容,读者可以先查看上面的链接走一遍数据结构的整体框架结构。其中,重点难点以及易混淆的知识点,笔者用其他近30篇文章进行了详述,在知识点处贴好了链接,有需要的可以查阅。

vector的博客 7065

nacos单机服务注册源码解析

nacos单机服务注册源码解析

m0_54258715的博客 691

1708张训练图,打电话玩手机数据集,可识别驾驶过程中是否接打电话手机,支持yolov8

1708张训练图,打电话玩手机数据集,可识别驾驶过程中是否接打电话手机,支持yolov8

风光储孤网型微网系统MATLAB仿真(MPPT采用S函数编写)

风、光、储孤网系统;单机,波形正确;各部分控制齐全。风机与光伏的MPPT控制均采用S函数编写,可以采用算法改进!!!!!!!!风机侧采用永磁同步风机,通过升压电路接入逆变器;光伏模块包括光伏电池模块、通过升压电路接入逆变器;储能部分为蓄电池及充放电控制。使用MATLAB2020a打开!!!!!!!!!!!!!!!!!!!!!!!

Nacos源码解析(一) - 服务注册

Nacos服务注册

qq_40344505的博客 1451

基于安卓AndroidStudio校园求职招聘app设计毕业设计源码.zip

1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。--------下载后请首先打开README.md文件(如有),仅供学习参考。

【Nacos】Nacos服务注册与发现 心跳检测机制源码解析

【Nacos】Nacos服务注册与发现 心跳检测机制源码解析

芝兰生于深谷,不以无人而不芳 3876

图像合集数据集(红外、可见光、高光谱、sar图像).rar

1、资源描述:图像合集数据集(红外、可见光、高光谱、sar图像).rar2、资源内容:图像合集数据集已配好3、适用对象:计算机,电子信息工程、数学等专业的大学生课程设计和毕业设计。4、更多数据集合和仿真源码下载列表(自行寻找自己需要的):https://blog.youkuaiyun.com/m0_62143653?type=download5、作者介绍:某大厂资深算法工程师,从事Matlab、Python、C/C++、Java、YOLO算法仿真工作10年;擅长计算机视觉、目标检测模型、智能优化算法、神经网络预测、信号处理、元胞自动机、图像处理、智能控制、路径规划、无人机等多种领域的算法仿真实验,欢迎交流学习。

Nacos源码之服务注册源码解析

文章目录前言主要类图客户端逻辑服务端逻辑 前言 本篇就开始探索Nacos的服务注册逻辑,建议大家准备两个项目,一个是Nacos的源码项目,一个是整合Nacos注册中心的业务项目。探索过程也是分为客户端和服务端的代码解析,我觉得这样应该更利于代码跟进和理解,篇幅过长,还请耐心观看。 主要类图 客户端逻辑 首先来看客户端,也就是我们的项目在启动时,Nacos依赖偷偷干了哪些见不得人的事情。打开项目依赖,找到引入的注册中心依赖,我们知道SpringBoot项目启动时会读取spring.factories文件进行

从入门到脱发 2206

Nacos源码-最详细讲解服务注册源码解析-客户端篇

nacos服务注册源码分析 最近在学习Nacos源码,本文会就客户端向Nacos服务注册整个流程做讲解,细化到方法的维度。整个调用链路在文章结尾,本人手绘了一张图,还算比较清晰明了了,有不对的地方欢迎指正。 spring-cloud-starter-alibaba-nacos-discovery 一切从pom文件引入的spring-cloud-starter-alibaba-nacos-discovery包入手,查找META_INF下的spring.fa...

a771664696的博客 1086

05 Nacos 服务注册源码解析:服务注册

从本文开始,就正式进入 Nacos 服务注册源码分析啦!至于为什么要把它放在配置中心之后来分析呢?原因有二:1️⃣ 从我自身项目经验来看,平时开发过程中很少会遇到服务注册相关的问题,大部分的问题还是配置相关问题,比如为什么改了配置没有自动更新等等。2️⃣ 注册中心的难度和复杂度更大,涉及到的异步操作更多。当然,本系列文章的分析也只是入门级的,主要还是考虑到自身能力水平有限😭。说了这么多,现在就开始吧!!!

michaellee312557478的博客 881

深度解析nacos源码之注册中心(服务注册)

原创不易,转载请注明出处 本文基于nacos1.4.0 文章目录前言1.服务注册client端2.服务注册server端 前言 1.服务注册client端 2.服务注册server端

猿上生活 3058

Nacos源码与原理 01,Nacos 源码解析:服务注册的核心流程与核心数据结构

本文深入剖析了Nacos服务注册的核心实现机制。首先解析了Nacos的三层模型(服务-集群-实例)数据结构设计,重点阐述了临时实例(ephemeral)与持久化实例的差异化存储策略。随后详细梳理了从客户端构造实例、发起注册请求,到服务端参数校验、业务处理、数据存储及集群同步的完整流程。Nacos通过内存+Raft协议保证临时实例的高性能与一致性,采用数据库+缓存机制确保持久化实例的可靠性,其分层架构设计体现了微服务注册中心高性能、高一致性和高扩展性的核心原则。

码路同行 1489

【微服务架构】SpringCloud Alibaba(一):Nacos作为注册中心(注册中心原理、源码解析)

Spring Cloud Alibaba是阿里巴巴提供的一站式微服务解决方案,是Spring Cloud体系中的一个重要分支,它将阿里巴巴在微服务领域的实践经验和开源技术进行了整合,为开发者提供了一系列便捷的工具和组件,用于构建分布式微服务应用。以下是其详细介绍:首先服务在启动的时候会将数据注册到注册中心,这样就服务只要启动就可以对外提供服务了, 那么同时他也要维持一个心跳,心跳的意义就是某个服务挂掉,我肯定让注册中心知道,如果服务挂了还没有告诉注册中心,

专注上位机开发、机器视觉、机器学习、嵌入式软硬件开发 1万+

一 nacos注册中心源码解析--服务注册

Nacos的注册中心是一个服务注册与发现组件,它包含了NacosServer 服务端(也叫注册中心)和NacosClient客户端两部分组成,NacosServer 是独立的服务,而NacosClient需要集成到每个微服务中。NacosClient(具体的微服务)在项目初始化的时候,会主动的把自己注册到注册中心(NacosServer)提交自己的服务名,ip,端口等,在 NacosServer会形成一个服务注册列表.微服务(NacosClient,通常是消费者服务)会定期(默认10s/次)从NacosSe

Instanceztt的博客 556

万字长文 | Spring Cloud Alibaba组件之Nacos实战及Nacos客户端服务注册源码解析

.markdown-body { color: rgba(89, 89, 89, 1); font-size: 15px; font-family: -apple-system, system-ui, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-se...

程序员小明1024 681

Nacos 服务注册源码解析

nacos官方提供额架构图: Provider App:服务提供者 Consumer App:服务消费者 Name Server:通过VIP(Vritual IP)或者DNS的方式实现Nacos高可用集群的服务路由 Nacos Server:Nacos服务提供者,包含OpenApi访问入口,Config Service 和 Naming Service是Nacos的配置服务,名字服务模块。Consistency Protocol是一致性协议,用来实现Nacos集群接节点的数据同步,使用的是 Ra

liu1390910的博客 1725
上一篇: MySQL如何保证数据不丢失
下一篇: 「一文搞懂」MySQL缓冲池(buffer pool)
南秋同学
博客等级 码龄7年 64粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值