V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
heqingpan
V2EX  ›  Rust

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

  •  1
     
  •   heqingpan · 226 天前 · 1627 次点击
    这是一个创建于 226 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 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 即可。

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

    先运行主节点

    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  
       226 天前
    支持
    heqingpan
        2
    heqingpan  
    OP
       226 天前
    感谢支持:-)
    pannanxu
        3
    pannanxu  
       226 天前   ❤️ 3
    第一眼:nacos
    第二眼:macos
    第三眼:rnacos ???

    我瞎了
    heqingpan
        4
    heqingpan  
    OP
       226 天前
    这么看有点尴尬😓
    nebkad
        5
    nebkad  
       225 天前
    在 rustcc 上看了简介,虽然不懂什么叫 nacos ,但是关于 “配置中心、注册中心需要实现两个不同的协议支持集群部署” 的说明惊艳到了我~ 关注一下
    heqingpan
        6
    heqingpan  
    OP
       224 天前
    欢迎关注:-)
    heqingpan
        7
    heqingpan  
    OP
       224 天前
    有谁知道 V2EX 是不是不支持修改已发布的内容?

    看到几处错别字,没找到修改的入口😓
    imchl
        8
    imchl  
       156 天前 via iPhone
    支持
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   813 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 22:30 · PVG 06:30 · LAX 15:30 · JFK 18:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.