• 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/

  • 相关阅读:
    mysql 数据库【目录】
    Django 模板层
    Django文件下载(通过反向解析)
    Django 的路由系统
    Linux 搭建Django环境 + nginx + virtualenv虚拟环境
    layui 框架之秒传文件 (前端分段 MD5 型成秒传)
    Bootstrap 使用小点总结
    Django 之数据表操作
    前端之旅【目录】
    学习中遇到的小坑坑
  • 原文地址:https://www.cnblogs.com/wang_yb/p/5491933.html
Copyright © 2020-2023  润新知