目录
Nacos
介绍
Dynamic Naming and Configuration Service
动态的命名和配置服务
反正可以实现注册中心的功能
注册中心架构
服务提供者
服务消费者
注册中心
- 提供者告知注册中心可以提供服务的地址
- 注册中心注册服务
- 消费者拉取服务列表
面临问题
- 提供者宕机怎么处理
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);