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

rnacos实现raft和类distro协议,支持集群部署的更多相关文章

  1. MPush开源消息推送系统:简洁、安全、支持集群

    引言由于之前自己团队需要一个消息推送系统来替换JPUSH,一直找了很久基本没有真正可用的开源系统所有就直接造了个轮子,造轮子的时候就奔着开源做打算的,只是后来创业项目失败一直没时间整理这一套代码,最近 ...

  2. Nacos源码分析-Distro协议概览

    温馨提示: 本文内容基于个人学习Nacos 2.0.1版本代码总结而来,因个人理解差异,不保证完全正确.如有理解错误之处欢迎各位拍砖指正,相互学习:转载请注明出处. 什么是Distro协议 今天来分析 ...

  3. Java 网络编程(二) 两类传输协议:TCP UDP

    链接地址:http://www.cnblogs.com/mengdd/archive/2013/03/09/2951841.html 两类传输协议:TCP,UDP TCP TCP是Transfer C ...

  4. Swift - 2 (?和!、结构体、类、协议、扩展、闭包)

    1> 可选类型(?)和强制解包(!) 在swift中,可选类型(?) 其根源是一个 枚举型,里面有 None 和 Some 两种类型.其实所谓的 nil 就是 Optional.None , 非 ...

  5. NSObject class和NSObject protocol的关系(抽象基类与协议)

    [转载请注明出处] 1.接口的实现 对于接口这一概念的支持,不同语言的实现形式不同.Java中,由于不支持多重继承,因此提供了一个Interface关键词.而在C++中,通常是通过定义抽象基类的方式来 ...

  6. Nacos 之 Distro 协议

    1. 概述 Distro协议是阿里自研的一个最终一致性协议,继承了 Gossip 以及 Eureka 通信(PeerEurekaNodes)的优点并做进一步优化而出来的: 对于原生的Gossip,由于 ...

  7. [开源精品] C#.NET im 聊天通讯架构设计 -- FreeIM 支持集群、职责分明、高性能

    FreeIM 是什么? FreeIM 使用 websocket 协议实现简易.高性能(单机支持5万+连接).集群即时通讯组件,支持点对点通讯.群聊通讯.上线下线事件消息等众多实用性功能. ImCore ...

  8. Mininet实验 OpenFlow1.3协议基于Mininet部署与验证

    参照:OpenFlow1.3协议基于Mininet部署与验证 安装过程,参考原文. 实验 使用ifconfig查看本机IP地址:192.168.1.101 进入OpenDayLight界面,cd到bi ...

  9. Redis一站式管理平台工具,支持集群创建,管理,监控,报警

    简介 Redis Manager 是 Redis 一站式管理平台,支持集群的创建.管理.监控和报警. 集群创建:包含了三种方式 Docker.Machine.Humpback: 集群管理:支持节点扩容 ...

  10. CabloyJS v3.1.0支持集群及更多 🎉

    在抗疫期间,CabloyJS v3.1.0设计并开发了大量特性,并且所有相关文档已集齐.强烈建议大家试用,拍砖 特性 - 后端核心 集群: 集群现在已经成为CabloyJS的一等公民.也就是说,Cab ...

随机推荐

  1. PicoRV32-on-PYNQ-Z2: An FPGA-based SoC System——RISC-V On PYNQ项目复现

    本文参考: 1️⃣ 原始工程 2️⃣ 原始工程复现教程 3️⃣ RISCV工具链安装教程 本文工程: https://bhpan.buaa.edu.cn:443/link/4B08916BF2CDB4 ...

  2. 深度解析SpringBoot内嵌Web容器

    你好,我是刘牌! 前言 今天分享一个SpringBoot的内嵌Web容器,在SpringBoot还没有出现时,我们使用Java开发了Web项目,需要将其部署到Tomcat下面,需要配置很多xml文件, ...

  3. PostgreSQL 12 文档: 部分 VIII. 附录

    部分 VIII. 附录 目录 A. PostgreSQL错误代码 B. 日期/时间支持 B.1. 日期/时间输入解释 B.2. 处理无效或不明确的时间戳 B.3. 日期/时间关键词 B.4. 日期/时 ...

  4. 力扣 662 https://leetcode.cn/problems/maximum-width-of-binary-tree/

    需要了解树的顺序存储 如果是普通的二叉树 ,底层是用链表去连接的 如果是满二叉树,底层用的是数组去放的,而数组放的时候 会有索引对应 当前父节点是索引i,下一个左右节点就是2i,2i+1 利用满二叉树 ...

  5. 堆栈式 CMOS、背照式 CMOS 和传统 CMOS 传感器的区别

    光电效应 光电效应的现象是赫兹(频率的单位就是以他命名的)发现的,但是是爱因斯坦正确解释的.简单说,光或某一些电磁波,照射在某些光敏物质会产生电子,这就是光电效应. 这就将光变为了电,光信号的改变会带 ...

  6. 【技术积累】Vue.js中的事件【一】

    Vue中的事件是什么 在Vue.js中,事件是用于处理用户交互的重要机制.Vue.js提供了一系列的事件处理方法和指令,使开发者能够方便地处理用户的各种操作. 1. 事件绑定:Vue.js通过v-on ...

  7. 编码技巧 --- 使用dynamic简化反射

    引言 dynamic 是 Framework 4.0 就出现特性,它的出现让 C# 具有了弱语言类型的特性.编译器在编译的时候不再对类型进行检查,默认 dynamic 对象支持开发者想要的任何特性. ...

  8. Anaconda 使用时,conda activate 失败

    今天使用一台电脑上新安装的 anaconda 时,运行 conda activate, 出现如下报错: 错误提示中,说要把 . C:\ProgramData\Anaconda3\etc\profile ...

  9. AI回答打鸟问题

    GPT-3.5 树上有9只鸟,猎枪打下来1只,还剩几只? 如果猎枪打下来1只鸟,那么树上剩下的鸟应该是8只.

  10. test.sh

    #!/bin/bash echo "=== show OS version ===" cat /etc/os-release echo "=== show IP addr ...