今天就应某位小伙伴的要求,来讲一讲Nacos作为服务注册中心底层的实现原理
不知你是否跟我一样,在使用Nacos时有以下几点疑问:
- 临时实例和永久实例是什么?有什么区别?
- 服务实例是如何注册到服务端的?
- 服务实例和服务端之间是如何保活的?
- 服务订阅是如何实现的?
- 集群间数据是如何同步的?CP还是AP?
- Nacos的数据模型是什么样的?
本文就通过探讨上述问题来探秘Nacos服务注册中心核心的底层实现原理。虽然Nacos最新版本已经到了2.x版本,但是为了照顾那些还在用1.x版本的同学,所以本文我会同时去讲1.x版本和2.x版本的实现
临时实例和永久实例
临时实例和永久实例在Nacos中是一个非常非常重要的概念,之所以说它重要,主要是因为我在读源码的时候发现,临时实例和永久实例在底层的许多实现机制是完全不同的
临时实例
临时实例在注册到注册中心之后仅仅只保存在服务端内部一个缓存中,不会持久化到磁盘,这个服务端内部的缓存在注册中心届一般被称为服务注册表
当服务实例出现异常或者下线之后,就会把这个服务实例从服务注册表中剔除
永久实例
永久服务实例不仅仅会存在服务注册表中,同时也会被持久化到磁盘文件中,当服务实例出现异常或者下线,Nacos只会将服务实例的健康状态设置为不健康,并不会对将其从服务注册表中剔除,所以这个服务实例的信息你还是可以从注册中心看到,只不过处于不健康状态,这是就是两者最最最基本的区别,当然除了上述最基本的区别之外,两者还有很多其它的区别,接下来本文还会提到,这里你可能会有一个疑问,为什么Nacos要将服务实例分为临时实例和永久实例?,主要还是因为应用场景不同,临时实例就比较适合于业务服务,服务下线之后可以不需要在注册中心中查看到,永久实例就比较适合需要运维的服务,这种服务几乎是永久存在的,比如说MySQL、Redis等等
MySQL、Redis等服务实例可以通过SDK手动注册
对于这些服务,我们需要一直看到服务实例的状态,即使出现异常,也需要能够查看时实的状态
所以从这可以看出Nacos跟你印象中的注册中心不太一样,他不仅仅可以注册平时业务中的实例,还可以注册像MySQL、Redis这个服务实例的信息到注册中心
在SpringCloud环境底下,一般其实都是业务服务,所以默认注册服务实例都是临时实例
当然如果你想改成永久实例,可以通过下面这个配置项来完成
springcloud:nacos:discovery:#ephemeral单词是临时的意思,设置成false,就是永久实例了ephemeral: false
这里还有一个小细节,在1.x版本中,一个服务中可以既有临时实例也有永久实例,服务实例是永久还是临时是由服务实例本身决定的,但是2.x版本中,一个服务中的所有实例要么都是临时的要么都是永久的,是由服务决定的,而不是具体的服务实例,所以在2.x可以说是临时服务和永久服务。
为什么2.x把临时还是永久的属性由实例本身决定改成了由服务决定?
其实很简单,你想想,假设对一个MySQL服务来说,它的每个服务实例肯定都是永久的,不会出现一些是永久的,一些是临时的情况吧
所以临时还是永久的属性由服务本身决定其实就更加合理了
服务注册
作为一个服务注册中心,服务注册肯定是一个非常重要的功能,所谓的服务注册,就是通过注册中心提供的客户端SDK(或者是控制台)将服务本身的一些元信息,比如ip、端口等信息发送到注册中心服务端,服务端在接收到服务之后,会将服务的信息保存到前面提到的服务注册表中
1、1.x版本的实现
在Nacos在1.x版本的时候,服务注册是通过Http接口实现的
代码如下
整个逻辑比较简单,因为Nacos服务端本身就是用SpringBoot写的,但是在2.x版本的实现就比较复杂了
2、2.x版本的实现
2.1、通信协议的改变
2.x版本相比于1.x版本最主要的升级就是客户端和服务端通信协议的改变,由1.x版本的Http改成了2.x版本gRPC,gRPC是谷歌公司开发的一个高性能、开源和通用的RPC框架,Java版本的实现底层也是基于Netty来的,之所以改成了gRPC,主要是因为Http请求会频繁创建和销毁连接,白白浪费资源,所以在2.x版本之后,为了提升性能,就将通信协议改成了gRPC
根据官网显示,整体的效果还是很明显,相比于1.x版本,注册性能总体提升至少2倍
虽然通信方式改成了gRPC,但是2.x版本服务端依然保留了Http注册的接口,所以用1.x的Nacos SDK依然可以注册到2.x版本的服务端
2.2、具体的实现
Nacos客户端在启动的时候,会通过gRPC跟服务端建立长连接