概述

如果我们需要管理多个进程,那么,就需要一个专门的 server 来集中监控和控制这些进程的状态,启停等。

OTP 平台中的 GenServer 就是对这个 server 通用部分的抽象。

利用 GenServer 中已经提供的通用操作, 可以很方便的开发出可靠,健壮的程序。

下面首先通过一个示例演示 GenServer 的方便和强大之处,然后再对其进行介绍。

GenServer 示例

这是一个 GenServer 管理多个进程的示例,模拟控制各个进程的启动,停止,以及状态查询。

defmodule ProcessMonitor do
use GenServer #====================================================
# api for clients
#====================================================
# start GenServer
def start(data, opt \\ []) do
GenServer.start_link(__MODULE__, data, opt)
end # add process which is controled by this GenServer
def process_add(server, name) do
GenServer.call(server, {:add, name})
end # get process status
def process_status(server, name) do
GenServer.call(server, {:status, name})
end # start a process by name
def process_start(server, name) do
GenServer.cast(server, {:start, name})
end # stop a process by name
def process_stop(server, name) do
GenServer.cast(server, {:stop, name})
end #====================================================
# callbacks for server
#====================================================
def init(data) do
{:ok, data}
end # handle status message synchronization
def handle_call({:status, name}, _from, data) do
val = Map.get(data, name, nil)
{:reply, val, data}
end # handle add message synchronization
def handle_call({:add, name}, _from, data) do
data = Map.put(data, name, "stopped")
{:reply, name, data}
end # handle start message asynchronization
def handle_cast({:start, name}, data) do
data = Map.put(data, name, "running")
{:noreply, data}
end # handle stop message asynchronization
def handle_cast({:stop, name}, data) do
data = Map.put(data, name, "stopped")
{:noreply, data}
end end

上面代码测试方法如下:

$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Interactive Elixir (1.2.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> {:ok, server} = ProcessMonitor.start(Map.new) # 创建 GenServer,并初始化一个 map 用于存储此server管理的 process 信息
{:ok, #PID<0.87.0>}
iex(2)> ProcessMonitor.process_status(server, "process01") # 创建 GenServer 后,默认没有管理任何进程,所以没有 process01 的信息
nil
iex(3)> ProcessMonitor.process_add(server, "process01") # 给 GenServer 增加一个被管理进程 process01
"process01"
iex(4)> ProcessMonitor.process_status(server, "process01") # 新加入的进程默认状态是 stopped,示例代码默认这么实现
"stopped"
iex(5)> ProcessMonitor.process_start(server, "process01") # 启动 process01
:ok
iex(6)> ProcessMonitor.process_status(server, "process01") # process01 状态变为 running
"running"
iex(7)> ProcessMonitor.process_stop(server, "process01") # 停止 process01
:ok
iex(8)> ProcessMonitor.process_status(server, "process01") # process01 状态变为 stopped
"stopped"
iex(9)> ProcessMonitor.process_add(server, "process02") # 再增加一个被管理进程 process02
"process02"
iex(10)> ProcessMonitor.process_start(server, "process02") # 启动 process02
:ok
iex(11)> ProcessMonitor.process_status(server, "process02") # process02 状态变为 running
"running"
iex(12)> ProcessMonitor.process_status(server, "process01") # process01 状态仍然是 stopped,不受 process02 的影响
"stopped"
iex(13)> ProcessMonitor.stop(server) # 停止 GenServer

上面的代码是用 mix 创建工程来运行的,mix 的使用方法可以参见 blog:mix 构建工具

GenServer 通用抽象简介

示例代码使用了 GenServer 中的几个关键函数: init handle_call handle_case

  • init: 这个函数在 GenServer.start_link 时执行,对 start_link 中的参数进行处理
  • handle_call: 这个函数接受同步消息并处理
  • handle_cast: 这个函数接受异步消息并处理

处理这3个常用的函数之外,GenServer 中的函数也不是很多,其他的函数,属性以及每个函数返回的值说明请参见:http://elixir-lang.org/docs/stable/elixir/GenServer.html

在上面的示例中,其实 client 也可以直接调用 GenServer 的 handle_call/handle_cast 来发送同步/异步消息,

我之所以封装了一些 api 给 client 调用,一方面,是为了简化客户端的调用(client 的 api 中参数更加简洁直观),

另一方面,将处理消息的代码和 发送消息的代码分开,便于以后扩展(因为,可能存在多个发送消息的处理都对应同一个消息处理)。

来源:http://blog.iotalabs.io/

elixir 高可用系列(二) GenServer的更多相关文章

  1. elixir 高可用系列 - 目录

    1. elixir 高可用系列(一) Agent 2. elixir 高可用系列(二) GenServer 3. elixir 高可用系列(三) GenEvent 4. elixir 高可用系列(四) ...

  2. elixir 高可用系列(五) Supervisor

    概述 OTP 平台的容错性高,是因为它提供了机制来监控所有 processes 的状态,如果有进程出现异常, 不仅可以及时检测到错误,还可以对 processes 进行重启等操作. 有了 superv ...

  3. elixir 高可用系列(四) Task

    概述 之前学习的 Agent,GenSever以及GenEvent,都是用来管理状态或者处理消息的. 但是在很多时候,我们需要的是执行某个任务,这时如果使用 GenSever 或者 GenEvent, ...

  4. elixir 高可用系列(三) GenEvent

    概述 GenEvent 是事件处理的通用部分的抽象. 通过 GenEvent ,我们给已有的服务 动态 的添加 事件处理. GenEevent 和 GenServer 的区别 之前已经介绍了 GenS ...

  5. elixir 高可用系列(一) Agent

    概述 elixir 本身是一种 immutable 的语言,默认情况下,进程间是不共享任何状态的,进程之间通过消息来交互. 而 Agent 则封装了一种进程间共享状态的方式,通过这种方式,不用显式的写 ...

  6. MySQL高可用系列之MHA(二)

    一.參数说明 MHA提供了一系列配置參数.深入理解每一个參数的详细含义,对优化配置.合理使用MHA非常重要.非常多高可用性也都是通过合理配置一些參数而实现的. MHA包含例如以下配置參数,分别说明例如 ...

  7. (5.8)mysql高可用系列——MySQL中的GTID复制(实践篇)

    一.基于GTID的异步复制(一主一从)无数据/少数据搭建 二.基于GTID的无损半同步复制(一主一从)(mysql5.7)基于大数据量的初始化 正文: [0]概念 [0.5]GTID 复制(mysql ...

  8. Mycat高可用解决方案二(主从复制)

    Mycat高可用解决方案二(主从复制) 系统部署规划 名称 IP 主机名称 用户名/密码 配置 mysql主节点 192.168.199.110 mysql-01 root/hadoop 2核/2G ...

  9. Docker 搭建pxc集群 + haproxy + keepalived 高可用(二)

    上一节我们有了两个分片的pxc集群,这一节我们接着安装haproxy和keepalived的实现集群的高可用 一.先下载haproxy的镜像 [root@localhost ~]# docker pu ...

随机推荐

  1. delphi XE Berlin ReadProcessMemory WriteProcessMemory

    delphi  XE,Berlin [dcc32 Error] Unit9.pas(93): E2033 Types of actual and formal var parameters must ...

  2. vsnprintf

    http://www.cplusplus.com/reference/cstdio/vsnprintf/ int vsnprintf (char * s, size_t n, const char * ...

  3. WPF 制作聊天窗口获取历史聊天记录

    腾讯从QQ2013版起开始在聊天记录里添加了历史记录查看功能,个人聊天窗口可以点击最上边的‘查看历史消息’,而群组里的未读消息可以通过滚动鼠标中键或者拖动滚动条加载更多消息,那这个用wpf怎么实现呢? ...

  4. Java值传递和引用传递详细解说

    前天在做系统的时候被Java中参数传递问题卡了一下,回头查阅了相关的资料,对参数传递问题有了新的了解和掌握,但是有个问题感觉还是很模糊,就是 Java中到底是否只存在值传递,因为在查阅资料时,经常看到 ...

  5. Installshield 打包安装包心得

     制作简单的安装软件 声明:下面的教程,是把读者当做完全没接触过IS的角度来制作的. 1. 启动InstallShield 12.建立一个InstallShield MSI Project,如图: 2 ...

  6. .NET/C#/Oracle数据库操作类

    public static class OracleHelper { //数据库连接字符串 private readonly static string connstr = Configuration ...

  7. JS打印页面

         打印 整个html页面(PS:样式要写在页面里面才能打印(就是用内部样式))             <a id="dayi" runat="server ...

  8. Spinal Tap Case

    function spinalCase(str) { // "It's such a fine line between stupid, and clever." // --Dav ...

  9. dede channelartlist 中引用channel 并且设置当前选择类的样式,currentstyle暂时没有效果.特发求助!!!!

    在dede中需要达到当前选择了该类,样式不同的话.如果是channelartlist 的话. 可以使用下面的情况: {dede:channelartlist typeid='6' row='3' cu ...

  10. 通过indexPath找到对应的cell

    在- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 这个方法中通过 ...