在 Elixir Phoenix 中使用代理在进程之间共享状态

Sharing state between processes using agents in Elixir Phoenix

我有一个 HTTP 端点,它创建一个地图并使用 Agent 存储它。

我想在到达另一个端点时访问此地图。但是当我试图从 Agent 获取数据时,它返回的是空的。

有人可以确认这是否是使用代理的有效方案吗?如果是,我错过了什么?

代码:

defmodule BoardState do
  use Agent

  def start_link do
    Agent.start_link(fn -> %{} end, name: __MODULE__)
  end

  def add(game_id, board) do
    Agent.update(__MODULE__, fn state ->
      Map.put(state, game_id, board)
    end)
  end

  def reset do
    Agent.update(__MODULE__, fn _state -> %{} end)
  end

  def get(game_id) do
    Agent.get(__MODULE__, fn state ->
      Map.get(state, game_id)
    end)
  end

  def getKeys() do
    Agent.get(__MODULE__, fn state ->
      Map.keys(state)
    end)
  end

  def name() do
    Agent.agent()
  end
end

代理启动的代码

`defmodule TicTacToe.Application do
  use Application

  def start(_type, _args) do
    children = [
      TicTacToeWeb.Endpoint
    ]

    **BoardState.start_link()**

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

  def config_change(changed, _new, removed) do
    TicTacToeWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end 

我也用 Genserver 尝试过,但是当我点击第二个点时,我无法获取存储在第一个端点中的数据

我几乎一字不差地使用了你的 BoardState 模块代码,除了我更改了 start_link 以便它与 Supervisor API:

defmodule BoardState do
  ...

  def start_link(_) do
    Agent.start_link(fn -> %{} end, name: __MODULE__)
  end

  ...
end

我将 BoardState 添加到我的监督树中:

defmodule Test.Application do
  ...

  def start(_type, _args) do
    children = [
      TestWeb.Endpoint,
      # Notice I'm putting BoardState under a supervisor here.
      # In your case, you linked it to the process performing the startup,
      # which is a different thing.
      BoardState
    ]

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

  ...
end

然后我添加了一个简单的控制器来测试它:

defmodule TestWeb.PageController do
  use TestWeb, :controller

  def put(conn, params) do
    BoardState.add(params["key"], params["value"])
    Plug.Conn.resp(conn, 200, "OK\n")
  end

  def get(conn, params) do
    value = BoardState.get(params["key"])
    Plug.Conn.resp(conn, 200, "#{value}\n")
  end
end

还有几条路线:

defmodule TestWeb.Router do
  ...
  scope "/", TestWeb do
    ...
    get "/put/:key/:value", PageController, :put
    get "/get/:key", PageController, :get
  end
end

有了它,它似乎按预期工作,记住发送给它的数据:

rindr:test yapee$ curl localhost:4000/put/hello/world
OK
rindr:test yapee$ curl localhost:4000/get/hello
world

我希望您可以使用这些片段调试您的问题。我认为您的问题可能与您没有真正将 BoardState 置于监督之下,而是随机地 start_linking 监督有关。

我在 Elixir 1.9 上测试了来自此存储库 https://github.com/sayali-kotkar/tic_tac_toe 的代码。您的代码看起来可以接受。这对我有用,或者我误解了问题。

第一个请求:

curl -X POST localhost:4000/game

第一个回复:

{
   "board":{
      "Cell(1, 1)":"empty",
      "Cell(2, 1)":"empty",
      "Cell(3, 1)":"empty",
      "Cell(1, 2)":"empty",
      "Cell(2, 2)":"empty",
      "Cell(3, 2)":"empty",
      "Cell(1, 3)":"empty",
      "Cell(2, 3)":"empty",
      "Cell(3, 3)":"empty"
   },
   "game_id":67,
   "status":"success"
}

第二个请求:

curl -X PUT localhost:4000/game/67 \
--header "Content-Type:application/json" \
--data '{"player_id":0, "row": 1, "column": 2}'

第二个回复:

{
   "board":{
      "Cell(1, 1)":"empty",
      "Cell(2, 1)":"empty",
      "Cell(3, 1)":"empty",
      "Cell(1, 2)":0,
      "Cell(2, 2)":"empty",
      "Cell(3, 2)":"empty",
      "Cell(1, 3)":"empty",
      "Cell(2, 3)":"empty",
      "Cell(3, 3)":"empty"
   },
   "game_id":67,
   "status":"success"
}