rnacos用 rust 重新现实的 nacos 服务,新版本实现 raft 和类 distro 协议,支持集群部署 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
heqingpan
V2EX    Rust

rnacos用 rust 重新现实的 nacos 服务,新版本实现 raft 和类 distro 协议,支持集群部署

  •  1
     
  • /div>   heqingpan 2023-09-18 07:50:24 +08:00 3117 次点击
    这是一个创建于 822 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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

    # 解压 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 运行参数支持通过环境变量,或指定配置文件方式设置。 如果不设置则按默认参数运行。

    例子

    # 从 0.3.0 版本开始支持 -e envfile 运行参数 ./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 即可。

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

    先运行主节点

    nohup ./rnacos -e env01 > n01.log & 

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

    nohup ./rnacos -e env02 > n02.log & nohup ./rnacos -e env03 > n03.log & 

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

    4.4 运行应用使用集群

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

    配置中心 http api 例子

    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 例子

    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 条回复    2023-11-27 07:33:39 +08:00
    cheneydog
        1
    cheneydog  
       2023-09-18 08:33:26 +08:00
    支持
    heqingpan
        2
    heqingpan  
    OP
       2023-09-18 09:00:12 +08:00
    感谢支持:-)
    pannanxu
        3
    pannanxu  
       2023-09-18 09:30:28 +08:00   3
    第一眼:nacos
    第二眼:macos
    第三眼:rnacos ???

    我瞎了
    heqingpan
        4
    heqingpan  
    OP
       2023-09-18 21:48:43 +08:00
    这么看有点尴尬
    nebkad
        5
    nebkad  
       2023-09-19 15:42:40 +08:00
    在 rustcc 上看了简介,虽然不懂什么叫 nacos ,但是关于 “配置中心、注册中心需要实现两个不同的协议支持集群部署” 的说明惊艳到了我~ 关注一下
    heqingpan
        6
    heqingpan  
    OP
       2023-09-20 08:37:51 +08:00
    欢迎关注:-)
    heqingpan
        7
    heqingpan  
    OP
       2023-09-20 08:40:41 +08:00
    有谁知道 V2EX 是不是不支持修改已发布的内容?

    看到几处错别字,没找到修改的入口
    imchl
        8
    imchl  
       2023-11-27 07:33:39 +08:00 via iPhone
    支持
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2764 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 14:47 PVG 22:47 LAX 06:47 JFK 09:47
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86