众所周知 Erlang/OTP 是一个具有高并发、低延时、高容错等特性的平台。
其三大 Behaviour :
- Gen_ Server(客户端服务器);
- Gen_ Fsm(有限状态机);
- Gen_ Event(事件通知),是其坚如磐石的系统的基石。
本场 Chat 将透过源码分析其中 Gen_Server 的实现原理并管中一窥 Erlang 的设计思想。
本场 Chat 主要内容:
- Erlang 异步编程背景知识。
- 异步编程模型。
- 阅读 Gen_Server 源码(如何使用、源码分析、代码调试)。
- 总结。
众所周知,Erlang/OTP 是一个具有高并发、低延时、高容错等等特性的平台,其三大 Behaviour:
- Gen_Server(客户端服务器)
- Gen_Fsm(有限状态机)
- Gen_Event(事件通知)
是其坚如磐石的系统的基石。本章节将透过源码分析其中 Gen_Server 的实现原理并管中一窥 Erlang 的设计思想。
二、背景知识在开始介绍 Gen_Server 实现原理之前,需要先介绍一下 Erlang 异步编程的思想与基础。Erlang 异步编程的基础就是 Erlang 进程和异步消息投递机制。
2.1 异步编程基础Erlang进程:
这里的 Erlang 进程并不是系统进程,而是通过 ErlangVM 实现创建的,一个 Erlang 进程大概占用 300 个字的内存空间,创建时间只有几微秒。ErlangVM 会为每个核启动一个线程充当调度器,调度器分配名下进程队列中的所有进程抢占运行时间片来达到并发执行的目的。在 ErlangVM 中,所有的 Erlang 代码都是运行在 Erlang 进程里面的。派生一个进程很简单,通过 spawn 函数,指定一个新进程运行的函数即可。
API 如下:
spawn(Fun) -> pid()spawn(Node, Fun) -> pid()spawn(Module, Function, Args) -> pid()
以匿名函数为例,在 Erlang shell 中运行:
spawn(fun() -> io:format(erlang:group_leader(),"hello world~n",[]),end).
屏幕中打印出
hello world
异步消息投递机制:
光有并行运行的 Erlang 进程是不够的,Erlang 通过异步消息来实现跨进程的同步与异步操作的。
Erlang 为每一个进程都配备了一个信箱,并且提供了发送消息和接受消息的命令。
发送消息的命令:
Pid ! Msg.
Pid
是目标进程,!
是发送消息的命令符,Msg
是消息体。
发送消息是异步执行的,发送消息后,本进程立即执行后续代码,消息投递到目标进程时会插入目标进程的信箱队列中,直到目标进程读取消息。
receive Msg -> do_something() after Timeout -> do_finish() end
以上代码就是目标进程处理消息的代码示例。receive 命令就是读取并接收消息的命令。这个 receive 命令有两层含义,按照信箱队列先进先出的原则读取一条消息;如果信箱是空的则等待信箱接收到消息为止,这是一个阻塞操作,会使进程主动交出运行时间片,直到信箱收到消息为止才会重新接受调度器分配的新时间片来继续运行,这个阻塞等待可以设置超时时间,超时后继续运行后续代码,不设置则一直等待收到消息为止。
2.2 异步编程模型Erlang 异步编程最常见的模型就是异步应答式模型,这个模型就是建立在前面介绍的基础上的。通过派生的进程,接收消息并作出应答,并且通过这个模型既可以实现异步的逻辑,也可以实现同步的逻辑。
见代码:
%%demo模块 -module(demo_server). -define(SERVER, demo_server). -export([start_server/0, loop/1]). -export([get/1, set/2, stop/0]). start_server() -> Pid = spawn_link(?MODULE, loop, [[]]), erlang:register(?SERVER, Pid), ok. loop(Data) -> receive {call, From, {get, Key}} -> Value = proplists:get_v