Spec-Zone.ru › Phoenix

Phoenix.Socket поведение

Исходный код

Реализация сокета, которая мультиплексирует сообщения через каналы.

Phoenix.Socket используется как модуль для установки соединения между клиентом и сервером. После установки соединения начальное состояние сохраняется в структуре Phoenix.Socket.

Один и тот же сокет можно использовать для получения событий от разных транспортов. Phoenix поддерживает параметры websocket и longpoll при вызове Phoenix.Endpoint.socket/3 в эндпоинте. По умолчанию задано websocket, а longpoll также можно настроить явно.

socket "/socket", MyAppWeb.Socket, websocket: true, longpoll: false

Приведённая выше команда позволяет устанавливать входящие соединения с сокетом через WebSocket-соединение. Входящие и исходящие события направляются в каналы по топику:

channel "room:lobby", MyAppWeb.LobbyChannel

Дополнительную информацию о каналах см. в разделе Phoenix.Channel.

Поведение сокета

Обработчики сокетов подключаются в эндпоинтах и должны определять два обратных вызова:

  • connect/3 — получает параметры сокета и, если есть, сведения о соединении, а также аутентифицирует соединение. Должен возвращать структуру Phoenix.Socket, часто с пользовательскими assigns

  • id/1 — получает сокет, возвращённый connect/3, и возвращает идентификатор этого соединения в виде строки. id используется для идентификации соединений сокетов, часто принадлежащих определённому пользователю, что позволяет принудительно разрывать соединения. Для сокетов, не требующих аутентификации, можно вернуть nil

Примеры

defmodule MyAppWeb.UserSocket do
  use Phoenix.Socket

  channel "room:*", MyAppWeb.RoomChannel

  def connect(params, socket, _connect_info) do
    {:ok, assign(socket, :user_id, params["user_id"])}
  end

  def id(socket), do: "users_socket:#{socket.assigns.user_id}"
end

# Disconnect all user's socket connections and their multiplexed channels
MyAppWeb.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})

Поля сокета

  • :id — строковый идентификатор сокета
  • :assigns — map с assigns сокета, значение по умолчанию: %{}
  • :channel — текущий модуль канала
  • :channel_pid — pid канала
  • :endpoint — модуль эндпоинта, из которого был создан этот сокет, например: MyAppWeb.Endpoint
  • :handler — модуль сокета, из которого был создан этот сокет, например: MyAppWeb.UserSocket
  • :joined — присоединился ли сокет к каналу
  • :join_ref — ref, отправленный клиентом при присоединении
  • :ref — последний ref, отправленный клиентом
  • :pubsub_server — зарегистрированное имя pubsub-сервера сокета
  • :topic — строковый топик, например "room:123"
  • :transport — идентификатор транспорта, используемый для ведения журнала
  • :transport_pid — pid процесса транспорта сокета
  • :serializer — сериализатор сообщений сокета

Использование параметров

Для use Phoenix.Socket принимаются следующие параметры:

  • :log — уровень ведения журнала по умолчанию для действий сокета. По умолчанию — :info. Можно задать false, чтобы отключить ведение журнала

  • :partitions — каждый канал запускается под супервизором. Этот параметр задаёт количество супервизоров, запускаемых для обработки каналов. По умолчанию используется количество ядер.

Сборка мусора

Можно принудительно запустить сборку мусора в процессе транспорта после обработки больших сообщений. Например, чтобы запустить её из каналов, выполните:

send(socket.transport_pid, :garbage_collect)

Также можно настроить сокет эндпоинта на более частый запуск полной сборки мусора, задав параметр :fullsweep_after для WebSocket. Дополнительную информацию см. в разделе Phoenix.Endpoint.socket/3.

Обмен данными между клиентом и сервером

Кодирование данных сервера и декодирование данных клиента выполняются согласно сериализатору, определённому в Phoenix.Socket.Serializer. По умолчанию для передачи сообщений клиентам и от них используется кодирование JSON.

Функция сериализатора decode! должна возвращать Phoenix.Socket.Message, который передаётся каналам, за исключением следующих случаев:

  • "heartbeat" — события в топике "phoenix": следует просто отправить ответ OK
  • "phx_join" для любого топика — следует присоединиться к топику
  • "phx_leave" для любого топика — следует покинуть топик

У каждого сообщения также есть поле ref, используемое для отслеживания ответов.

Сервер может отправлять сообщения или ответы. Для сообщений ref уникально идентифицирует сообщение. Для ответов ref совпадает с ref исходного сообщения. Оба типа данных также содержат join_ref, который уникально идентифицирует текущий канал, к которому выполнено присоединение.

Реализация Phoenix.Socket также может отправлять специальные сообщения и ответы:

  • "phx_error" — в случае ошибок, например при аварийном завершении процесса канала или попытке присоединиться к каналу, к которому уже выполнено присоединение

  • "phx_close" — канал был корректно закрыт

В Phoenix входят реализации WebSocket и long polling на JavaScript, взаимодействующие с Phoenix.Socket. Их можно использовать как образец при создании собственных клиентов.

Пользовательские сокеты и транспорты

Дополнительную информацию о создании собственного сокета, не использующего каналы, или собственных транспортов для взаимодействия с другими сокетами см. в документации Phoenix.Socket.Transport.

Пользовательские каналы

Любой модуль можно указать как канал, если он реализует функцию child_spec/1. Функция child_spec/1 получает вызывающий процесс в качестве аргумента и должна возвращать спецификацию дочернего процесса, которая запускает процесс.

После инициализации процесс получит следующее сообщение:

{Phoenix.Channel, auth_payload, from, socket}

Пользовательская реализация канала ОБЯЗАНА вызвать GenServer.reply(from, {:ok | :error, reply_payload}) во время инициализации и передать пользовательский reply_payload, который будет отправлен клиенту в качестве ответа. Если этого не сделать, сокет будет заблокирован навсегда.

Пользовательский канал получает структуры Phoenix.Socket.Message в виде обычных сообщений от транспорта. Ответы на эти сообщения и пользовательские сообщения можно в любой момент отправить сокету, создав соответствующие структуры Phoenix.Socket.Reply и Phoenix.Socket.Message, закодировав их с помощью сериализатора и передав сериализованный результат транспорту.

Например, для обработки сообщений "phx_leave", которые рекомендуется обрабатывать во всех реализациях каналов, можно использовать следующий код:

def handle_info(
      %Message{topic: topic, event: "phx_leave"} = message,
      %{topic: topic, serializer: serializer, transport_pid: transport_pid} = socket
    ) do
  send transport_pid, serializer.encode!(build_leave_reply(message))
  {:stop, {:shutdown, :left}, socket}
end

Особое сообщение, доставляемое всем каналам, — это Broadcast с событием "phx_drain", отправляемое при остановке сокета во время завершения работы приложения. Обычно его обрабатывают, отправляя транспорту сообщение drain, которое приводит к его остановке:

def handle_info(
      %Broadcast{event: "phx_drain"},
      %{transport_pid: transport_pid} = socket
    ) do
  send(transport_pid, :socket_drain)
  {:stop, {:shutdown, :draining}, socket}
end

Кроме того, мы рекомендуем всем каналам отслеживать transport_pid на init и завершаться, если завершается транспорт. Мы также рекомендуем преобразовывать причины завершения :normal (обычно возникающие из-за закрытия сокета) в {:shutdown, :closed}, чтобы гарантировать разрыв связей при завершении канала (завершение :normal не разрывает связи):

def handle_info({:DOWN, _, _, transport_pid, reason}, %{transport_pid: transport_pid} = socket) do
  reason = if reason == :normal, do: {:shutdown, :closed}, else: reason
  {:stop, reason, socket}
end

Уровень сокета рассматривает завершение любого процесса как ошибку, если перед остановкой сокету не было отправлено сообщение {:socket_close, pid, reason}.

Пользовательские реализации каналов нельзя тестировать с помощью Phoenix.ChannelTest.

Краткое описание

Типы

t()

Обратные вызовы

connect(params, t)

Упрощённый вариант connect/3, который не получает connect_info.

connect(params, t, connect_info)

Получает параметры сокета и аутентифицирует соединение.

id(t)

Идентифицирует соединение сокета.

Функции

assign(socket, keyword_or_map)

Добавляет пары ключ/значение в assigns сокета. Принимает список ключевых слов, map или функцию с одним аргументом.

assign(socket, key, value)

Добавляет пару key/value в assigns объекта socket.

channel(topic_pattern, module, opts \\ [])

Определяет канал, соответствующий заданному топику и транспортам.

Типы

t()Исходный код

@type t() :: %Phoenix.Socket{
  assigns: map(),
  channel: atom(),
  channel_pid: pid(),
  endpoint: atom(),
  handler: atom(),
  id: String.t() | nil,
  join_ref: term(),
  joined: boolean(),
  private: map(),
  pubsub_server: atom(),
  ref: term(),
  serializer: atom(),
  topic: String.t(),
  transport: atom(),
  transport_pid: pid()
}

Обратные вызовы

connect(params, t)Исходный код

@callback connect(params :: map(), t()) :: {:ok, t()} | {:error, term()} | :error

Упрощённый вариант connect/3, который не получает connect_info.

Предоставлен для обратной совместимости.

connect(params, t, connect_info)Исходный код

@callback connect(params :: map(), t(), connect_info :: map()) ::
  {:ok, t()} | {:error, term()} | :error

Получает параметры сокета и аутентифицирует соединение.

Параметры и assigns сокета

Параметры сокета передаются клиентом и могут использоваться для проверки и аутентификации пользователя. После проверки в сокет можно добавить assigns по умолчанию, которые будут заданы для всех каналов, например:

{:ok, assign(socket, :user_id, verified_user_id)}

Чтобы отклонить соединение, верните :error или {:error, term}. Чтобы управлять ответом, который в таком случае получит клиент, задайте обработчик ошибок в конфигурации WebSocket.

Примеры проверки токена при подключении см. в документации Phoenix.Token.

id(t)Исходный код

@callback id(t()) :: String.t() | nil

Идентифицирует соединение сокета.

Идентификаторы сокетов — это топики, позволяющие идентифицировать все сокеты заданного пользователя:

def id(socket), do: "users_socket:#{socket.assigns.user_id}"

Это позволит отправить событие "disconnect" и завершить все активные сокеты и каналы заданного пользователя:

MyAppWeb.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})

Возврат nil делает этот сокет анонимным.

Функции

assign(socket, keyword_or_map)Исходный код

Добавляет пары ключ/значение в assigns сокета. Принимает список ключевых слов, map или функцию с одним аргументом.

Если передан список ключевых слов или map, он будет объединён с существующими assigns.

Если передана функция, она получает текущие assigns в качестве аргумента, а её возвращаемое значение объединяется с текущими assigns.

Примеры

iex> assign(socket, name: "Elixir", logo: "💧")
iex> assign(socket, %{name: "Elixir"})
iex> assign(socket, fn %{name: name, logo: logo} -> %{title: Enum.join([name, logo], " | ")} end)

assign(socket, key, value)Исходный код

Добавляет пару key/value в assigns объекта socket.

Чтобы добавить несколько пар ключ/значение, см. также assign/2.

Примеры

iex> assign(socket, :name, "Elixir")

channel(topic_pattern, module, opts \\ [])Исходный код

Определяет канал, соответствующий заданному топику и транспортам.

  • topic_pattern — строковый шаблон, например "room:*", "users:*" или "system"
  • module — модуль-обработчик канала, например MyAppWeb.RoomChannel
  • opts — необязательный список параметров, см. ниже

Параметры

  • :assigns — map с assigns сокета, объединяемый с сокетом при присоединении

Примеры

channel "topic1:*", MyChannel

Шаблоны топиков

Макрос channel принимает шаблоны топиков двух видов. В качестве последнего символа можно указать звёздочку (символ *), чтобы обозначить совпадение "topic:subtopic". Если указана обычная строка, обработчику канала будет соответствовать только этот топик. В большинстве случаев используется шаблон "topic:*", позволяющий гибче задавать область действия топиков.

Дополнительную информацию см. в разделе Phoenix.Channel.

Пакет Hex Предварительный просмотр Hex Скачать версию ePub

Создано с помощью ExDoc (v0.40.3) для языка программирования Elixir

© 2014 Chris McCord
Licensed under the MIT License.
https://phoenix.hexdocs.pm/Phoenix.Socket.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API