001-Nacos 服务注册

6 篇文章 0 订阅
3 篇文章 0 订阅

Nacos

介绍

Dynamic Naming and Configuration Service
动态的命名和配置服务

反正可以实现注册中心的功能

注册中心架构

服务提供者
服务消费者
注册中心

  1. 提供者告知注册中心可以提供服务的地址
  2. 注册中心注册服务
  3. 消费者拉取服务列表

面临问题

  1. 提供者宕机怎么处理
    Nacos client 中会启动一个线程上报心跳,5s一次
    默认配置下 15s 没有上报心跳则 instance 健康状态切换为 false
    30s 没有上报则直接删除 instance

源码分析

实例注册-接口

Nacos 使用 Java 代码编写其实就是用的 Spring boot
查看官网 Open-API 注册实例接口是:@PostMapping /nacos/v1/ns/instance
在这里插入图片描述

实例注册-入口

com.alibaba.nacos.naming.controllers.InstanceController#register

//读取 request 传输的参数,组装成 instance
final Instance instance = parseInstance(request);
serviceManager.registerInstance(namespaceId, serviceName, instance);
return "ok"; 
registerInstance(namespaceId, serviceName, instance):

//如果缓存中不存在 则创建空的Service
createEmptyService(namespaceId, serviceName, instance.isEphemeral());
//从缓存中读取
Service service = getService(namespaceId, serviceName);
//注册实例
addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);

实例注册-创建一个(Nacos)Service

createEmptyService(namespaceId, serviceName, instance.isEphemeral()):
createServiceIfAbsent(namespaceId, serviceName, local, null):

//根据 namespaceId, serviceName 去缓存中查询一下是否已经有 service 
Service service = getService(namespaceId, serviceName);
if (service == null) {
	//没有则注册
	service = new Service();
	//省略属性赋值
	...
	putServiceAndInit(service);
	//这个local就是是否临时的意思 是临时就返回 true 默认就是true
	if (!local) {
        addOrReplaceService(service);
    }
}
putServiceAndInit(service):

//Map<String, Map<String, Service>>
//key 为 namespaceId; value 为 Map<service.getName(), service>
putService(service);
//从缓存中获取
service = getService(service.getNamespaceId(), service.getName());
//注册一个 心跳检查任务
//维护一个 Map<String, Cluster> clusterMap 执行所有的 value.init()
service.init();
//注册对一个Servce 发生变动的监听
//最后一个参数是 是否是临时节点
String key = KeyBuilder.buildInstanceListKey(namespaceId(), name(), true);
consistencyService.listen(key , service);
key = KeyBuilder.buildInstanceListKey(namespaceId(), name(), false);
consistencyService.listen(key , service);

实例注册-注册(Nacos)Service

addInstance(namespaceId, serviceName, instance.isEphemeral(), instance):

String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
//从缓存中获取
Service service = getService(namespaceId, serviceName);
synchronized (service) {
	//设置一个List<Instance>
	List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
	
	//设置集群相关数据
	Instances instances = new Instances();
    instances.setInstanceList(instanceList);
    consistencyService.put(key, instances);
}

实例注册-设置一个List

//@param ips instances 目前只有一个新的 instance
addIpAddresses(service, ephemeral, ips)updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, ephemeral, ips):

//如果是新增的话 这里应该返回为null
//Datum 字段有:String key 和 (Instances) Record value 
Datum datum = consistencyService.get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));
//所有的 ephemeralInstances 或者 persistentInstances
List<Instance> currentIPs = service.allIPs(ephemeral);
//这里只考虑新增 减少复杂度 修改的话这里会把老的 Instance 放进来 下边也一样 并不在解释修改
Map<String, Instance> instanceMap = new HashMap<>(ips.length);

for (Instance instance : ips) {
	Cluster cluster = new Cluster(instance.getClusterName(), service);
    cluster.init();
    service.getClusterMap().put(instance.getClusterName(), cluster);
}

if (!UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
	Instance oldInstance = instanceMap.get(instance.getDatumKey());
	if (oldInstance != null) {
        instance.setInstanceId(oldInstance.getInstanceId());
    } else {
    	//雪花算法赋值
        instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
    }
    instanceMap.put(instance.getDatumKey(), instance);
    //这里是放入一个写时复制list中在修改的时候 修改副本 这样就不用加锁 提高性能
    return new CopyOnWriteArrayList<>(instanceMap.values());
}

实例注册-List注册

//DistroConsistencyServiceImpl#put
consistencyService.put(key, instances):

onPut(key, value);
//同步数据到其它服务 应该是nacos集群相关内容
distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE, globalConfig.getTaskDispatchPeriod() / 2);
//@param value instances
onPut(key, value):

//根据key判断是否是临时实例 临时和持久化key开头不一样
if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
    Datum<Instances> datum = new Datum<>();
    datum.value = (Instances) value;
    datum.key = key;
    datum.timestamp.incrementAndGet();
    //这一步就是维护到缓存中 Map<String, Datum> dataMap
    dataStore.put(key, datum);
}

//这里的 listeners 在 putServiceAndInit(service) 已经设值了 所以存在
if (!listeners.containsKey(key)) {
    return;
}

//notifier implements Runnable
//addTask 就是把 key 放入 tasks = new ArrayBlockingQueue<>(1024 * 1024) 中
//然后再其 run() 方法中 出队处理
notifier.addTask(key, DataOperation.CHANGE);

实例注册-List注册-Instance 设置到 Service

com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.Notifier#run

//出队
Pair<String, DataOperation> pair = tasks.take();
//清理key
services.remove(datumKey);
//这里就是根据 Key查出之前缓存的 Server Queue
ConcurrentLinkedQueue<RecordListener> recordListeners = listeners.get(datumKey);
for (RecordListener listener : recordListeners) {
	if (action == DataOperation.CHANGE) {
		//这一步在 onPut(key, value): 那边赋值了 datum value 里边存的是 instances
		Datum datum = dataStore.get(datumKey);
		listener.onChange(datumKey, datum.value);
	}
}
com.alibaba.nacos.naming.core.Service#onChange

//更新 instances
updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key));
//重新计算下 md5 放在 checksum 中
recalculateChecksum();
updateIPs(Collection<Instance> instances, boolean ephemeral):

//因为没传对应的集群名称所以就是 默认的 DEFAULT 所以就1个
Map<String, List<Instance>> ipMap = new HashMap<>(clusterMap.size());
for (String clusterName : clusterMap.keySet()) {
    ipMap.put(clusterName, new ArrayList<>());
}
for (Instance instance : instances) {
	if (!clusterMap.containsKey(instance.getClusterName())) {
		//如果是新的 clusterName 就新建 并设置 心跳检查 task
		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);
	}
	//通过对象关联 instance > clusterIPs > ipMap 
	clusterIPs.add(instance);
}

for (Map.Entry<String, List<Instance>> entry : ipMap.entrySet()) {
    //make every ip mine
    List<Instance> entryIPs = entry.getValue();
    //就是更新下 Cluster.ephemeralInstances 字段 如果是持久就是 Cluster.persistentInstances 字段
    clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral);
}
//到这里其实 instance 已经和 Service 关联上了 通过  clusterMap

setLastModifiedMillis(System.currentTimeMillis());
//发布 service 更新事件 ServiceChangeEvent
getPushService().serviceChanged(this);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值