【问题标题】:Actively remove user from chat channel in Elixir主动从 Elixir 的聊天频道中删除用户
【发布时间】:2018-09-21 04:55:24
【问题描述】:

我是 Elixir 的初学者,我正在开发一个具有多个频道的简单聊天服务器。

我的频道是GenServer,它存储了一个用户列表(pid),还有一个映射(user ref -> pid)来处理用户进程崩溃(来自Elixir Guides)。但是由于它是一个以 refs 为键的映射,当我实现 remove_member_from_channel 函数时,我想删除 pid(映射的值),我该怎么做呢?还是我选择了错误的实现方式?

defmodule Chat.Channel do
  use GenServer

  # ......

  def add_member(channel, user) do
    GenServer.call(channel, {:add_member, user})
  end

  def remove_member(channel, user) do
    GenServer.call(channel, {:remove_member, user})
  end

  # ......

  def handle_call({:add_member, user}, _from, {members, refs}) do
    if Enum.member?(members, user) do
      {:reply, :already_added, {members, refs}}
    else
      new_members = [user | members]
      ref = Process.monitor(user)
      new_refs = Map.put(refs, ref, user)
      {:reply, :ok, {new_members, new_refs}}
    end
  end

  def handle_call({:remove_member, member}, _from, {members, refs}) do
    if Enum.member?(members, member) do
      new_members = List.delete(members, member)

      # What to do with refs??

      {:reply, :ok, {new_members, refs}}
    else
      {:reply, :member_not_added, {members, refs}}
    end
  end

  def handle_info({:DOWN, ref, :process, _pid, _reason}, {members, refs}) do
    {member, new_refs} = Map.pop(refs, ref)
    new_members = List.delete(members, member)
    {:noreply, {new_members, new_refs}}
  end
end

【问题讨论】:

    标签: elixir chat erlang-otp


    【解决方案1】:

    使用Enum.filter/2(或Enum.reject/2

    def handle_call({:remove_member, member}, _from, {members, refs}) do
      if Enum.member?(members, member) do
        new_members = List.delete(members, member)
        new_refs =
          Enum.filter(refs, fn
            {_ref, ^member} -> false
            _ -> true
          end)
        {:reply, :ok, {new_members, refs}}
      else
        {:reply, :member_not_added, {members, refs}}
      end
    end
    

    一般来说,我最好存储used_id ⇒ reference 地图,类似于:

    new_refs = Map.put(refs, user, ref) # or even better used.id
    

    这样以后可以简单地使用Map.delete/2

    ...
      new_members = List.delete(members, member)
      new_refs = Map.delete(refs, member) # or member.id
    

    【讨论】:

    • 是不是说,如果我们把它存储为user_pid = reference,当用户进程崩溃时,我们必须使用Enum.filter/2或者Enum.reject/2来删除它?因为我们只能在它崩溃时获得参考。
    • 呃,确实如此。那么,一个人可能会同时维护 both user ⇒ refref ⇒ user 映射。
    猜你喜欢
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多