Elixir + Phoenix:如何使用Agent作为商店?

时间:2017-03-01 14:19:20

标签: elixir phoenix-framework

我有一个聊天应用程序,双方必须在进入聊天之前按“接受”。顺序如下:

  1. 甲方加入频道。 Agentstart_link与派对合作 A的电子邮件地址为密钥
  2. 乙方加入频道。 Agent 将{B}的电子邮件地址作为密钥
  3. start_link
  4. 甲方 按下接受。如果B尚未接受(代理。Agent将存储Party A's email -> Party B's email -> true,否则会发送消息 乙方也接受了乙方。
  5. 乙方按下接受。 如果A尚未接受,Agent将存储Party A's email -> Party B's email -> true,否则会向甲方发送消息 也接受了。
  6. 我现在遇到一个错误,它似乎不是

    2017-03-01T14:02:40.434791+00:00 app[web.1]: 14:02:40.433 [error] GenServer #PID<0.1743.0> terminating
    2017-03-01T14:02:40.434793+00:00 app[web.1]: ** (stop) exited in: GenServer.call(:"dhdjsjsjs@plabs.com", {:get, #Function<1.108716101/1 in VideoChat.CallChannel.handle_in/3>}, 5000)
    2017-03-01T14:02:40.434794+00:00 app[web.1]:     ** (EXIT) no process
    2017-03-01T14:02:40.434795+00:00 app[web.1]:     (elixir) lib/gen_server.ex:596: GenServer.call/3
    2017-03-01T14:02:40.434795+00:00 app[web.1]:     (video_chat) web/channels/call_channel.ex:152: VideoChat.CallChannel.handle_in/3
    2017-03-01T14:02:40.434796+00:00 app[web.1]:     (phoenix) lib/phoenix/channel/server.ex:226: anonymous fn/4 in Phoenix.Channel.Server.handle_info/2
    2017-03-01T14:02:40.434797+00:00 app[web.1]:     (stdlib) gen_server.erl:615: :gen_server.try_dispatch/4
    2017-03-01T14:02:40.434798+00:00 app[web.1]:     (stdlib) gen_server.erl:681: :gen_server.handle_msg/5
    2017-03-01T14:02:40.434798+00:00 app[web.1]:     (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
    

    这是我的频道:

      def join("user_pool:" <> email, %{ "app" => app }, socket) do
        Logger.info "PRODUCTION user_pool email: #{email}, app: #{app}"
        socket = assign(socket, :email, email)
        socket = assign(socket, :app, app)
    
        Agent.start_link(fn -> %{} end, name: :"#{email}") # <---------------- should start link here
        {:ok, socket}
      end
    
    
      def handle_in("approve_match", %{ "matched_client_email" => matched_client_email }, socket) do
        Logger.info "approve_match"
        current_user_email = socket.assigns[:email]
    
        Agent.update :"#{current_user_email}", fn state ->
          Map.put(state, matched_client_email, true)
        end
    
        match_accepted = Agent.get(:"#{matched_client_email}", &(Map.get(&1, current_user_email))) # <----------- breaks here. this is line 152
        Logger.info "#{matched_client_email} has approved #{current_user_email}: #{match_accepted}"
        case match_accepted do
          true ->
            VideoChat.Endpoint.broadcast(
              "user_pool:#{matched_client_email}",
              "match_accepted",
              %{ matched_client_email: socket.assigns[:email] }
            )
            {:noreply, socket}
          _ ->
            Logger.info "2nd person has yet to approve"
            {:noreply, socket}
        end
      end
    

    当我在控制台中运行测试行时似乎没有中断:

    iex(20)> Agent.start_link(fn -> %{} end, name: :"blah")
    {:ok, #PID<0.365.0>}
    iex(21)> Agent.get(:"blah", &(Map.get(&1, "sdfs")))
    nil
    

    我只是错误地使用Agent吗?

1 个答案:

答案 0 :(得分:1)

这是一条失败的行:

Agent.get(:"#{matched_client_email}" ...)

这是因为Agent未必开始。您在测试中检查的内容是不同的,以复制您应该执行的行为:

iex(20)> Agent.start_link(fn -> %{} end, name: :"blah")
{:ok, #PID<0.365.0>}
iex(21)> Agent.get(:"foo", &(Map.get(&1, "sdfs"))) # NOTE :foo

可以明确检查目标Agent是否已经开始,或try / catch Agent.get

除此之外,你在没有受到监督的情况下产生代理人;当Agent由于某种原因而失效时,可能会导致意外问题:它不会重新启动(保持关闭状态),您将会遇到上述行为。

我个人会选择一位受监督的Agent,以email作为关键持有一切。值可能会根据您的需要变得复杂,只需映射email%{}