BEAM Book 第14、15章节总结

第14章:Distribution(分布式)

14.1 节点与连接

节点的概念

  • 定义:节点是ERTS的一个命名运行实例,只有命名节点才能通过Erlang分布进行通信
  • 命名规范:由原子 + '@' + 主机名组成,例如:node1@localhost

短名称 vs 长名称

特性短名称长名称
格式仅原子(如 node1)原子@主机名
使用场景所有节点在同一主机节点分布在不同主机
互通性只能与其他短名称节点通信只能与其他长名称节点通信

关键点:短名称和长名称不能互相通信,需要保持一致

节点启动方式

  1. 无名节点:无法连接到其他节点(Node.alive? -> false
  2. 有名节点:使用 --sname(短名)或 --name(长名)启动,可以连接其他节点
  3. 动态启动:即使没有指定名称,也可通过 net_kernel:start([Name, shortnames]) 动态启动分布

连接建立

  • 函数:net_kernel:connect_node/1
  • 默认行为:全连接拓扑 - 当N1连接到N2时,N1会自动连接到N2已连接的所有节点
  • 禁用方式:启动时使用 -connect_all false 标志

Cookie(魔法cookie)认证

  • 作用:基本的安全认证机制,确保只有授权的节点能连接
  • 配置方式

    • 启动时:-setcookie 选项
    • 动态设置:erlang:set_cookie/2 函数
  • 默认行为

    • 节点启动时,在 ~/.erlang.cookie 中查找或创建
    • 自动生成随机atom作为默认cookie
    • 权限为用户只读(UNIX权限)

SSL支持

  • Erlang支持节点间的SSL连接
  • 配置:需要设置SSL证书、密钥和inet_dist选项
  • 性能注意:SSL加密/解密会造成性能开销,需权衡

隐藏节点(Hidden Nodes)

  • 特点:不在 nodes() 返回列表中,不自动连接其他节点
  • 连接方式:必须显式使用 net_kernel:connect_node/1
  • 用途

    • 隔离特定操作或管理网络流量
    • 大型动态网络中减少连接开销
    • 创建子网拓扑
  • 安全性不是安全特性,不提供访问控制保护

第15章:How the Erlang Distribution Works(分布式工作原理)

15.1 EPMD(Erlang Port Mapper Daemon)

EPMD的角色

  • 小型服务器,协助节点间建立连接
  • 独立的OS进程,即使Erlang节点停止也继续运行
  • 默认端口:4369

工作流程

  1. Erlang节点启动时,自动启动EPMD(如果尚未运行)
  2. 节点向EPMD注册,提供:节点名 + 监听端口
  3. 其他节点连接前,先询问EPMD要连接的目标节点的端口号
  4. EPMD返回端口,节点建立TCP连接

实现细节

  • 源代码:erts/epmd/src/epmd_srv.c
  • 简单的C实现,核心逻辑在单一C文件中
  • 协议:小型固定格式二进制协议

15.2 Erlang Distribution Protocol(分布式协议)

握手过程(Handshake)

Node1 ──SEND_NAME──> Node2
       <──ALIVE_ACK── (包含challenge随机数)
       ──CHALLENGE_REPLY──> (计算: challenge + magic_cookie)
       <──CHALLENGE_ACK── (完成握手)

关键点

  • SEND_NAME:包含协议版本和发起节点名
  • ALIVE_ACK/NACK:接受/拒绝连接
  • Challenge-Response:使用魔法cookie进行身份验证
  • 连接建立后开始交换消息

三种消息类型

消息类型用途示例
Control Messages分布式状态管理进程链接/取消链接、监控、EXIT/KILL信号
Data Messages进程间通信发送项数据、RPC
System MessagesVM内部通信分布式控制器、EPMD管理

15.3 Alternative Distribution(可选的分布式实现)

为什么需要可选分布式

原因说明
性能默认TCP可能不是最优的,可用更高效的协议
安全增强加密、访问控制等超越魔法cookie的功能
可靠性更精细的网络分区处理、更强的消息保证
互操作性与其他语言系统(C、Scala等)通信

实现方式

  1. Custom Distribution Driver(C语言驱动)

    • 完全控制低层通信细节
    • 协议和数据格式完全自定义
  2. Distribution Callback Module(Erlang回调)

    • 高层事件处理
    • 连接建立/关闭、消息收发
  3. Third-Party Libraries

    • 封装好的高层API
    • 抽象底层细节

实现步骤

  1. 编写分布式代码(C驱动或Erlang回调)
  2. 配置VM(使用 -proto_dist 选项)
  3. 测试机制(连接、消息传递、错误处理)

15.4 Processes in Distributed Erlang(分布式进程)

PID的结构和转换

本地PID格式: <node.index.serial>
例如: <0.10.0>  (其中0代表本地节点)

远程PID格式: <NodeId.index.serial>
例如: <2.10.0>  (其中2代表远程节点ID)

PID自动转换

  • 出站:将本地PID中的节点ID转换为真实远程节点ID(term_to_binary/1)
  • 入站:接收时将远程节点ID转换回本地(0)
  • 透明性:进程无需感知这种转换

Process Table的索引

  • Index部分:在进程表中查找进程
  • Serial部分:区分重用同一index的新旧进程
  • 进程死亡时:标记为free,serial递增

分布式进程创建(spawn_request)

Node1 spawn/4请求
  ↓
Node1 ERTS发送SPAWN_REQUEST
  ↓
Node2 ERTS接收并在本地spawn
  ↓
Node2 创建进程:Module:Function(ArgList)
  ↓
Node2 ERTS发送SPAWN_REPLY + Flags
  ↓
Node1 ERTS返回PID给调用者

SPAWN_REPLY中的Flags

  • Flag 1:是否建立链接(link)
  • Flag 2:是否建立监控(monitor)

15.5 Remote Procedure Calls(RPC)

同步RPC:rpc:call/4

Result = rpc:call(Node, Module, Function, Args)

执行流程

  1. 发送消息给远程节点的rex服务器
  2. rex服务器在新进程中调用指定函数
  3. 函数执行完毕,结果返回给调用者
  4. rpc:call/4返回结果

rex服务器

  • 名称:Remote EXecution
  • 职责:处理来自其他节点的RPC请求
  • 错误处理:捕获异常,返回给调用者

异步RPC:rpc:cast/4

  • 不等待结果
  • 立即返回 noreply
  • 适用于不关心返回值的场景

RPC的特性

  • 使用标准Erlang分布式协议传输
  • 支持选择性接收和模式匹配
  • 本质上是普通Erlang消息

15.6 Large-Scale Distribution(大规模分布式系统)

主要挑战

  1. 连接数爆炸

    • 全连接拓扑:连接数 = O(n²)
    • 大量节点时网络流量激增
  2. 网络延迟

    • 地理分散时问题严重
    • Erlang异步模型可以有效应对,但不推荐用于地理分布式
  3. 故障处理

    • 节点崩溃、网络断裂、数据损坏不可避免
    • 解决方案:监督树、自动重启、故障检测
  4. 数据一致性

    • 跨节点数据更新同步困难
    • 推荐方案:避免分布式状态,保存单一真实来源
    • Mnesia有性能问题,不适合大数据集
    • 最佳实践:使用SQL数据库 + 消息队列

地理分布式建议

  • 不推荐:用Erlang分布在地理分散的节点间
  • 设计原则:Erlang分布适合数据中心或机架内通信
  • 替代方案:异步消息模式、HTTP API等

15.7 Dist Port(分布式端口缓冲区)

缓冲区概述

  • 用途:节点间通信缓冲
  • 默认大小:128 MB
  • 配置+zdbbl 标志(单位:KB)
# 将缓冲区增加到192GB(实际应为192MB = 192000KB)
erl +zdbbl 192000

缓冲区饱和的症状

2023-05-28 23:10:11.032 [warning] <0.431.0> busy_dist_port <0.324.0>

根本原因和解决方案

原因解决方案
网络问题检查网络性能,升级基础设施
消息量过大优化代码,批量发送,减少频率,尽量同节点通信
长时间任务确保进程定期让出CPU,检查NIF实现
VM调优增加调度器数(+S)、IO线程数(+A)、其他VM参数

监控工具

  • etop:实时性能监控
  • :observer:可视化监控应用

关键总结

Distribution架构

Application Layer
    ↓
Erlang Processes (PIDs with node info)
    ↓
Distribution Protocol (Handshake + 3 message types)
    ↓
EPMD (Port mapping) + TCP/IP
    ↓
Network

核心特性

  1. 透明性:跨节点消息传递对应用透明
  2. 自动转换:PID和数据格式自动转换
  3. 认证机制:Cookie-based简单认证
  4. 可扩展:支持自定义分布实现
  5. 故障恢复:内置链接、监控机制

设计原则

  • 适合:局域网、数据中心、同机架
  • 不适合:地理分散的系统
  • 状态管理:倾向于中央化数据存储而非分布式状态