20250709-使用Elixir-OTP和Phoenix进行函数式Web开发
Table of Contents
GenServer入门总结
一、GenServer基础概念
- 重要性:GenServer是Elixir代码中的核心组件,熟练掌握它是提升Elixir开发水平的关键。
- 简单实现:在模块中添加`use GenServer`即可创建一个基础的GenServer。
二、启动GenServer
- 相关函数:GenServer模块提供了`start_link/3`和`start/3`函数用于生成新进程,参数包括模块名、初始状态和可选选项列表。
- 返回值:`start_link/3`成功时返回`{:ok, <PID>}`,失败时返回`{:error, <reason>}`,可通过模式匹配获取进程ID(PID)。
三、GenServer模式
- 三部分组成:
- 客户端函数:作为公共接口,可自定义名称和参数。
- GenServer模块函数:需遵循特定名称和参数数量,如`start_link/3`、`call/3`、`cast/2`。
- 回调函数:由模块函数触发,有固定名称和参数数量,如`init/1`、`handle_call/3`、`handle_cast/2`。
- 映射关系:`start_link/3`触发`init/1`,`call/3`触发`handle_call/3`,`cast/2`触发`handle_cast/2`。
四、消息传递
- 发送消息:使用`Kernel.send/2`向GenServer进程发送消息,如`send(game, :first)`。
- 处理消息:需实现`handle_info/2`回调函数处理消息,默认实现会发出警告。返回`{:noreply, state}`表示不回复调用者,保持或更新状态。
五、调用(Calls)
- 同步特性:GenServer调用是同步的,调用者会阻塞直到获取返回值。
- 回调函数:通过`handle_call/3`处理调用,返回`{:reply, 回复值, 新状态}`,其中回复值会返回给调用者,新状态更新进程状态。
- 客户端函数:可封装`GenServer.call/3`为公共接口,如`demo_call/1`,方便外部调用。
六、广播消息(Casts)
- 异步特性:Casts是异步的,不返回特定回复,调用者无需等待。
- 回调函数:通过`handle_cast/2`处理广播消息,返回`{:noreply, 新状态}`,无需回复调用者,只需更新状态。
- 客户端函数:封装`GenServer.cast/2`为公共接口,如`demo_cast/2`,简化使用。
七、总结与实践建议
- 通过本章学习,掌握了GenServer的基本使用,包括启动进程、消息传递、同步调用和异步广播。
- 在实际开发中,可根据需求实现相应的回调函数,结合客户端函数封装接口,构建功能完善的GenServer。
- 后续可删除示例中的临时回调函数和客户端函数,开始实际项目的开发。
容错机制总结
一、Elixir 与 Erlang 的容错特性
- 核心优势:并非防止错误,而是具备从运行时错误中优雅恢复的工具。
- 与其他语言的区别:多数语言依赖内置异常处理机制(需提前识别风险代码并包裹在 try-rescue 块中),但 Elixir 很少需要直接使用该机制。
二、OTP 团队的设计理念
- 背景需求:源于电话通信系统对高可用性的严格要求(需在自然灾害等极端情况下保持运行)。
- 核心逻辑:提前预测所有可能的故障几乎不可能,因此专注于故障恢复而非预防。
- 实现方式:通过“监督者行为(Supervisor Behaviour)”将错误处理代码与业务逻辑分离。
三、监督者(Supervisor)的工作机制
- 功能:创建监督进程,与其他进程建立链接并监控其状态,当被监控进程崩溃时重启它们。
- 优势:
- 分离关注点,使代码更清晰、易维护。
- 业务逻辑无需处理异常,可假设操作始终成功,监督者会在错误发生时提供支持。
四、监督者行为的设计原则
- 运行时错误的性质:大多数运行时错误是短暂的,由不良状态引起。
- 不良状态的解决方案:最佳方式是让进程崩溃,然后用良好状态重启。
- BEAM 系统的适配性:BEAM 中的进程小且独立,可将错误隔离在最小范围,重启时最大限度减少干扰。
五、后续内容预告
- 将从多个角度探讨进程监督,包括:
- 启动新进程的不同方式及其对进程交互的影响。
- 监督者与被监督进程的交互方式。
- 监督者重启崩溃进程的不同策略。
- 进程重启后恢复状态的不同方法。
监督策略
一、监督策略概述
- 进程依赖关系:进程间常存在相互依赖,子进程崩溃时,仅重启该进程可能无法稳定应用,监督者需依据重启策略决定重启范围。
- 影响策略选择的因素**
- 子进程间的关系:是相互依赖(一个崩溃会导致其他失效)还是完全独立。
- 子进程启动的时间顺序:部分进程依赖于之前启动的进程。
- 启动模式:监督者是在应用启动时一次性启动固定数量的子进程,还是在运行时动态启动和停止可变数量的子进程,如Islands项目属于后者。
二、具体监督策略
(一)One for One(一对一)
- 策略行为:当单个进程终止时,监督者仅重启该进程。
- 适用场景:被监督的进程组能够相互独立工作,即重启单个进程不会影响整个进程组的功能。
- 示例:一个监督者管理三个工作进程,其中一个崩溃,仅重启该崩溃进程。
(二)One for All(一对全)
- 策略行为:若单个进程终止,监督者会终止所有其他被监督进程,然后全部重启。
- 适用场景:进程组内的进程相互依赖,且依赖彼此的状态才能正常工作。
- 示例:同上设置,一个工作进程崩溃,监督者重启所有工作进程。
(三)Rest for One(为一而停)
- 策略行为:将被监督的进程组视为有时间顺序(按启动先后,从早到晚、从左到右)。若中间某个进程终止,监督者会终止该问题进程之后启动的所有进程,然后重启所有被终止的进程。
- 适用场景:适用于具有时间依赖关系的进程组,即新创建的进程依赖于之前启动进程的状态和完整性。
(四)Simple One for One(简单一对一)
- 策略行为:重启策略与“一对一”类似,单个进程终止时,监督者仅重启该进程。
- 适用场景:适用于需要动态启动和停止进程的场景,如Islands项目中会频繁启动和结束游戏进程。
三、总结
监督策略是定制监督者行为的重要方式之一,除了上述策略外,还有其他可定制的方面,文中后续将进一步探讨其他相关内容。