在 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"
}
我有一个 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"
}