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)的工作机制

  • 功能:创建监督进程,与其他进程建立链接并监控其状态,当被监控进程崩溃时重启它们。
  • 优势:
    • 分离关注点,使代码更清晰、易维护。
    • 业务逻辑无需处理异常,可假设操作始终成功,监督者会在错误发生时提供支持。

四、监督者行为的设计原则

  1. 运行时错误的性质:大多数运行时错误是短暂的,由不良状态引起。
  2. 不良状态的解决方案:最佳方式是让进程崩溃,然后用良好状态重启。
  3. BEAM 系统的适配性:BEAM 中的进程小且独立,可将错误隔离在最小范围,重启时最大限度减少干扰。

五、后续内容预告

  • 将从多个角度探讨进程监督,包括:
    • 启动新进程的不同方式及其对进程交互的影响。
    • 监督者与被监督进程的交互方式。
    • 监督者重启崩溃进程的不同策略。
    • 进程重启后恢复状态的不同方法。

监督策略

一、监督策略概述

  • 进程依赖关系:进程间常存在相互依赖,子进程崩溃时,仅重启该进程可能无法稳定应用,监督者需依据重启策略决定重启范围。
  • 影响策略选择的因素**
    • 子进程间的关系:是相互依赖(一个崩溃会导致其他失效)还是完全独立。
    • 子进程启动的时间顺序:部分进程依赖于之前启动的进程。
    • 启动模式:监督者是在应用启动时一次性启动固定数量的子进程,还是在运行时动态启动和停止可变数量的子进程,如Islands项目属于后者。

二、具体监督策略

(一)One for One(一对一)

  • 策略行为:当单个进程终止时,监督者仅重启该进程。
  • 适用场景:被监督的进程组能够相互独立工作,即重启单个进程不会影响整个进程组的功能。
  • 示例:一个监督者管理三个工作进程,其中一个崩溃,仅重启该崩溃进程。

(二)One for All(一对全)

  • 策略行为:若单个进程终止,监督者会终止所有其他被监督进程,然后全部重启。
  • 适用场景:进程组内的进程相互依赖,且依赖彼此的状态才能正常工作。
  • 示例:同上设置,一个工作进程崩溃,监督者重启所有工作进程。

(三)Rest for One(为一而停)

  • 策略行为:将被监督的进程组视为有时间顺序(按启动先后,从早到晚、从左到右)。若中间某个进程终止,监督者会终止该问题进程之后启动的所有进程,然后重启所有被终止的进程。
  • 适用场景:适用于具有时间依赖关系的进程组,即新创建的进程依赖于之前启动进程的状态和完整性。

(四)Simple One for One(简单一对一)

  • 策略行为:重启策略与“一对一”类似,单个进程终止时,监督者仅重启该进程。
  • 适用场景:适用于需要动态启动和停止进程的场景,如Islands项目中会频繁启动和结束游戏进程。

三、总结

监督策略是定制监督者行为的重要方式之一,除了上述策略外,还有其他可定制的方面,文中后续将进一步探讨其他相关内容。

Date: 2025-07-09 Wed 15:13