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发布后已有一位同学参于共建,非常感谢一起共建的同学。

相关推荐
水w12 小时前
【项目实践】SpringBoot Nacos配置管理 map数据
java·服务器·开发语言·spring boot·nacos
Likelong~14 小时前
Etcd注册中心基本实现
etcd·注册中心
维李设论1 天前
Node.js的Web服务在Nacos中的实践
前端·spring cloud·微服务·eureka·nacos·node.js·express
bohu8310 天前
分布式事务seata(AT)与nacos整合-笔记1
笔记·nacos·seata·安装·at
河岸飞流15 天前
Nacos源码学习-本地环境搭建
nacos
chen2017sheng19 天前
Eureka和Zookeeper、Nacos的区别
zookeeper·eureka·nacos
运维&陈同学19 天前
【Nacos02】消息队列与微服务之Nacos 单机部署
linux·后端·微服务·云原生·容器·nacos·架构·注册中心
运维&陈同学20 天前
【Nacos01】消息队列与微服务之Nacos 介绍和架构
linux·运维·后端·微服务·云原生·nacos·架构·注册中心
wjs20241 个月前
基于Java的Nacos云原生动态服务发现、配置和服务管理平台设计源码
java·nacos·服务发现·配置管理·云原生平台
阿维的博客日记1 个月前
java八股-SpringCloud微服务-Eureka理论
spring cloud·eureka·nacos