AnomaNode 深入解析:Anoma 协议的 Elixir/OTP 节点应用、安装配置与监督架构
【免费下载链接】anoma-archiveReference implementation of Anoma项目地址: https://gitcode.com/GitHub_Trending/an/anoma-archive
本文以仓库根目录的 README.md 为主体,围绕 AnomaNode——Anoma 协议的参考实现节点应用——展开:先说明它的定位与安装方式,再结合 mix.exs、config/config.exs 与lib/下的源码,完整讲清一个节点应用从启动、监督树组织到多节点管理的运行方式。读完本文,你可以独立配置、启动并验证一个 AnomaNode 实例,并理解它内部由哪些“引擎”(Engine)协作完成状态变更处理。
1. 项目定位:AnomaNode 是什么
README.md 对项目的定义是:
"I am the Anoma Node application. I provide the main functionality of the Anoma protocol. In particular, I instantiate all the Engine functionality and state-change processing of Anoma."
即 AnomaNode 是整个 Anoma 协议的节点应用:它负责实例化协议中的全部引擎(Engine),并承担 Anoma 协议的状态变更(state-change)处理。从 mix.exs 可以确认其工程身份:
- OTP 应用名为
:anoma_node,通过mod: {Anoma.Node, []}指定了应用入口模块(见 lib/node.ex); - 要求 Elixir
~> 1.17(mix.exs); - 版本由 version.exs 单独维护,当前仓库版本为
"1.0.0"。
注意 README.md 安装示例中写的~> 0.1.0属于 Mix 项目模板沿用的写法,当前仓库的实际版本以 version.exs 为准。此外,mix.exs 中声明了自动启动的应用(extra_applications)::crypto、:debugger、:enacl、:logger、:runtime_tools、:tools、:ex_unit;Mnesia 被刻意放入included_applications,注释明确说明不应自动启动 Mnesia——它的初始化由监督树在需要时驱动(见 lib/supervisor.ex 中的Anoma.Tables.initialize_storage/0)。
2. 安装与依赖
2.1 作为 Hex 依赖引入
README.md 给出的标准安装方式是把anoma_node加入mix.exs的依赖列表:
def deps do [ {:anoma_node, "~> 0.1.0"} ] end2.2 作为源码直接编译
更常见的用法是直接使用仓库源码。依赖关系集中在 global_deps.exs(由 mix.exs 引入),核心运行时依赖包括:
| 依赖 | 说明 |
|---|---|
anoma_lib | Anoma 核心库(git 依赖,tag v1.0.1) |
anoma_protobuf | gRPC 通信使用的 protobuf 定义(v1.0.0) |
event_broker | 事件总线(v1.0.0) |
grpc/protobuf | 节点间与客户端的 gRPC 服务端 |
jason | JSON 编解码 |
typed_struct | 项目中广泛使用的带类型结构体宏 |
credo/dialyxir/ex_doc | 仅 dev/test 环境的静态检查与文档工具 |
本地构建与测试可使用仓库自带的 Makefile:make build(即mix compile)、make test(mix test)、make release(先清理_build、deps,再执行mix deps.get与mix release,并固定 glibc 2.20 目标以保证二进制兼容性)。
2.3 文档生成
README.md 还说明:文档可用 ExDoc 生成并发布到 HexDocs。对应地,global_deps.exs 中声明了{:ex_doc, "~> 0.31", only: [:dev]};Makefile 提供了docs与docs-release两个目标,前者执行mix toc及scripts/docs.sh、scripts/docs-files.sh,后者再追加scripts/docs-version.sh完成版本化文档发布流程。
3. 配置:一个节点应用的关键开关
所有配置集中在 config/config.exs,按config_env()再叠加 config/dev.exs、config/test.exs 等环境文件。当前仓库中与节点行为直接相关的配置有三组:
# 1) 日志:生产级收敛,只保留 error 级 config :logger, level: :error, handle_otp_reports: false, handle_sasl_reports: false # 2) gRPC 端口:可用环境变量 NODE_GRPC_PORT 覆盖,默认 50051 config :anoma_node, grpc_port: String.to_integer(System.get_env("NODE_GRPC_PORT") || "50051") # 3) Mnesia 存储行为 config :anoma_node, :mnesia, persist_to_disk: false, rocksdb: falsegrpc_port:节点对外暴露 gRPC 服务的端口,Anoma.Supervisor在启动时读取该值拉起 gRPC 服务端(见 lib/supervisor.ex)。需要多节点同机运行时应通过NODE_GRPC_PORT环境变量区分端口。mnesia.persist_to_disk:当前配置为false,即 Mnesia 数据不持久化到磁盘(配置注释中说明了开启后会写入$XDG_DATA_HOME/anoma等平台相关目录)。这意味着默认的节点是“临时性”的:每次启动都是干净状态——这一点与 USAGE.md 中“ephemeral node(可随时创建与销毁的节点)”的描述一致。mnesia.rocksdb:当前配置为false,即默认使用 Mnesia 内存表而非 RocksDB 后端;CHANGELOG.md 记录了历史上 RocksDB 表曾被默认启用的演进过程,实际取值请以当前配置与所用环境为准。
4. 启动流程与监督架构:节点是如何被实例化的
README 说 AnomaNode “instantiates all the Engine functionality”,这句话的实现依据就是两层监督树。
4.1 顶层:Anoma.Supervisor管理共享进程与多个节点
应用入口 lib/node.ex 中的Anoma.Node.start/2仅一行:调用Anoma.Supervisor.start_link/1。lib/supervisor.ex 的init/1做了三件事:
- 初始化全局存储:
:ok = Anoma.Tables.initialize_storage(); - 注册三个共享子进程:
Anoma.Node.Registry(Elixir.Registry,unique 键)——以node_id为键定位各个节点进程;GRPC.Server.Supervisor——以Anoma.Node.Transport.GRPC.Endpoint为端点、grpc_port为端口启动 gRPC 服务(端点实现见 lib/node/transport/grpc/endpoint/endpoint.ex);Anoma.Node.NodeSupervisor(DynamicSupervisor)——用于动态地挂载、卸载任意多个节点;
- 监督策略为
:one_for_all。
由此得到 README 所说的“主功能”:进程名固定的 gRPC 端点 + 可动态增删的节点池。
4.2 单节点:Anoma.Node.Supervisor组织四大引擎
每个节点是一棵以 lib/node/supervisor.ex 为根的监督树。start_link/1会用node_id通过Anoma.Node.Registry.via/2生成唯一进程名,init/1中实例化四个子系统:
children = [ {Transport.Supervisor, node_id: node_id, node_config: args[:node_config]}, {Transaction.Supervisor, [node_id: node_id] ++ transaction}, {Intents.Supervisor, node_id: node_id}, {Logging, node_id: node_id} ] Supervisor.init(children, strategy: :one_for_all)- Transport.Supervisor(lib/node/transport/supervisor.ex):负责节点间的网络传输层,包括 gRPC 端点、节点发现(network_register/advertise)等;
- Transaction.Supervisor(lib/node/transaction/supervisor.ex):即 README 所说的“state-change processing”核心,内部再分 mempool、ordering、executor、storage 等引擎(对应 lib/node/transaction/ 下的各子目录);
- Intents.Supervisor(lib/node/intents/supervisor.ex):管理意图(intent)池与 solver;
- Logging(lib/node/logging.ex):节点级日志引擎。
Anoma.Node.Supervisor的合法启动参数(@args)为:node_id、:transaction、:node_config以及默认replay: true——replay默认开启意味着新节点启动时会尝试重放既有状态(配合 Mnesia/RocksDB 持久化场景)。
4.3 节点的动态生命周期:start_node / stop_node
Anoma.Supervisor提供了一对面向节点生命周期的公开 API(lib/supervisor.ex):
start_node/1:先经State.startup_arguments_or_default/1解析启动参数(lib/node/replay/start_state.ex),再调用Tables.initialize_tables_for_node/1检查该node_id的存储表是created(新节点,:new_node)还是existing(既有节点,:existing_node),最后通过DynamicSupervisor.start_child/2把Anoma.Node.Supervisor挂入动态监督树;stop_node/1:通过 Registry 按node_id定位节点监督进程并Supervisor.stop/1整个子树。
这套机制支撑了 USAGE.md 演示的“临时节点”用法:在 IEx 中反复创建、销毁节点,而底层的 gRPC 端点与 Registry 保持不变。
5. 实操:启动一个节点并验证
仓库提供了可直接运行的示例模块Anoma.Node.Examples.ENode(lib/examples/e_node.ex)。start_node/1的行为(源码见 lib/examples/e_node.ex):
- 用
:crypto.strong_rand_bytes(32)生成随机十六进制node_id; - 组装
node_config(含grpc_host/grpc_port,端口取自Application.get_env(:anoma_node, :grpc_port),即第 3 节中可被NODE_GRPC_PORT覆盖的那个值); - 调用
Anoma.Supervisor.start_node/1,成功或“已启动”时返回%ENode{node_id, pid}结构体,失败时记录日志并返回{:error, :failed_to_start_node}。
在仓库根目录执行iex -S mix后:
iex(1)> node = Anoma.Node.Examples.ENode.start_node() # => %Anoma.Node.Examples.ENode{node_id: "3F2A...", pid: #PID<0.xxx.0>}随后用 test/ 目录中的测试(如 test/node_id_test 相关的 registry_test.exs、test/mempool_test.exs 等,覆盖 Registry、Mempool、gRPC 传输、pubsub、replay 等面)作为行为参照:make test(或mix test)即完整回归。需要强调的适用前提是:当前仓库配置下 Mnesia 不写盘(persist_to_disk: false),节点重启后状态不保留;如需持久化,应自行调整:anoma_node, :mnesia配置项,且该行为的默认值以 config/config.exs 及对应环境文件为准。
6. 质量工具与版本脉络
- 静态检查:mix.exs 配置了 Dialyzer(本地 PLT 位于
plts/anoma.plt,关闭 improper list 警告——项目有意使用裸 cons),global_deps.exs 同时提供 Credo; - 版本演进:CHANGELOG.md 记录了从 v0.11 到 v0.21 的功能轨迹,与本文所述的“引擎”结构一脉相承:存储引擎化、Pinger 的 CQRS 化、Transport 引擎进入路由、Cairo 后端、Shielded RM 与事件总线(event broker)的合入(v0.20)等。README 中 “Engine functionality and state-change processing” 的表述,正是这条演进路线的浓缩;
- 文档:ExDoc 文档按 scripts/ 下的脚本生成与版本化,
make docs是入口命令。
小结
README.md 用不到 20 行定义了 AnomaNode 的身份:它是 Anoma 协议的节点应用,承载全部引擎与状态变更处理。仓库源码把这句话落实为:一个以 lib/supervisor.ex 为顶、Registry + 固定 gRPC 端点 + 动态节点池的顶层结构,以及每个节点内部由 Transport、Transaction、Intents、Logging 四大子树构成的监督体系(lib/node/supervisor.ex)。结合 config/config.exs 的端口与存储开关、Makefile 的构建/测试/文档流程,以及 lib/examples/e_node.ex 的启动示例,即可完整地把“安装—配置—启动—验证”整条链路走通。
【免费下载链接】anoma-archiveReference implementation of Anoma项目地址: https://gitcode.com/GitHub_Trending/an/anoma-archive
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考