文章
Elixir in Action - 5
Using GenServer
目录
1、Using GenServer#
对于可用于生产的代码,构建和使用ServerProcess这种抽象没有多大意义。这是因为 Elixir 附带了对通用服务器进程的更好支持,称为GenServer。除了比ServerProcess 功能更丰富之外,GenServer 还可以处理几种边缘情况并在复杂的并发系统中经过了生产实战测试。 GenServer 提供的一些引人注目的特性包括以下内容:
- 支持同步调用(call)和异步请求(cast)
- 可针对调用请求自定义超时设置
- 服务器进程崩溃会传递给正在等待响应的客户端进程
- 支持分布式系统
需要注意的是,GenServer 的背后并没有什么神奇魔法。其代码实现依赖于第5章讲解的并发原语(concurrency primitives)以及后续讨论的容错特性(fault-tolerance features)。毕竟,GenServer 完全是用标准的 Erlang 和 Elixir 实现的。其核心功能由 Erlang 标准库中的
:gen_server模块完成,而 Elixir 标准库中的GenServer模块则在此基础上进行了额外的封装(wrapping)。 在本节中,你将学习如何使用 GenServer 构建自己的服务进程。但在开始之前,我们先来探讨 OTP 行为模式(OTP behaviours)这一概念。
1、OTP behaviours#
在 Erlang 术语中,行为模式(behaviour) 是一种实现通用模式的框架代码。其核心逻辑通过行为模块(behaviour module) 对外暴露,开发者可通过实现对应的回调模块(callback module) 进行功能接入。回调模块需遵守行为模式定义的接口契约(contract),即必须实现并导出一组特定函数。行为模块通过动态调用这些回调函数,使开发者能在通用框架中注入定制化逻辑。
这正是 ServerProcess 的设计目标。它驱动了一个通用化的服务进程,要求具体实现方提供包含 init/0、handle_call/2 和 handle_cast/2 函数的回调模块。ServerProcess 是 行为模式(behaviour) 的一个简单示例。
Erlang 标准库包含以下 OTP 行为模式(OTP behaviours):
gen_server有状态服务进程的通用实现,支持并发系统中的容错恢复。- 典型场景:服务进程管理(如缓存服务、TCP 连接池)。
- 核心能力:同步调用(call)、异步消息(cast)、状态持久化、崩溃自动重启。
application组件与库的应用级封装框架,管理服务生命周期和依赖关系。- 典型场景:定义可启动/停止的服务单元(如 Web 服务、数据库连接池)。
- 核心能力:启动顺序控制、资源配置、监督树集成。
gen_event提供事件驱动编程支持,实现发布-订阅模型。- 典型场景:日志系统、实时通知、审计追踪。
- 核心能力:动态添加/删除事件处理器、事件广播、错误隔离。
gen_statem在具备状态的服务进程中运行有限状态机(FSM),支持复杂状态流转逻辑。- 典型场景:协议实现(如 TCP 状态机)、工作流引擎。
- 核心能力:状态转移规则定义、状态触发动作、条件守卫(guards)。
还有一种特殊的行为模式:Supervisor(监督者)行为模式
在 Erlang/OTP 中,Supervisor 是一种用于构建容错系统的核心行为模式,专门负责监控和管理子进程的生命周期。其核心目标是实现 "自愈式系统",即通过预定义的策略自动处理子进程的崩溃和重启,确保系统在部分故障时仍能维持整体可用性。
2、Erlang/OTP 核心行为模式对比#
以下是对 gen_server、application、gen_event、gen_statem 和 Supervisor 的全面对比,涵盖其设计目标、交互方式、回调机制及典型应用场景:#
1. 核心功能与设计目标#
| **行为模式** | **核心目标** | **关键特性** |
| **`gen_server`** | 实现**有状态服务进程**,处理同步/异步请求 | 维护内部状态,支持 **`call`**(同步)、**`cast`**(异步)消息,自动序列化请求。 |
| **`application`** | 管理**应用生命周期**与依赖关系 | 定义应用的启动/停止逻辑,集成监督树,协调资源分配。 |
| **`gen_event`** | 提供**事件驱动编程**支持,实现发布-订阅模型 | 动态添加/移除事件处理器,支持广播事件,隔离处理器故障。 |
| **`gen_statem`** | 实现**有限状态机(FSM)**,管理复杂状态迁移逻辑 | 定义状态转移规则,支持条件守卫(guards)、状态触发动作,适合多阶段流程控制。 |
| **`Supervisor`** | 构建**容错系统**,监控并重启子进程 | 定义重启策略(如 **`one_for_one`**),限制重启频率,隔离进程崩溃的传播。 |
2. 回调函数对比#
| **行为模式** | **必需回调函数** | **可选回调函数** |
| **`gen_server`** | **`init/1`**, **`handle_call/3`**, **`handle_cast/2`**, **`terminate/2`** | **`handle_info/2`**, **`code_change/3`** |
| **`application`** | **`start/2`**, **`stop/1`** | **`prep_stop/1`**, **`config_change/2`** |
| **`gen_event`** | **`init/1`**, **`handle_event/2`**, **`terminate/2`** | **`handle_call/2`**, **`handle_info/2`** |
| **`gen_statem`** | **`init/1`**, **`callback_mode/0`**, **`StateName/3`**(状态函数)或 **`handle_event/4`** | **`terminate/3`**, **`code_change/4`** |
| **`Supervisor`** | **`init/1`** | 无 |
3. 交互方式与容错机制#
| **行为模式** | **主要交互方式** | **容错机制** |
| **`gen_server`** | 同步 **`call`** / 异步 **`cast`** 消息 | 依赖 **`Supervisor`** 重启,自身无容错逻辑。 |
| **`application`** | 启动参数配置、依赖声明 | 作为监督树的根节点,崩溃会导致整个应用终止。 |
| **`gen_event`** | 事件广播(**`notify`**)、定向发送(**`sync_notify`**) | 单个事件处理器崩溃不影响其他处理器,可配置重启策略。 |
| **`gen_statem`** | 状态触发消息(事件驱动) | 类似 **`gen_server`**,依赖监督者重启,但可通过状态转移逻辑规避部分错误。 |
| **`Supervisor`** | 监控子进程状态,发送重启指令 | 自身无状态,通过重启策略(如 **`one_for_one`**)和频率限制(**`MaxR`**, **`MaxT`**)实现容错。 |
4. 典型应用场景#
| **行为模式** | **应用场景示例** |
| **`gen_server`** | 缓存服务、数据库连接池、实时计数器、TCP 通信服务。 |
| **`application`** | 微服务架构中的独立服务模块(如 REST API 服务)、分布式系统的顶层容器。 |
| **`gen_event`** | 日志系统(多输出源)、实时通知(邮件/SMS)、系统监控(CPU/内存事件)。 |
| **`gen_statem`** | 订单状态机(待支付→已支付→发货)、协议解析(如 HTTP 请求处理流程)、游戏角色状态管理。 |
| **`Supervisor`** | 进程监控树(如 Web 服务器的监听进程+工作进程组)、容错系统(自动恢复崩溃的数据库连接)。 |