文档章节

(整理)用Elixir做一个多人扑克游戏 3

ljzn
 ljzn
发布于 2016/10/05 21:07
字数 639
阅读 53
收藏 0

今天我们将为德州扑克游戏添加故障恢复能力。

OTP为我们准备好了构建容错程序所需要的工具。我们只需要定义正确的behavior 行为。

Supervisor

有了Supervisor,我们就只需要关心当进程崩溃时如何反应。首先,我们使用顶层的Supervisor——Application:

defmodule GenPoker do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec

    children = [
      worker(Poker.Bank, [])
    ]

    opts = [strategy: :one_for_one, name: GenPoker.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

在mix.exs中注册我们的应用模块:

def application do
  [mod: {GenPoker, []}]
end

当工作中的进程崩溃后,会新建一个新的进程,打开 iex -S mix 测试一下:

iex(1)> Process.whereis(Poker.Bank)      
#PID<0.93.0>
iex(2)> Process.whereis(Poker.Bank) |> Process.exit(:kill)
true
iex(3)> Process.whereis(Poker.Bank)                       
#PID<0.97.0>

The Table Supervisor

我们可以把牌桌和牌局进程放在同一个Supervisor下:

defmodule Poker.Table.Supervisor do
  use Supervisor

  def start_link(table_name, num_players) do
    Supervisor.start_link(__MODULE__, [table_name, num_players])
  end

  def init([table_name, num_players]) do
    children = [
      worker(Poker.Table, [table_name, num_players])
    ]

    supervise children, strategy: :one_for_one
  end
end

把这个Supervisor添加到顶层的Supervisor下:

def start(_type, _args) do
  import Supervisor.Spec

  children = [
    worker(Poker.Bank, []),
    supervisor(Poker.Table.Supervisor, [:table_one, 6])
  ]

  opts = [strategy: :one_for_one, name: GenPoker.Supervisor]
  Supervisor.start_link(children, opts)
end

添加hand 牌局

我们不希望牌局在玩家准备好之前自动启动,也不希望在牌局结束之后重启。首先,向 Table Supervisor 中添加一个函数:

def start_hand(supervisor, table, players, config \\ []) do
  Supervisor.start_child(supervisor,
    supervisor(Poker.Hand.Supervisor, 
      [table, players, config], restart: :transient, id: :hand_sup
    )
  )
end

我们使用了 transient 暂时策略,也就是它不会在普通的退出之后被重启。子进程是牌局的Supervisor:

defmodule Poker.Hand.Supervisor do
  use Supervisor

  def start_link(table, players, config) do
    Supervisor.start_link(__MODULE__, [table, players, config])
  end

  def init([table, players, config]) do
    hand_name = String.to_atom("#{table}_hand")
    children = [
      worker(Poker.Hand, [table, players, config, [name: hand_name]], restart: :transient)
    ]

    supervise children, strategy: :one_for_one
  end
end

之后我们会解释多加这一层Supervisor的原因。我们需要对Table Supervisor的init稍作修改:

def init([table_name, num_players]) do
  children = [
    worker(Poker.Table, [self, table_name, num_players])
  ]

  supervise children, strategy: :one_for_one
end

以及对deal 发牌消息的 handle_call:

def handle_call(:deal, _from, state = %{hand_sup: nil}) do
  players = get_players(state) |> Enum.map(&(&1.id))

  case Poker.Table.Supervisor.start_hand(
    state.sup, state.table_name, players
  ) do
    {:ok, hand_sup} ->
      Process.monitor(hand_sup)
      {:reply, {:ok, hand_sup}, %{state | hand_sup: hand_sup}}
    error ->
      {:reply, error, state}
  end
end

我们在收到deal消息后启动hand牌局,并使用之前创建的Hand Supervisor 来监控。

现在我们的Supervisor 树已经有了雏形,但我们的state 状态信息无法保存,它会在进程崩溃时消失。所以我们需要 ETS 来保存state。当崩溃次数达到一定限度,Supervisor就会放弃,并由上一级Supervisor来重启。

下一篇中,我们将把已有的程序导入Phoenix Channel 中。

© 著作权归作者所有

共有 人打赏支持
ljzn
粉丝 29
博文 69
码字总数 96245
作品 0
南平
程序员
(整理)用Elixir做一个多人扑克游戏 2

原文 现在我们已经做好了牌面大小的比较,游戏的流程,但还没有做玩家登陆,人数限制,甚至没有将奖金发送给赢家。接下来,让我们来完成它们。 玩家需要兑换游戏中的筹码才能开始游戏,在当不...

ljzn
2016/10/04
22
0
(整理)用Elixir做一个多人扑克游戏 1

原文 学习一门新的语言或框架,最好的方法就是做一些小项目。Elixir和Phoenix很适合用来做扑克应用。 洗牌 我们要做的是德州扑克,首先,需要牌组: 我们定义了一个能够给出一套洗好了的52张...

ljzn
2016/10/03
524
2
(整理)用Elixir做一个多人扑克游戏 4

sockets 和 channels 是Phoenix中用来实现实时效果的两大工具。 Sockets socket是用来连接客户端与服务器的,它使用endpoint来声明: Channels 客户端只有加入了channel之后才能发送消息。 ...

ljzn
2016/10/06
49
0
(译)循序渐进学习Elixir

——Chris Bell Learning Elixir at Made by Many 在今年奥兰多的Elixir大会上演讲之后,就经常有人问我:你们团队是如何学习Elixir和OTP的? 和学习其他语言一样,学习Elixir也需要付出时间...

ljzn
2016/09/29
115
0
独家首发 | NIPS 最佳论文视频解读!德州扑克背后的不完全信息博弈

美国时间, 2017 年 12 月 4 日 8:00。 全球机器学习顶级会议 NIPS 在美国长滩开幕了。 本年度 NIPS 将持续一周,你现在才想参加肯定来不及,因为票早就卖光了。 为了让你隔着太平洋都能跟上...

雷锋字幕组
2017/12/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

八大包装类型的equals方法

先看其中一个源码 结论:八大包装类型的equals方法都是先判断类型是否相同,不相同则是false,相同则判断值是否相等 注意:包装类型不能直接用==来等值比较,否则编译报错,但是数值的基本类型...

xuklc
38分钟前
1
0
NoSQL , Memcached介绍

什么是NoSQL 非关系型数据库就是NoSQL,关系型数据库代表MySQL 对于关系型数据库来说,是需要把数据存储到库、表、行、字段里,查询的时候根据条件一行一行地去匹配,当量非常大的时候就很耗...

TaoXu
昨天
0
0
890. Find and Replace Pattern - LeetCode

Question 890. Find and Replace Pattern Solution 题目大意:从字符串数组中找到类型匹配的如xyy,xxx 思路: 举例:words = ["abc","deq","mee","aqq","dkd","ccc"], pattern = "abb"abc ......

yysue
昨天
0
0
Linux | Redis

写在前面的话 常言道,不作笔记不读书。在下是深有体会啊,所以,跟我一起做下本节的笔记吧,或许多年以后,你一定会感谢今天的你。 安装 在官网的下载页 Redis Download 直接写了在Linux的安...

冯文议
昨天
1
0
NoSQL-memcached

NoSQL介绍 NoSQL叫非关系型数据库。而关系型数据库代表有MySQL。对于关系型数据库来说,是需要把数据存储到库、表、行、字段里,查询的时候根据条件一行一行地去匹配,当量非常大的时候就很...

ln97
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部