rnacos实现raft和类distro协议,支持集群部署

1. rnacos 简介

rnacos是一个用rust实现的nacos服务。

rnacos是一个轻量、 快速、稳定、高性能的服务;包含注册中心、配置中心、web管理控制台功能,支持单机、集群部署。

rnacos设计上完全兼容最新版本nacos面向client sdk 的协议(包含1.x的http OpenApi,和2.x的grpc协议), 支持使用nacos服务的应用平迁到 rnacos。

rnacos相较于java nacos来说,是一个提供相同功能,启动更快、占用系统资源更小、性能更高、运行更稳定的服务。

2. rnacos支持集群部署

rnacos之前只支持单机部署,不能水平扩容,同时存在单点稳定性问题,不太合适用于生产环境。所以rnacos一直有计划开发支持集群部署的功能。

目前rnacos 0.3.1版本已支持集群部署。其中配置中心通过raft协议支持集群部署,注册中心通过类distro协议支持集群部署。

rnacos主要功能模块:

2.1 为什么在同一个应用中,配置中心、注册中心需要实现两个不同的协议支持集群部署?

主要因为配置中心和注册中心的特点不一样。

配置中心的数据需要持久化,在多个服务节点中的数据需要强一致,raft是一个逻辑完备的分页式共识协议。实现raft协议只要大于半数的节点正常,就可以正常提供服务。 同时实例raft协议就相当于实现一个分页式存储,配置中心可以不需要额外依赖 mysql 等外部数据库,部署依赖更简单。 所以配置中心选择通过raft协议支持集群部署。

注册中心的数据主要是临时的服务实例数据,这类数据不需要持久化,不追求多个服务节点中的数据强一致。同时注册中心更关注在部分节点异常时能提供完整的服务,更观注集群的读写性能。所以注册中心不选择 raft 协议,而是通过类 distro协议支持集群部署。

现在模块协议的对比:

模块 协议 写性能 读性能 数据一致性 容错率
配置中心 raft 一般(只有主节点可写) 高,每个节点都可读 强一致 一般,大于半数节点正常则可以正常提供服务
注册中心 raft 高(每个节点都可写) 高,每个节点都可读 一般 高,一个节点能不依赖其它依赖提供服务

2.2 配置中心raft协议

raft协议的主要逻辑:

  1. 节点区分角色:leader(主节点),follower(从节点),candidate(选举节点);
  2. 稳定状态是一个主节点,多个从节点;
  3. 主节点负责写入,写入时需要先把写入日志同步到其它节点,超过半数节点写入日志成功后才能提交日志到状态机。
  4. 主节点需要定时发心跳到从节点,从节点如果超时未收到心跳,则会发起选举。选举时收到超过半数节点的同意,就可以切换成主节点。

具体协议可以参考 raft协议论文

rnacos 接入 raft的主要逻辑:

  1. 基于 async-raft 库实现raft协议,主要实现网络层和存储层。在 rnacos中存储层的状态机就是配置中心。
  2. 配置中心接入raft 协议的状态机,由 raft 状态机驱动更新配置中心的内容。

rnacos一个三节点的配置中心请求处理示例:

写入:

  1. 客户端随机向一个节点发起一个更新配置请求
  2. 在请求入口层加一个raft路由判断,如果本节点是主节点则处理,否则路由到指定主节点处理
  3. 主节点写入请求到raft日志
  4. 将请求同步到其它从节点
  5. 如果超过半数节点写入日志成功(包含自身),则提交请求日志到状态机中,配置写入配置中心。(其它从节点的提交在下次日志同步或心跳时提交)
  6. 返回处理结果

请求:

  1. 客户端随机向一个节点发起一个查询配置请求
  2. 收到请求的节点和单机处理一样,直接查询本节点配置中心数据返回。

2.3 注册中心类distro协议

协议主要逻辑:

  1. 每个节点有全量的数据,都可提供注册信息查询服务。
  2. 注册中心每个节点平等,按hash划分每个节点负责的内容;节点对负责的服务可写,否则转发到对应负责的节点处理。
  3. 通过 grpc协议注册的服务,接收的节点直接处理。
  4. 一个节点更新服务实例信息后再同步给其它节点。

具体协议可以参考java nacos 的distro协议实现 。

rnacos 和 java版主体逻辑相同,但实现的细节有些区别。

rnacos一个三节点的注册中心请求处理示例:

http 写入:

  1. 客户端随机向一个节点发起一个注册服务实例请求
  2. 请求跳过服务路由判断,如果服务路由的节点是本节点则处理,否则路由到指定的其它节点处理
  3. 收到本节点负责的服务实例请求,把请求注册到注册中心中
  4. 返回处理结果
  5. 异步同步更新的数据到其它节点

grpc 写入(不路由,本节点直接处理):

  1. 客户端随机向一个节点发起grpc长链接
  2. 客户端发起一个注册服务实例请求
  3. 像单机一样,把请求注册到注册中心中
  4. 返回处理结果
  5. 异步同步更新的数据到其它节点

查询:

  1. 客户端随机向一个节点发起一个查询服务信息请求
  2. 收到请求的节点和单机处理一样,直接查询本节点注册中心数据返回。

为什么http的写入与grpc写入的路由逻辑不同?

因为grpc的心跳是按长链接来处理,一个客户端的链接段开,则这个链接的所用请求都失效。【高效】

然后 http 的实例注册是无状态的,只能通过定时器按注册时间更新实例的状态;同时注册中心中实例是按服务分类维护的,所以 http 注册的实例需要按服务做路由,这样才能支持不同的节点负责不同范围的服务。【低效】

所以在注册中心使用grpc协议的性能会比http协议性能好很多。

3. 性能与容量

rnacos 支持集群后其性能与容量的水位是怎样的呢?

下面给出一组在我台式电脑(8核16线程+16内存)的压测性能对比数据.

主要使用goose压测,具体可以参考项目中的子压测工程 loadtest

性能压测结果

模块 场景 单节点qps 集群qps 总结
配置中心 配置写入,单机模式 1.5万 1.5万
配置中心 配置写入,集群模式 1.8千 1.5千 接入raft后没有充分优化,待优化,理论上可接近单机模式
配置中心 配置查询 8万 n*8万 集群的查询总qps是节点的倍数
注册中心 服务实例注册,http协议 1.2万 1.0万 注册中心单机模式与集群模式写入的性能一致
注册中心 服务实例注册,grpc协议 1.2万 1.2万 grpc协议压测工具没有支持,目前没有实际压测,理论不会比http协议低
注册中心 服务实例心跳,http协议 1.2万 1.0万 心跳是按实例计算和服务实例注册一致共享qps
注册中心 服务实例心跳,grpc协议 8万以上 n*8万 心跳是按请求链接计算,且不过注册中心处理线程,每个节点只需管理当前节点的心跳,集群总心跳qps是节点的倍数
注册中心 查询服务实例 3万 n*3万 集群的查询总qps是节点的倍数

注: 具体结果和压测环境有关

压测记录

注册中心查询(单机3万 qps):

配置中心查询,两个进程分别限流4万qps同时压测(共8万qps),其中一个的压测记录:

容量分析

配置中心

  1. 配置中心的单机查询8万qps,很高,又支持水平扩容;集群基本没有查询瓶颈。
  2. 配置中心所占用的内存和配置内存有关,在内存没有满前,基本没有瓶颈。
  3. 配置中心集群写入时统一在主节点写入,写入可能有瓶颈;目前1.5千tps,后面优化后应该能到1万 tps以上。

注册中心

  1. 注册中心的单机查询3万qps,比较高,又支持水平扩容;集群基本没有查询瓶颈。
  2. 注册中心所占用的内存和配置内存有关,在内存没有满前,基本没有瓶颈。
  3. 注册中心集群写入时每个节点都要写一遍,整体集群的写入性能tps和单机理论上相当。
  4. http协议(v1.x版本)和grpc协议(v2.x)的心跳维护机制不同;http心跳是按实例计算和服务实例注册一致共享qps, grpc的心跳是按请求链接计算且不过注册中心处理线程。所有这类协议理论支持的容量差别很大。

注册中心集群注册容量推理

  1. http协议注册+心跳qps是1万,每个实例5秒钟一次心跳;理论上只能支持5万服务实例左右。
  2. grpc协议,注册qps假设也是1万,心跳qps单实例8万,3节点集群总心跳24万;如果平均一个应用实例1小时重连一次;支持注册的服务实例总数为:606010000 = 3600万,心跳支持的链接实例总数为:5*24万=120万个链接实例(和集群节点有关)。

结论:

如果使用v1.0x http协议,支持的实例在5万个左右。

如果使用v2.0x grpc协议,理论上能到达千万实例,基本没有瓶颈。

4. rnacos 集群部署

4.1 获取rnacos应用包

方式1:从 github release 下载对应系统的应用包,解压后即可运行。

linux 或 mac

shell 复制代码
# 解压
tar -xvf rnacos-x86_64-apple-darwin.tar.gz
# 运行
./rnacos -e envfine

windows 解压后直接运行 rnacos.exe 即可。

方式2: 通过docker 运行

#stable是最新正式版本号,也可以指定镜像版本号,如: qingpan/rnacos:v0.3.0
docker pull qingpan/rnacos:stable  
# 在/path/rnacos/.env 配置文件中配置好运行参数
docker run --name mynacos -p 8848:8848 -p 9848:9848 -d -v /path/rnacos:/io qingpan/rnacos:stable

docker 的容器运行目录是 /io,会从这个目录读写配置文件

方式3:通过 cargo 编译安装

# 安装
cargo install rnacos
# 运行
rnacos -e envfile

方式4: 下载源码编译运行

git clone https://github.com/heqingpan/rnacos.git
cd rnacos
cargo build --release
cargo run --release -- -e envfile

测试、试用推荐使用第1、第2种方式,直接下载就可以使用。

在linux下第1、第2种方式默认是musl版本(性能比gnu版本差一些),在生产服务对性能有要求的可以考虑使用第3、第4种在对应环境编译gnu版本部署。

4.2 运行参数说明

同一个应用包需要支持不同场景,就需要支持设置自定义参数。

rnacos 运行参数支持通过环境变量,或指定配置文件方式设置。 如果不设置则按默认参数运行。

例子

sh 复制代码
# 从0.3.0版本开始支持 -e env_file 运行参数
./rnacos -e env_file

如果不指定文件时也会尝试从当前目录下.env文件加载配置参数

env_file内容的格式是

KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3

运行参数:

参数KEY 内容描述 默认值 示例 开始支持的版本
RNACOS_HTTP_PORT rnacos监听http端口 8848 8848 0.1.x
RNACOS_GRPC_PORT rnacos监听grpc端口 默认是 HTTP端口+1000 9848 0.1.x
RNACOS_HTTP_WORKERS http工作线程数 cpu核数 8 0.1.x
RNACOS_CONFIG_DB_FILE 配置中心的本地数据库文件地址【0.2.x后不在使用】 config.db config.db 0.1.x
RNACOS_CONFIG_DB_DIR 配置中心的本地数据库sled文件夹, 会在系统运行时自动创建 nacos_db nacos_db 0.2.x
RNACOS_RAFT_NODE_ID 节点id 1 1 0.3.0
RNACOS_RAFT_NODE_ADDR 节点地址Ip:GrpcPort,单节点运行时每次启动都会生效;多节点集群部署时,只取加入集群时配置的值 127.0.0.1:GrpcPort 127.0.0.1:9848 0.3.0
RNACOS_RAFT_AUTO_INIT 是否当做主节点初始化,(只在每一次启动时生效) 节点1时默认为true,节点非1时为false true 0.3.0
RNACOS_RAFT_JOIN_ADDR 是否当做节点加入对应的主节点,LeaderIp:GrpcPort;只在第一次启动时生效 127.0.0.1:9848 0.3.0
RUST_LOG 日志等级:debug,info,warn,error;所有http,grpc请求都会打info日志,如果不观注可以设置为error减少日志量 info error 0.3.0

注:从v0.3.0开始,默认参数启动的节点会被当做只有一个节点,当前节点是主节点的集群部署。支持其它新增的从节点加入。

配置集群规则

  1. 所有的集群节点都需要设置RNACOS_RAFT_NODE_ID,RNACOS_RAFT_NODE_ADDR ,其中不同节点的node_id和 node_addr不能相同;node_id为一个正整数,node_addr为ip:grpc_port
  2. 集群主节点: 初始设置RNACOS_RAFT_AUTO_INIT为true (如果节点为1,默认是 true,不用额外设置)。
  3. 集群从节点: 初始设置RNACOS_RAFT_AUTO_INIT为false (节点非1,默认就是false,不用额外设置);另外需要设置RNACOS_RAFT_JOIN_ADDR为当前主节点的地址,以方便启动时自动加入集群中。
  4. 第2、3点只是为了初始化组建集群。集群运行起来之后,后继启动配置从raft db中加载。
  5. 集群节点数量不要求,可以是1、2、3、4、... ; 不过raft协议只支持小于集群半数节点异常后继续提供写入服务(查询不影响)。例如:3个节点集群支持1个节点异常后提供写入服务,2个节点集群可以正常运行,不支持节点异常后提供服务。
  6. 从节点可以在使用过程中按需加入。比如原来3个节点,可能在使用一段时间后增加2个节点扩容。

4.3 集群实例

按上面的配置规则,下面我们配置一个3节点集群例子。

初始化节信息

  1. 主节点id为1,地址为127.0.0.1:9848
  2. 第一个从节点id为2,地址为127.0.0.1:9849
  3. 第二个从节点id为3,地址为127.0.0.1:9849

正式集群部署的log等级建议设置为warn,不打正常的请求日志,只打报警或异常日志,减少日志量。

配置信息如下

env01

#file:env01 , Initialize with the leader node role
RUST_LOG=warn
RNACOS_HTTP_PORT=8848
RNACOS_RAFT_NODE_ADDR=127.0.0.1:9848
RNACOS_CONFIG_DB_DIR=db01
RNACOS_RAFT_NODE_ID=1
RNACOS_RAFT_AUTO_INIT=true

env02:

#file:env02 , Initialize with the follower node role
RUST_LOG=warn
RNACOS_HTTP_PORT=8849
RNACOS_RAFT_NODE_ADDR=127.0.0.1:9849
RNACOS_CONFIG_DB_DIR=db02
RNACOS_RAFT_NODE_ID=2
RNACOS_RAFT_JOIN_ADDR=127.0.0.1:9848

env03:

#file:env03 , Initialize with the follower node role
RUST_LOG=warn
RNACOS_HTTP_PORT=8850
RNACOS_RAFT_NODE_ADDR=127.0.0.1:9850
RNACOS_CONFIG_DB_DIR=db03
RNACOS_RAFT_NODE_ID=3
RNACOS_RAFT_JOIN_ADDR=127.0.0.1:9848

注: 上面的地址是本机运行多实例的地址,实际使用时换成具体的服务ip和port即可。

分别运行三个节点,需要先运行主节点成功后再运行

先运行主节点

sh 复制代码
nohup ./rnacos -e env01 > n01.log &

主节点功能启动后,再运行从节点

sh 复制代码
nohup ./rnacos -e env02 > n02.log &
nohup ./rnacos -e env03 > n03.log &

实例过程中不同的节点需要在不同的服务器运行服务。

4.4 运行应用使用集群

集群服务启动后,即可运行原有的 nacos 应用。

配置中心http api例子

sh 复制代码
echo "\npublish config t001:contentTest to node 1"
curl -X POST 'http://127.0.0.1:8848/nacos/v1/cs/configs' -d 'dataId=t001&group=foo&content=contentTest'
sleep 1

echo "\nget config info t001 from node 1, value:"
curl 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=t001&group=foo'

echo "\nget config info t001 from node 2, value:"
curl 'http://127.0.0.1:8849/nacos/v1/cs/configs?dataId=t001&group=foo'

echo "\nget config info t001 from node 3, value:"
curl 'http://127.0.0.1:8850/nacos/v1/cs/configs?dataId=t001&group=foo'
sleep 1

echo "\npublish config t002:contentTest02 to node 2"
curl -X POST 'http://127.0.0.1:8849/nacos/v1/cs/configs' -d 'dataId=t002&group=foo&content=contentTest02'
sleep 1

echo "\nget config info t002 from node 1, value:"
curl 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=t002&group=foo'

echo "\nget config info t002 from node 2, value:"
curl 'http://127.0.0.1:8849/nacos/v1/cs/configs?dataId=t002&group=foo'

echo "\nget config info t002 from node 3, value:"
curl 'http://127.0.0.1:8850/nacos/v1/cs/configs?dataId=t002&group=foo'

注册中心http api例子

sh 复制代码
echo "\nregister instance nacos.test.001 to node 1"
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance' -d 'port=8000&healthy=true&ip=192.168.1.11&weight=1.0&serviceName=nacos.test.001&groupName=foo&metadata={"app":"foo","id":"001"}'
echo "\nregister instance nacos.test.001 to node 2"
curl -X POST 'http://127.0.0.1:8849/nacos/v1/ns/instance' -d 'port=8000&healthy=true&ip=192.168.1.12&weight=1.0&serviceName=nacos.test.001&groupName=foo&metadata={"app":"foo","id":"002"}'
echo "\nregister instance nacos.test.001 to node 3"
curl -X POST 'http://127.0.0.1:8850/nacos/v1/ns/instance' -d 'port=8000&healthy=true&ip=192.168.1.13&weight=1.0&serviceName=nacos.test.001&groupName=foo&metadata={"app":"foo","id":"003"}'
sleep 1
echo "\n\nquery service instance nacos.test.001 from node 1, value:"
curl "http://127.0.0.1:8848/nacos/v1/ns/instance/list?&namespaceId=public&serviceName=foo%40%40nacos.test.001&groupName=foo&clusters=&healthyOnly=true"
echo "\n\nquery service instance nacos.test.001 from node 2, value:"
curl "http://127.0.0.1:8849/nacos/v1/ns/instance/list?&namespaceId=public&serviceName=foo%40%40nacos.test.001&groupName=foo&clusters=&healthyOnly=true"
echo "\n\nquery service instance nacos.test.001 from node 3, value:"
curl "http://127.0.0.1:8850/nacos/v1/ns/instance/list?&namespaceId=public&serviceName=foo%40%40nacos.test.001&groupName=foo&clusters=&healthyOnly=true"
echo "\n"

详细使用说明参考rnacos book

5. 欢迎试用与共建

rnacos单机版本发布已有4个月,期间有收到一些使用问题的反馈,目前主体功能已经算比较稳定,有使用nacos的同学欢迎试用。

项目已开源到 github gitee

使用过程中和什么问题或建议可以到github提issues反馈。

如果对你有帮助就给个star鼓励鼓励 😃

对rnacos开发感兴趣的同学也欢迎到github提rp共建。 rnacos发布后已有一位同学参于共建,非常感谢一起共建的同学。

相关推荐
青石路8 小时前
不单独部署注册中心,又要具备注册中心的功能,我能上天!
eureka·注册中心
新时代农民工~4 天前
Nacos详解
java·分布式·nacos
月亮被咬碎成星星6 天前
微服务部署上线过程总结
java·数据库·redis·微服务·nacos·rabbitmq·腾讯云
青石路7 天前
不单独部署注册中心,又要具备注册中心的功能,咋不让我上天?
eureka·注册中心
流月up8 天前
nacos在k8s上的集群安装实践
nacos·k8s·集群·pvc
一个搬砖的农民工9 天前
微服务——服务治理
java·微服务·nacos·springcloud·服务治理
華小灼10 天前
SpringBoot整合Nacos并开启鉴权
spring boot·nacos
Wen先森12 天前
windows安装Nacos并使用
java·nacos
kellerxq15 天前
docker nacos2.3.2安装填坑
java·docker·容器·nacos
p_fly16 天前
【SpringCloud】Nacos
java·spring·spring cloud·nacos