프로세스 및 채널에 대한 현재 상태 추적을 제공합니다.
이 동작은 주어진 주제에 대한 현재 상태 가져오기와 같은 현재 상태 기능을 제공하고 실시간으로 발생하는 참여 및 탈퇴 이벤트의 차이를 처리합니다. 이 모듈을 사용하면 감독자와 Phoenix.PubSub를 사용하여 현재 상태 업데이트를 브로드캐스트하는 Phoenix.Tracker 동작을 구현하는 모듈을 정의합니다.
업데이트를 브로드캐스팅하지 않고 추적 프로세스와 같이 Phoenix.Presence에서 제공하는 기능의 하위 집합만 사용하려는 경우 phoenix_pubsub 프로젝트의 Phoenix.Tracker 기능을 살펴보는 것이 좋습니다.
hoenix.Presence를 사용하는 애플리케이션 내에서 presence 모듈을 정의하여 시작하고 구성을 보유하는 :otp_app과 :pubsub_server를 제공합니다.
defmodule MyAppWeb.Presence do
use Phoenix.Presence,
otp_app: :my_app,
pubsub_server: MyApp.PubSub
end
:pubsub_server는 애플리케이션에서 실행 중인 기존 pubsub 서버를 가리켜야 하며, 새 애플리케이션의 경우 기본적으로 MyApp.PubSub로 포함됩니다.
다음으로 lib/my_app/application.ex의 감독 트리에 새 감독자를 추가합니다. PubSub 자식 뒤와 Endpoint 앞에 있어야 합니다.
children = [
...
{Phoenix.PubSub, name: MyApp.PubSub},
MyAppWeb.Presence,
MyAppWeb.Endpoint
]
추가되면 joining 후 channel에서 현재 상태를 추적할 수 있습니다.
defmodule MyAppWeb.MyChannel do
use MyAppWeb, :channel
alias MyAppWeb.Presence
def join("some:topic", _params, socket) do
send(self(), :after_join)
{:ok, assign(socket, :user_id, ...)}
end
def handle_info(:after_join, socket) do
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
online_at: inspect(System.system_time(:second))
})
push(socket, "presence_state", Presence.list(socket))
{:noreply, socket}
end
end
위의 예에서 Presence.track은 이 채널의 프로세스를 메타데이터 맵과 함께 소켓의 사용자 ID에 대한 현재 상태로 등록하는 데 사용됩니다. 다음으로 소켓 주제에 대한 현재 존재 정보가 "presence_state" 이벤트로 클라이언트에 푸시됩니다.
마지막으로 "presence_diff" 이벤트와 함께 실시간으로 발생하는 presence join 및 leave 이벤트의 차이가 클라이언트에 전송됩니다. diff 구조는 다음 형식의 :joins 및 :leaves의 맵입니다.
%{
joins: %{"123" => %{metas: [%{status: "away", phx_ref: ...}]}},
leaves: %{"456" => %{metas: [%{status: "online", phx_ref: ...}]}}
},
presence 데이터 구조에 대한 자세한 내용은 list/1을 참조하십시오.
현재 상태 메타데이터는 최소화해야 하며 사용자의 "온라인" 또는 "자리 비움" 상태와 같은 작고 일시적인 상태를 저장하는 데 사용해야 합니다. 데이터베이스에서 가져와야 하는 사용자 세부 정보와 같은 더 자세한 정보는 fetch/2 함수를 재정의하여 얻을 수 있습니다.
fetch/2 콜백은 list/1을 사용하고 업데이트할 때마다 트리거되며 모든 채널 구독자에게 정보를 브로드캐스트하기 전에 현재 상태 정보를 한 번만 가져오는 메커니즘 역할을 합니다. 이는 N개의 쿼리 문제를 방지하고 현재 상태 메타데이터를 확장하기 위해 격리된 데이터 가져오기를 그룹화할 단일 위치를 제공합니다.
이 함수는 :metas 키를 포함하여 설명된 Presence 데이터 구조와 일치하는 데이터 맵을 반환해야 하지만 추가 정보를 포함하도록 정보 맵을 확장할 수 있습니다. 예를 들어:
def fetch(_topic, presences) do
users = presences |> Map.keys() |> Accounts.get_users_map()
for {key, %{metas: metas}} <- presences, into: %{} do
{key, %{metas: metas, user: users[String.to_integer(key)]}}
end
end
Account.get_users_map/1은 다음과 같이 구현할 수 있습니다.
def get_users_map(ids) do
query =
from u in User,
where: u.id in ^ids,
select: {u.id, u}
query |> Repo.all() |> Enum.into(%{})
end
위의 fetch/2 함수는 데이터베이스에서 주어진 주제에 대해 등록된 모든 사용자를 가져옵니다. 그런 다음 존재 정보는 사용자 정보의 :user 키로 확장되는 동시에 원래 존재 데이터의 필수 :metas 필드를 유지합니다.
fetch 콜백이 호출될 때마다 별도의 프로세스에서 수행됩니다. 이러한 프로세스가 비동기식으로 실행되는 경우 모든 테스트가 끝날 때 종료되었음을 보장해야 하는 경우가 많습니다. 이것은 ExUnit의 on_exit 후크와 fetchers_pids 기능을 사용하여 수행할 수 있습니다.
on_exit(fn ->
for pid <- MyAppWeb.Presence.fetchers_pids() do
ref = Process.monitor(pid)
assert_receive {:DOWN, ^ref, _, _, _}, 1000
end
end)
get_by_key(arg1, key)
@callback get_by_key(Phoenix.Socket.t() | topic(), key :: String.t()) :: presences()
socket/topic-key 쌍에 대한 프레즌스 메타데이터 맵을 반환합니다.
list/1과 동일한 데이터 형식을 사용하지만 주제 및 키 쌍 아래의 존재에 대한 메타데이터만 반환합니다. 예를 들어, 키가 "user1"인 사용자가 두 장치에서 동일한 대화방 "room:1"에 연결된 경우 다음을 반환할 수 있습니다.
MyPresence.get_by_key("room:1", "user1")
[%{name: "User 1", metas: [%{device: "Desktop"}, %{device: "Mobile"}]}]
list/1과 마찬가지로 presence 메타데이터는 프레즌스 모듈의 fetch 콜백으로 전달되어 추가 정보를 가져옵니다.
list(arg1)
@callback list(Phoenix.Socket.t() | topic()) :: presences()
socket/topic에 대한 presence를 반환합니다.
presence 정보는 키별로 그룹화된 presence가 포함된 맵으로 반환되고, 문자열로 캐스팅되며, 다음 형식으로 메타데이터가 누적됩니다.
%{key => %{metas: [%{phx_ref: ..., ...}, ...]}}
예를 들어, 두 개의 다른 장치에서 온라인으로 ID가 123인 사용자와 단 하나의 장치에서 온라인으로 ID가 456인 사용자를 상상해 보십시오. 다음 현재 상태 정보가 반환될 수 있습니다:
%{"123" => %{metas: [%{status: "away", phx_ref: ...},
%{status: "online", phx_ref: ...}]},
"456" => %{metas: [%{status: "online", phx_ref: ...}]}}
맵의 키는 일반적으로 리소스 ID를 가리킵니다. 값에는 각 리소스에 대한 메타데이터 목록이 포함된 :metas 키가 있는 맵이 포함됩니다. 또한 모든 메타데이터 항목에는 지정된 키에 대한 메타데이터를 고유하게 식별하는 데 사용할 수 있는 :phx_ref 키가 포함됩니다. 메타데이터가 이전에 업데이트된 경우 이전 :phx_ref 값을 포함하는 :phx_ref_prev 키가 표시됩니다.
track(socket, key, meta)
@callback track(socket :: Phoenix.Socket.t(), key :: String.t(), meta :: map()) ::
{:ok, ref :: binary()} | {:error, reason :: term()}
채널의 프로세스를 현재 상태로 추적합니다.
추적된 현재 상태는 키별로 그룹화되어 문자열로 캐스팅됩니다. 예를 들어, 각 사용자의 채널을 함께 그룹화하려면 사용자 ID를 키로 사용하십시오. 각 프레즌스는 사용자의 온라인 상태와 같은 작고 일시적인 상태를 저장하기 위해 메타데이터 맵과 연관될 수 있습니다. 자세한 정보를 저장하려면 fetch/2를 참조하세요.
alias MyApp.Presence
def handle_info(:after_join, socket) do
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
online_at: inspect(System.system_time(:second))
})
{:noreply, socket}
end
track(pid, topic, key, meta)
@callback track(pid(), topic(), key :: String.t(), meta :: map()) ::
{:ok, ref :: binary()} | {:error, reason :: term()}
임의의 프로세스를 현재 상태로 추적.
topic 및 key로 모든 프로세스를 추적하는 것을 제외하고 track/3과 동일.
untrack(socket, key)
@callback untrack(socket :: Phoenix.Socket.t(), key :: String.t()) :: :ok
채널 프로세스 추적 중지.
untrack(pid, topic, key)
@callback untrack(pid(), topic(), key :: String.t()) :: :ok
채널 프로세스 추적 중지.
update(socket, key, meta)
@callback update(
socket :: Phoenix.Socket.t(),
key :: String.t(),
meta :: map() | (map() -> map())
) ::
{:ok, ref :: binary()} | {:error, reason :: term()}
채널 presence의 메타데이터 업데이트.
새 맵 또는 현재 맵을 가져와 새 맵을 반환하는 함수를 전달하여 현재 presence의 메타데이터를 교체합니다.
update(pid, topic, key, meta)
@callback update(pid(), topic(), key :: String.t(), meta :: map() | (map() -> map())) ::
{:ok, ref :: binary()} | {:error, reason :: term()}
채널 presence의 메타데이터 업데이트.
update/3과 동일하지만 임의의 프로세스가 있습니다.