nacos的见解
Nacos是一款阿里巴巴开源的分布式微服务管理中间件,它提供了服务注册与发现、服务配置、服务元数据及流量管理等核心功能。Nacos作为注册中心,主要功能包括:
- 服务注册与发现:动态增加或减少服务节点,并实时通知服务消费者,无需消费者主动更新配置。
- 服务配置:动态修改服务配置,并推送到服务提供者和服务消费者,无需重启服务。
- 健康检查和服务摘除:主动检查服务健康状况,对于宕机的服务将其从服务列表中摘除。
-
通讯协议
整个服务注册与发现过程,都离不开通讯协议,在1.x的 Nacos 版本中服务端只支持 http 协议,后来为了提升性能在2.x版本引入了谷歌的 grpc,grpc 是一款长连接协议,极大的减少了 http 请求频繁的连接创建和销毁过程,能大幅度提升性能,节约资源。据官方测试,Nacos服务端 grpc 版本,相比 http 版本的性能提升了9倍以上。并且使用protocol buff数据结构,优点
- 体积更小,序列化和传输的速度更快
- 使用相对简单维护成本低兼容性好
- 跨平台,跨语言
-
Nacos注册表结构
Nacos注册表的整体结构采用了多层Map分级的存储模型,包括以下几个主要部分:- Namespace:用于隔离不同的环境和应用。
- Group:用于将服务进行分组以便管理。
-
Service:每个服务可以包含多个实例,这些实例可能位于不同的物理机房或数据中心。
- 每个服务下的一个子结构是Cluster,它代表服务的某个部署组。
- Cluster下进一步分为Instance,即具体的服务实例。
// Map(namespace,Map(group::serviceName, Service)). private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
-
Nacos服务注册
简单来讲,服务注册的目的就是客户端将自己的ip端口等信息上报给 Nacos 服务端,过程如下:
- 创建长连接
Nacos SDK 通过Nacos服务端域名解析出服务端ip列表,grpc
- 发起注册
当你查看Nacos java SDK的注册方法时,你会发现没有返回值,这是因为Nacos SDK做了补偿机制,在真实给服务端上报数据之前,会先往缓存中插入一条记录表示开始注册,注册成功之后再从缓存中标记这条记录为注册成功,当注册失败时,缓存中这条记录是未注册成功的状态,,每个Nacos节点之间会定时互相发送Distro数据,以此保证数据最终一致。
- 服务实例上线推送:
grpc其中1.x版本的SDK通过http协议来定时向服务端发送心跳维持自己的健康状态,2.x版本的SDK则通过自身的心跳机制来保活,当Nacos服务端接受不到服务实例的心跳,会认为实例下线
- 客户端:1.x版本,在注册实例的时候会为临时节点生成一个默认5s的BeatTask心跳定时任务,BeatTask定时任务会调用
NamingProxy#sendBeat
向服务端发起心跳请求。 - 服务端:检测到15秒没有心跳,就会标记为不健康,然后30秒没有心跳就会剔除服务。
public class ConnectionBasedClientManager extends ClientConnectionEventListener implements ClientManager {
//连接断开,发送连接断开事件
public boolean clientDisconnected(String clientId) {
Loggers.SRV_LOG.info("Client connection {} disconnect, remove instances and subscribers", clientId);
ConnectionBasedClient client = clients.remove(clientId);
if (null == client) {
return true;
}
client.release();
NotifyCenter.publishEvent(new ClientEvent.ClientDisconnectEvent(client));
return true;
}
}
-
Nacos服务订阅
当一个服务发生上下线,Nacos如何知道要推送给哪些客户端?
Nacos SDK 提供了订阅和取消订阅方法,当客户端向服务端发起订阅请求,服务端会记录发起调用的客户端为该服务的订阅者,同时将服务的最新实例列表返回。当客户端发起了取消订阅,服务端就会从该服务的订阅者列表中把当前客户端移除。
当客户端发起订阅时,服务端除了会同步返回最新的服务实例列表,还会异步的通过grpc推送给该订阅者最新的服务实例列表,这样做的目的是为了异步更新客户端本地缓存的服务数据。
当客户端订阅的服务上下线,该服务所有的订阅者会立刻收到最新的服务列表并且将服务最新的实例数据更新到内存。
-
Nacos服务发现
客户端支持订阅获取实例和定时任务主动查询。实例信息会缓存到磁盘缓存中。
服务消费者首先需要调用Nacos SDK的接口来获取最新的服务实例,然后才能从获取到的实例列表中以加权轮询的方式选择出一个实例(包含ip,port等信息),最后再发起调用。
前面已经提到Nacos服务发生上下线、订阅的时候都会推送最新的服务实例列表到当客户端,客户端再更新本地内存中的缓冲数据,所以调用Nacos SDK提供的查询实例列表的接口时,不会直接请求服务端获取数据,而是会优先使用内存中的服务数据,只有内存中查不到的情况下才会发起订阅请求服务端数据。Nacos SDK内存中的数据除了接受来自服务端的推送更新之外,自己本地也会有一个定时任务定时去获取服务端数据来进行兜底。Nacos SDK在查询的时候也了容灾机制,即从磁盘获取服务数据,而这个磁盘的数据其实也是来自于内存,有一个定时任务定时从内存缓存中获取然后加载到磁盘。
-
Nacos的CP和AP
Spring Cloud Alibaba Nacos 在 1.0.0 正式支持 AP 和 CP 两种一致性协议,其中默认的是AP,临时实例来说,Nacos会优先保证可用性,也就是AP。对于永久实例,Nacos会优先保证数据的一致性,也就是CP。
-
Nacos的AP实现
对于AP来说,Nacos使用的是阿里自研的Distro协议。在这个协议中,每个服务端节点是一个平等的状态,每个服务端节点正常情况下数据是一样的,每个服务端节点都可以接收来自客户端的读写请求。
Distro 协议被定位为 临时数据的一致性协议 :该类型协议, 不需要把数据存储到磁盘或者数据库 ,因为临时数据通常和服务器保持一个session会话, 该会话只要存在,数据就不会丢失 。
-
Nacos的CP实现
Nacos的CP实现是基于Raft算法来实现的
在1.x版本早期,Nacos是自己手动实现Raft算法
在2.x版本,Nacos移除了手动实现Raft算法,转而拥抱基于蚂蚁开源的JRaft框架
在Raft算法,每个节点主要有三个状态
- Leader,负责所有的读写请求,一个集群只有一个
- Follower,从节点,主要是负责复制Leader的数据,保证数据的一致性
- Candidate,候选节点,最终会变成Leader或者Follower
为什么说Raft是保证CP的呢?
主要是因为Raft在处理写的时候有一个判断过程
- 首先,Leader在处理写请求时,不会直接数据应用到自己的系统,而是先向所有的Follower发送请求,让他们先处理这个请求
- 当超过半数的Follower成功处理了这个写请求之后,Leader才会写数据,并返回给客户端请求处理成功
- 如果超过一定时间未收到超过半数处理成功Follower的信号,此时Leader认为这次写数据是失败的,就不会处理写请求,直接返回给客户端请求失败
所以,一旦发生故障,导致接收不到半数的Follower写成功的响应,整个集群就直接写失败,这就很符合CP的概念了。
-
Nacos几个思考
- Nacos是如何撑起十万服务器的高并发注册?
Nacos 内部接收到注册请求后 1.不会立即写入数据库,而是将注册信息封装成注册任务。 2.并将注册任务放入一个阻塞队列后,立即返回客户端。 3.后台会利用线程池读取阻塞队列中的人物,异步来完成实例更新。从而提高并发能力。- Nacos如何避免并发读写冲突问题?
Nacos在更新实例列表时,会采用CopyOnWrite技术,首先将旧的实例列表拷贝一份,然后更新拷贝的实例列表,再用更新后的实例列表来覆盖旧的实例列表。这样在更新的过程中,就不会对读实例列表的请求产生影响,也不会出现脏读问题了。然后在写实例的时候,会根据实例id去锁synchronized方法块实现,基于service 添加一个同步锁,一旦加了同步锁,对于单个服务内的多个实例,它就只能串行执行 了,这样就可以避免并发的写冲突问题了【不同的服务相互之间 就不会有影响了 】这样对服务加锁 的形式就保证了 同一个 服务的多个实例只能串行执行但是说到底还是一种 对Service 的加锁,即map 的一部分, → 锁的是 局部资源。从而 让不同服务之间可以 并行写,这样性能影响不大,又保证了安全。- Nacos与eureka的区别?
Nacoseureka集群模式支持CP、AP,默认AP模式AP模式:强调数据的可用性,CP模式:强调数据的可靠性和一致性。只支持AP模式通讯方式1.x版本http通讯、使用protocol buff数据结构2.x版本使用grpc长链接通讯http通讯,使用jesery通讯实例类型支持临时实例和持久化实例只有临时实例服务注册实例启动时候,向server注册,server之间相互同步健康机制Nacos对临时实例采用心跳模式,对持久实例采用主动请求检查只支持心跳模式服务发现支持定时任务主动拉取、订阅推送两种模式只支持主动拉取缓存机制使用CopyOnWrite技术,首先将旧的实例列表拷贝一份,然后更新拷贝的实例列表,再用更新后的实例列表来覆盖旧的实例列表使用只读缓存、读写缓存、注册实例多级缓存实现高并发