elixir 高可用系列(二) GenServer

简介:

概述

如果我们需要管理多个进程,那么,就需要一个专门的 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/



本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/p/5491933.html,如需转载请自行联系原作者


目录
相关文章
|
11月前
|
人工智能 Linux 文件存储
旧台式电脑的 10 种用途
旧台式电脑的 10 种用途
717 14
|
机器学习/深度学习 供应链 算法
区块链与机器学习:未来科技交叉口的深度洞察
随着科技进步,区块链与机器学习成为焦点技术。区块链以去中心化和安全性革新金融、供应链等领域;机器学习通过算法促进各行业创新。二者结合,区块链提供可靠数据支持机器学习,而机器学习优化区块链性能。应用场景包括金融信用评估、供应链管理、医疗健康及智能合约等。面对数据隐私保护、算法优化等挑战,需跨学科合作并完善政策法规。展望未来,技术突破、产业应用拓展及跨学科人才培养将推动这一领域向前发展。
985 3
|
Java Shell Linux
11MyCat - Window下安装MyCat
11MyCat - Window下安装MyCat
306 0
|
NoSQL Shell 容器
Cgroup Freezer 【ChatGPT】
Cgroup Freezer 【ChatGPT】
|
运维 Cloud Native 关系型数据库
复盘:我在真实场景下对几款主流云原生数据库进行极限性能压测的一次总结!!(建议收藏)
最近几年,云数据库市场日趋繁荣,进入百花齐放、百家争鸣的时代,头部云计算厂商相继推出了自己的数据库产品,特别是亚马逊的Aurora、阿里云的PolarDB、华为云的GaussDB等等。
1219 1
复盘:我在真实场景下对几款主流云原生数据库进行极限性能压测的一次总结!!(建议收藏)
|
JSON NoSQL 编译器
工具使用教程(四) 【VSCode使用教程】
工具使用教程(四) 【VSCode使用教程】
550 0
|
机器学习/深度学习 程序员 API
【深度学习】基于卷积神经网络的验证码识别
【深度学习】基于卷积神经网络的验证码识别
468 0
|
Java 程序员
Java 格式转换:利用格式转换实现随机数生成随机 char 字母及 string 字母串
Java 格式转换:利用格式转换实现随机数生成随机 char 字母及 string 字母串
258 0
Java 格式转换:利用格式转换实现随机数生成随机 char 字母及 string 字母串
|
机器学习/深度学习 存储 测试技术
Python之 sklearn:sklearn中的RobustScaler 函数的简介及使用方法之详细攻略
Python之 sklearn:sklearn中的RobustScaler 函数的简介及使用方法之详细攻略
Kam
|
Java
java.lang.IllegalStateException: Method has too many Body parameters
java.lang.IllegalStateException: Method has too many Body parameters
Kam
880 0