源码解析-Nacos注册中心(AP架构)
一、Nacos核心功能点
服务注册:Nacos Client 会通过发送 REST 请求的方式向 Nacos Server 注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。Nacos Server 接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。
服务心跳:在服务注册后,Nacos Client 会维护一个定时心跳来持续通知 Nacos Server,说明服务一直处于可用状态,防止被剔除。默认5s发送一次心跳。
服务健康检查:Nacos Server 会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将它的 healthy 属性置为 false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)
服务发现:服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个 REST 请求给 Nacos Server,获取上面注册的服务清单,并且缓存在 Nacos Client 本地,同时会在 Nacos Client 本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存。
服务同步:Nacos Server 集群之间会互相同步服务实例,用来保证服务信息的一致性。
Nacos服务注册表结构:Map<namespace, Map<group::serviceName, Service>>
nacos 可部署一套对应多个环境,比如生产、开发、测试等,用 namespace 来区分。
举例说明:
二、Nacos服务注册与发现
2.1 AP
ephemeralInstances 临时实例注册,存在内存 BlockingQueue。使用异步队列设计,满足高并发。
2.2 CP
persistentInstances 持久实例注册,存在文件
三、Nacos注册表如何防止多节点读写并发冲突
写时复制(读写分离)
不采用加锁的设计,可以保证高并发。当要更新实例时,先将内存中的注册表实例复制出来,跟要更新的实例对比,如果不一致,则去更新注册表中的实例。
四、Nacos心跳机制与服务健康检查源码剖析
nacos 客户端使用 ScheduledExecutorService 每隔5秒(默认)向服务端发送心跳。
五、Nacos心跳在集群架构下的设计原理剖析
如果 Nacos 是集群的状态,并不是集群中的每节点机器都会检测所有的 Instance 的心跳,而是用算法来计算每个节点机器需要检测那些 Instances,我们来看看源码中是如何计算的:
通过 serviceName 计算出一个 Hash 值然后跟 Nacos 集群中所有机器数量进行取模,对得到的结果进行判断,是否采用当前的Nacos Server节点进行心跳检测。
public boolean responsible(String serviceName) { final List<String> servers = healthyList; // 如果采用独立模式启动,直接返回true if (!switchDomain.isDistroEnabled() || EnvUtil.getStandaloneMode()) { return true; } if (CollectionUtils.isEmpty(servers)) { // means distro config is not ready yet return false; } // 获取当前Nacos Server节点在所有节点列表的第一个index int index = servers.indexOf(EnvUtil.getLocalAddress()); // 获取当前Nacos Server节点在所有节点列表的最后个index int lastIndex = servers.lastIndexOf(EnvUtil.getLocalAddress()); if (lastIndex < 0 || index < 0) { return true; } // 把serviceName进行Hash操作,然后跟service size进行取模 // 取模后的结果在index和lastIndex之间,那么就返回true int target = distroHash(serviceName) % servers.size(); return target >= index && target <= lastIndex; }