5

Elixir Channels を使用してリアルタイム イベントを処理するアプリケーションを作成しています。クライアントごとに 1 つのソケットが開かれ、複数のチャネルを多重化できることを理解しています。したがって、私のアプリは、ユーザーが複数のグループ チャットに参加するチャット アプリケーションです。join メソッドが動的トピックを処理する MessageChannel という 1 つの Phoenix Channel があります。

def join("groups:" <> group_id, payload, socket) do
....

ジョンがグループ/トピック A と B に参加し、ボブがグループ/トピック B にのみ参加するとします。ジョンがグループ/トピック A にメッセージを送信するbroadcast!/3と、そのメッセージもボブに送信されますか? handle_inメッセージが送信されたトピック/グループのコンテキストがないためです。

グループ A に送信されたイベントをボブが受信しないようにするには、どうすればよいでしょうか。

4

3 に答える 3

3

channel実際には、 APIを使用してプロジェクトのソケット モジュールでトピックを定義する方法に基づいて、ソケット ルーティングが行われます。私の Slack クローンでは、3 つのチャンネルを使用しています。プレゼンスの更新を処理するシステム レベル チャネル、ユーザー チャネル、およびルーム チャネルがあります。

特定のユーザーは、0 または 1 つのチャネルにサブスクライブされます。ただし、ユーザーは多数のチャンネルに登録することができます。

特定の部屋に送信されるメッセージについては、部屋のチャネルを介してブロードキャストします。

特定のルームの未読メッセージ、通知、またはバッジを検出すると、ユーザー チャネルを使用します。各ユーザー チャネルには、ユーザーがサブスクライブしたルームのリストも保存されます (クライアントのサイド バーに表示されます)。

これらすべての秘訣は、主interceptに 、handle_outMy.Endpoint.subscribe、およびの 2 つのチャネル API を使用することhandle_info(%Broadcast{},socket)です。

  • ブロードキャストされたメッセージをinterceptキャッチして、無視するか、送信する前に操作します。
  • ユーザー チャネルでは、ルーム チャネルからブロードキャストされたイベントをサブスクライブします。
  • サブスクライブすると、ブロードキャストされたメッセージのトピック、イベント、およびペイロードを含む構造体をhandle_info使用して呼び出しが行われます。%Broadcast{}

ここに私のコードのいくつかの部分があります:

defmodule UcxChat.UserSocket do
  use Phoenix.Socket
  alias UcxChat.{User, Repo, MessageService, SideNavService}
  require UcxChat.ChatConstants, as: CC

  ## Channels
  channel CC.chan_room <> "*", UcxChat.RoomChannel    # "ucxchat:"
  channel CC.chan_user <> "*", UcxChat.UserChannel  # "user:"
  channel CC.chan_system <> "*", UcxChat.SystemChannel  # "system:"
  # ...
end

# user_channel.ex
 # ...
 intercept ["room:join", "room:leave", "room:mention", "user:state", "direct:new"]
 #...
 def handle_out("room:join", msg, socket) do
    %{room: room} = msg
    UserSocket.push_message_box(socket, socket.assigns.channel_id, socket.assigns.user_id)
    update_rooms_list(socket)
    clear_unreads(room, socket)
    {:noreply, subscribe([room], socket)}
  end
  def handle_out("room:leave" = ev, msg, socket) do
    %{room: room} = msg
    debug ev, msg, "assigns: #{inspect socket.assigns}"
    socket.endpoint.unsubscribe(CC.chan_room <> room)
    update_rooms_list(socket)
    {:noreply, assign(socket, :subscribed, List.delete(socket.assigns[:subscribed], room))}
  end

  # ...
  defp subscribe(channels, socket) do
    # debug inspect(channels), ""
    Enum.reduce channels, socket, fn channel, acc ->
      subscribed = acc.assigns[:subscribed]
      if channel in subscribed do
        acc
      else
        socket.endpoint.subscribe(CC.chan_room <> channel)
        assign(acc, :subscribed, [channel | subscribed])
      end
    end
  end
  # ...
end

クライアントの状態、エラー メッセージなど、特定のユーザーに関連するすべてのイベントにも user_channel を使用します。

于 2017-04-14T03:14:46.247 に答える