Отслеживание состояния пользователя с Phoenix Presence, React и Redux

В любом приложении вам может потребоваться отслеживать вовлеченность пользователей в функции реального времени. В игровом, чатном или другом режиме реального времени, в интерактивном приложении, мы хотим отслеживать такие вещи, как пользователи, присутствующие в чате / игре / в прямом эфире, или кто печатает сообщение / снимает пришельцев / потоковое вещание.

Например, допустим, вы поддерживаете RickChat, внутреннее приложение для чата, используемое Советом Рикс. В большой комнате для переговоров, доступной для Ricks из любой мыслимой реальности, вам нужно будет отобразить Ricks в настоящее время в чате, и даже указать, какой из них сейчас набирает Рик (возможно, идентифицируется по их размерности происхождения).

Конечно, вы уже используете огромную мощь Phoenix Channels для реализации этой передовой функциональности в реальном времени (вы так хороши в этом, кстати). Но как вы синхронизируете и делитесь такой информацией с состоянием на разных каналах?

Один из вариантов — создать собственный модуль, который использует GenServer от Elixir. Всякий раз, когда пользователь присоединяется / уходит или иным образом осмысленно взаимодействует с данным каналом, вы можете обновлять данные в своем GenServer и транслировать централизованное состояние во все соответствующие каналы. Это отличный вариант для отслеживания сложных изменений состояния.

Однако, когда дело доходит до отслеживания данных об участии пользователя в «комнате» и раскрытия данных для набора клиентов, Phoenix предоставляет мощный инструмент.

Что такое Phoenix Presence?

Модуль Phoenix Presence предоставляет API для регистрации информации по данной теме и предоставления этой информации всем каналам этой темы.

Модуль Phoenix Presence использует тип реплицированных данных типа Conflict Free или CRTDT для хранения и передачи информации , относящейся к теме. В отличие от централизованных хранилищ данных, таких как Redis, которые многие из нас знакомы с использованием в Rails, Phoenix Presence дает вам высокую доступность и производительность, потому что нам не нужно отправлять все данные через центральный сервер. Это также дает вам устойчивость к сбою, потому что система автоматически восстанавливается после сбоев.

Phoenix Presence отличается тем, что звучит так: отслеживание присутствия пользователей. Мы будем внедрять Phoenix Presence для отслеживания того, кто в сети, в данной среде, подобной чат-комнате, и которая в настоящее время печатает.

Приложение

Мы создадим функцию отслеживания пользователей для Phoenix Pair , приложения для создания пар с интерфейсом React + Redux и Phoenix API. Приложение позволяет пользователям выбирать из списка проблем с кодом. Изнутри заданной комнаты вызова в /challenges/:id пользователи могут взаимодействовать с решением кода, введя вместе в общий текстовый редактор.

Функцию, которую мы будем использовать для создания Phoenix Presence, можно увидеть выше:

  • Список участников с правой стороны.
  • «Индикатор ввода» свечения + « … », который окружает имя пользователя, который в настоящее время печатает в общем текстовом редакторе.

Наше приложение в настоящее время реализует функциональные возможности совместного использования в режиме реального времени с использованием Phoenix Channels. Каждая «комната вызова» по адресу /challenges/:id представляет собой тему канала. Когда пользователь «присоединяется» к комнате, для них открывается новое соединение с этим тестом.

Мы начнем с функциональных возможностей пользователя. Когда пользователь присоединяется к комнате, мы должны не только подключать их к каналу с данной темой, но и отслеживать их присутствие в комнате и транслировать это присутствие на все каналы с этой темой. Давайте начнем!

Настройка Phoenix Presence

Во-первых, нам нужно настроить Phoenix Presence в нашем приложении Phoenix.

Использование модуля Phoenix Presence

Во-первых, мы определим модуль ChallengePresence и скажем ему использовать модуль PhoenixPresence .

Нам также необходимо предоставить наш модуль:
* Наш otp_app , который содержит нашу конфигурацию приложения * Наш сервер PubSub, PhoenixPair.PubSub , который бесплатно входит в наше приложение Phoenix.

# lib/phoenix_pair/challenge_presence.ex

defmodule PhoenixPair.ChallengePresence do

use Phoenix.Presence, otp_app: :phoenix_pair,

pubsub_server: PhoenixPair.PubSub

end

Использование модуля PhoenixPresence дает нам доступ к поведению Phoenix Presence и API.

Нам нужно сделать еще одну вещь, чтобы запустить наш ChallengePresence — мы должны добавить его в наше дерево супервизора.

# lib/phoenix_pair.ex

def start(_type, _args) do

import Supervisor.Spec

 

children = [

supervisor(PhoenixPair.Repo, []),

supervisor(PhoenixPair.Endpoint, []),

supervisor(PhoenixPair.ChallengePresence, [])

]

end

Теперь мы готовы использовать наш модуль ChallengePresence для отслеживания событий, связанных с подключением пользователей.

Пользователь присоединяется

Когда пользователь «присоединяется» к комнате для вызова? Когда они посещают /challenges/:id . В нашем приложении React + Redux это приведет к установке компонента show challenge.

Мы уже используем метод жизненного цикла componentDidMount для отправки действия, которое отправляет сообщение, чтобы присоединиться к каналу с заданной задачей. Мы воспользуемся существующей архитектурой. Когда канал получает событие присоединения пользователя, он будет отслеживать присутствие нового пользователя через наш модуль ChallengePresence и передавать это присутствие всем подписчикам.

React будет слушать эту трансляцию. Когда он получает данные о присутствии пользователя, мы отправим действие нашему редуктору, чтобы обновить состояние приложения React с обновленным списком существующих пользователей.

Отправка действия

Мы отправим наше действие из метода жизненного цикла componentDidMount на нашем вызове show component:

import Actions from ‘../../actions/currentChallenge’;

 

componentDidMount() {

const {dispatch, socket, params} = this.props;

dispatch(Actions.connectToChannel(socket, params.id))

}

Наше действие делает несколько вещей:

  • Запускает событие join на нашем канале
  • Прослушивает положительный ответ от этого события объединения
  • Отправляет действие по обновлению состояния приложения React с выбранным вызовом
  • Устанавливает пустой объект, заданный переменной presences , который мы будем использовать для отслеживания присутствия пользователя в интерфейсе.

const Actions = {

connectToChannel: (socket, challengeId) => {

return dispatch => {

const channel = socket.channel(`challenges:${challengeId}`);

let presences = {};

channel.join().receive(‘ok’, (response) => {

dispatch({

type: Constants.SET_CURRENT_CHALLENGE,

challenge: response.challenge,

channel: channel

})

});

}

}

}

Теперь, когда мы видим, как React отправляет событие присоединения пользователя к нашему каналу вызова, идентифицированному через challengeId , давайте научим наш канал отслеживать это событие с помощью Phoenix Presence.

Канал вызова

Наш ChallengeChannel ответит на получение сообщения join , извлекая вызов с данным идентификатором из базы данных, отправив сообщение after_join и отвечая клиенту сообщением :ok и объектом вызова.

after_join отслеживания пользователя происходит в функции after_join . Здесь мы добавляем нашего пользователя в хранилище данных ChallengePresence существующих пользователей для этого канала. Затем мы транслируем событие «presence_state» по всем каналам с этой темой.

defmodule PhoenixPair.ChallengeChannel do

use PhoenixPair.Web, :channel

alias PhoenixPair.{Challenge, User, Message, Chat}

alias PhoenixPair.ChallengePresence

 

def join(«challenges:» <> challenge_id, _params, socket) do

challenge = get_challenge(challenge_id) # get challenge record from the DB with this helper method, definition not shown

send(self, {:after_join, challenge})

 

{:ok, %{challenge: challenge}, assign(socket, :challenge, challenge)}

end

 

def handle_info({:after_join, challenge}, socket) do

ChallengePresence.track_user_join(socket, current_user(socket))

push socket, «presence_state», ChallengePresence.list(socket)

{:noreply, socket}

end

Давайте сначала посмотрим на наш код отслеживания пользователей.

Отслеживание присутствия пользователей

Использование модуля PhoenixPresence в ChallengePresence дает нам доступ к функции track . Эта функция регистрирует процесс этого канала как присутствие под ключом верхнего уровня идентификатора пользователя, указывая на карту метаданных.

Я завершил использование PhoenixPresence.track в аккуратной функции, track_user_join . Давайте взглянем:

# lib/phoenix_pair/challenge_presence.ex

def track_user_join(socket, user) do

ChallengePresence.track(socket, user.id, %{

typing: false,

first_name: user.first_name,

user_id: user.id

})

end

Функция track добавляет пользователя в коллекцию пользователей хранилища данных Presence для этой темы. Он поддерживает это хранилище данных в следующем формате:

%{

«1» => %{

metas: [%{

typing: false,

first_name: «Sophie»,

phx_ref: «xxxx»

}]

},

«2» => %{

metas: [%{

typing: false,

first_name: «Antoin»,

phx_ref: «xxxx»

}]

}

}

Событие Presence Diff

Это новое отслеживание пользователя для данной темы вызова автоматически транслирует событие «presence_diff» на все соответствующие каналы. Передаваемая информация делится на ключи joins и leaves .

%{

joins: %{

«1» => %{

metas: [%{

typing: false,

phx_ref: «xxxx»,

first_name: «Sophie»

}]

}

},

leaves: %{

«2» => %{

metas: [%{

typing: false,

phx_ref: «xxxx»,

first_name: «Antoin»

}]

}

}

Каждый ключ верхнего уровня — это идентификатор пользователя, определяемый вторым аргументом, который мы передали для track . Каждая коллекция метаданных определяется третьим аргументом, который мы передали в track .

В интерфейсе наше приложение React будет прослушивать это событие «presence_diff» через channel.on(«presence_diff», response) , в котором response представляет собой полезную нагрузку diff, описанную выше.

// actions/currentChallenge.js

import {Presence} from ‘phoenix’;

const Actions = {

connectToChannel: (socket, challengeId) => {

return dispatch => {

const channel = socket.channel(`challenges:${challengeId}`);

channel.on(«presence_diff», (response) => {

presences = Presence.syncDiff(presences, response);

syncPresentUsers(dispatch, presences);

})

}

}

}

Мы можем использовать класс Presence предоставленный нам Phoenix для согласования текущего списка присутствий с полезной нагрузкой diff.

presences = Presence.syncDiff(presences, response);

Обратите внимание на то, как мы переставляем нашу переменную presences , которая начиналась как пустой объект, на карту пользовательских присутствий. Наша переменная presences действует как мини-хранилище данных, удерживая текущий список присутствий. Это позволяет нам согласовывать предыдущее и новое состояние присутствия каждый раз, когда событие «presence_diff» передается на канал.

Когда мы получим наш обновленный список присутствий, мы передадим его вспомогательной функции syncPresentUsers .

const syncPresentUsers = (dispatch, presences) => {

const participants = [];

Presence.list(presences).map(p => {participants.push(p.metas[0])})

dispatch({

type: Constants.CURRENT_CHALLENGE_PARTICIPANTS,

participants

});

};

Эта вспомогательная функция использует функцию Presence.list для сбора списка существующих метаданных пользователя и отправки действия, содержащего эту коллекцию, в редуктор.

Затем редуктор обновит состояние с новым списком присутствий пользователей:

[{1: {first_name: «Sophie», typing: «false», …}]

// reducers/currentChallenge.js

import Constants from ‘../constants’;

const initialState = {

currentChallenge: {},

participants: [],

channel: null

};

export default function reducer(state = initialState, action = {}) {

switch (action.type) {

case Constants.CURRENT_CHALLENGE_PARTICIPANTS:

return {…state, participants: action.participants}

}

}

Изменение состояния редуктора вызовет вызовы, которые демонстрирует компонент для повторного рендеринга с новым списком участников.

Событие Presence State

Событие Presence Diff будет передавать информацию о наличии различий присутствия всем подписчикам. Но как насчет клиента, который инициировал это событие? Как этот клиент получит текущее состояние присутствия, когда они прибудут в комнату для вызова?

Вот почему мы передаем событие Presence State в нашей after_join channel after_join :

# challenge_channel.ex

def handle_info({:after_join, challenge}, socket) do

ChallengePresence.track_user_join(socket, current_user(socket))

push socket, «presence_state», ChallengePresence.list(socket)

{:noreply, socket}

end

Эта строка:

push socket, «presence_state», ChallengePresence.list(socket)

отправляет событие «presence_state» для недавно подписанного клиента с полезной нагрузкой текущего списка присутствий для данного канала.

В интерфейсе React будет прослушивать это событие через channel.on(«presence_state», state_payload) .

// actions/currentChallenge.js

channel.on(«presence_state», (response) => {

presences = Presence.syncState(presences, response);

syncPresentUsers(dispatch, presences);

})

Здесь мы используем функцию syncState класса syncState . Эта функция будет сверять список текущих присутствий со списком присутствий, включенных в полезную нагрузку события. Затем мы передаем этот обновленный список нашей функции syncPresentUsers , которая будет использовать редуктор для обновления, как показано в предыдущем разделе.

Мы рассмотрели, что происходит, когда пользователь присоединяется к комнате для вызова. Но что происходит, когда пользователь уходит?

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

Объем работы, которую мы должны сделать для трансляции пользовательского события (подсказка: очень мало),  действительно иллюстрирует силу модуля Phoenix Presence.

Мы будем использовать метод жизненного цикла componentWillUnMount для отправки действия, removeParticipant :

// challenges show component

componentWillUnmount() {

const {channel, dispatch} = this.props;

dispatch(Actions.removeParticipant(channel))

}

Это простое действие. Он отправляет сообщение о leave на данный канал:

// actions/currentChallange.js

removeParticipant: (channel) => {

return dispatch => {

channel.leave();

}

}

Это автоматически запускает событие « Presence Diff» . Наш канал уже знает, как реагировать на события Presence diff и согласовывать присутствие пользователей с помощью функции Presence.syncDiff , так что это все, что нам нужно сделать, чтобы заставить React правильно обновить его состояние, когда пользователь покидает комнату для вызова. Так круто!

Прежде чем идти, давайте рассмотрим использование Phoenix Presence для отслеживания пользовательского события: пользовательский ввод.

Пользовательское событие: пользовательский ввод

Мы уже обсуждали, что нам нужно, чтобы имя пользователя, который печатает, выделяет и добавляет « … », чтобы указать это состояние ввода другим пользователям. Мы будем отслеживать и транслировать, будет ли текущий пользователь печатать с помощью Phoenix Presence.

Отправка действия

Когда пользователь updateChallengeResponse решение для вызова кода в общий текстовый редактор, наш компонент будет вызывать функцию updateChallengeResponse , которая будет вызывать функцию создателя действия:

// challenge show component

updateChallengeResponse(text) {

const {dispatch, channel, currentUser} = this.props;

dispatch(Actions.updateResponse(channel, text, currentUser));

}

Давайте посмотрим на updateResponse действия updateResponse :

// actions/currentChallenge.js

updateResponse: (channel, codeResponse, currentUser) => {

return dispatch => {

channel.push(«response:update», {response: codeResponse, user_id: currentUser.id})

}

}

Здесь мы отправляем сообщение «response:update» на наш канал с полезной нагрузкой нового текста для ответа на вызов кода вместе с идентификатором текущего пользователя.

Обновление Presence Meta Data пользователя

Наш ChallengeChannel ответит на сообщение «response:update» , обновив атрибут response на вызов в базе данных и обновив атрибут typingтекущего пользователя в нашем хранилище данных ChallengePresence .

# challenge_channel.ex

def handle_in(«response:update», %{«response» => response, «user_id» => user_id}, socket) do

case

Challenge.update(current_challenge(socket), response) do

{:ok, challenge}

->

ChallengePresence.do_user_update(socket, current_user(socket), %{typing: true})

broadcast! socket, «response:updated», %{challenge: challenge}

{:noreply, socket}

{:error, changeset} ->

{:reply, {:error, %{error: «Error updating challenge»}}, socket}

end

end

Линия, которая действительно нас волнует, здесь:

ChallengePresence.do_user_update(socket, current_user(socket), %{typing: true})

Мы вызываем вспомогательную функцию do_user_update , которую мы определили в нашем модуле ChallengePresence . Давайте взглянем.

# challenge_presence.ex

def do_user_update(socket, user, %{typing: typing}) do

ChallengePresence.update(socket, user.id, %{

typing: typing,

first_name: user.first_name,

user_id: user.id

})

end

Использование модуля PhoenixPresence дает нам доступ к поведению update . Мы передаем ключ, который хотим обновить, наш идентификатор пользователя и полезную нагрузку, к которой мы хотим обновить meta ключ под этим идентификатором пользователя.

К счастью для нас, функция update запускает событие Presence Diff для трансляции. Таким образом, нам не нужно добавлять какой-либо код в интерфейсе , чтобы React узнал о обновленном состоянии пользователя.

Вывод

Мы видели, что Phoenix Presence — действительно мощный инструмент для отслеживания присутствия пользователей для данного канала. В частности, предложение на передней панели упрощает синхронизацию данных о присутствии после того, как были запущены события присутствия. В целом, Phoenix Presence отлично играет с React + Redux, что позволяет нам синхронизировать состояние канала с состоянием нашего приложения React без написания тонны кода.

Счастливое кодирование!

Использование Agent для Maintain Channel State в Phoenix

В этом посте мы создадим наш собственный модуль с поддержкой агента, чтобы отслеживать состояние канала, которое не относится к присутствию пользователя.

В нашем приложении для парного обмена общий текстовый редактор сопровождается раскрывающимся меню языков программирования. Если один пользователь выбирает конкретный язык, скажем, Ruby, этот выбор должен быть передан всем другим пользователям в комнате для вызова, и их языковой выбор также должен быть обновлен. Кроме того, когда пользователь приходит в комнату для вызова, их выбранный язык должен автоматически устанавливаться на тот язык, который выбран в данный момент для выбранного класса. Эта функциональность в режиме реального времени поддерживается программой ChallengeChannel .

Наша функция должна вести себя следующим образом:

Почему Agent?

Agent — супер простая оболочка вокруг состояния.

Часто в Elixir существует потребность разделить или сохранить состояние, к которому необходимо получить доступ из разных процессов или к тому же процессу в разные моменты времени. Модуль Agent предоставляет базовую реализацию сервера, которая позволяет извлекать и обновлять состояние через простой API. — Hex Docs

Agent, который построен поверх GenServer, ведет себя аналогично GenServer. Оба позволяют нам запускать процесс отслеживания состояния и поддержки централизованного хранилища данных, обработки и реагирования на события ошибок и управления процессом процесса из дерева наблюдения.

Agent предоставляет более простой интерфейс, чем GenServer. Мы можем получить модуль с поддержкой агента и запустить его, написав меньше кода, чем с помощью GenServer

Мы разворачиваем процесс агента и регистрируем его как глобальный процесс, управляемый деревом надзора нашего приложения. Таким образом, мы можем использовать Agent для обмена информацией (в данном случае, с выбранным языком вызова) по всем каналам.

Давайте начнем!

Определение Agent Module

Мы определим наш модуль с поддержкой агента, ChallengeChannel.Monitor в lib/phoenix_pair/challenge_channel/monitor.ex .

defmodule PhoenixPair.ChallengeChannel.Monitor do

def start_link(initial_state) do

Agent.start_link(fn -> initial_state end, name: __MODULE__)

end

end

Функция start_link сообщает нашему модулю, поддерживающему Agent, как запустить процесс агента. Он будет вызван нашим деревом наблюдения, когда приложение запустится.

Наблюдение за агентом

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

Мы добавим наш мониторный модуль в качестве одного из рабочих, которым будет управлять наше приложение:

# lib/phoenix_pair.ex

def start(_type, _args) do

import Supervisor.Spec

children = [

supervisor(PhoenixPair.Repo, []),

supervisor(PhoenixPair.Endpoint, []),

worker(PhoenixPair.ChallengeChannel.Monitor, [%{}])

]

end

Функция worker принимает первый аргумент процесса, который мы хотим запустить и контролировать. Он принимает во втором аргументе список параметров для перехода к функции start_link контролируемого модуля.

Мы определяем начальное состояние, которое передается в Monitor.start_link/1 как пустую карту. Это связано с тем, что мы будем строить состояние каждого канала, поскольку пользователи взаимодействуют с каждым каналом.

Теперь мы готовы выстроить функциональность нашего модуля.

Использование агента для поддержания состояния

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

Таким образом, в функции join нашего канала мы будем вызывать наш модуль Monitor чтобы предоставить нам состояние канала, если такое состояние существует, или инициализировать состояние канала, если этот пользователь является первым посетителем.

# challenge_channel.ex

def join(«challenges:» <> challenge_id, _params, socket) do

challenge = Repo.get(Challenge, id)

send(self, {:after_join, challenge})

{:ok, %{challenge: challenge}, assign(socket, :challenge, challenge)}

end

def handle_info({:after_join, challenge}, socket) do

Monitor.get_challenge_state(challenge.id)

broadcast! socket, «user:joined», %{challenge_state: challenge_state(challenge.id)}

{:noreply, socket}

end

Здесь мы :after_join нашему каналу ответить на сообщение :after_join , спросив нашего Monitor о состоянии канала вызова и передав его клиенту.

Поэтому наш Monitor должен иметь возможность реагировать на функцию get_challenge_state с состоянием данного вызова, то есть на выбранном в данный момент языком для этой задачи.

Функция #get_challenge_state

Агент, такой как GenServer, обеспечивает разделение между клиентом и сервером .

Действия, которые мы выполняем внутри сервера, являются атомарными — они блокируют сервер до завершения действия. Это означает, что мы, возможно, захотим дважды подумать, прежде чем выполнять дорогостоящие действия на сервере Агента, поскольку он задерживает любые другие действия, выполняемые на этом сервере.

С другой стороны, выполнение действия в клиенте, копирование по текущему состоянию процесса агента клиенту и работа с ним там, может создать условие гонки. В состоянии гонки процесс №1 может выполняться на одной клиентской копии состояния, а процесс №2 может создать новую версию состояния до завершения процесса №1.

Операция состояния выборки для заданного идентификатора вызова не является дорогостоящей, и мы не хотим сталкиваться с состоянием гонки, в котором одному пользователю удается обновить выбранный язык до того, как другой пользователь закончит получение / обновление этого языка.Поэтому мы будем использовать серверные процессы для взаимодействия с состоянием нашего агента.

Чтобы выполнить процесс на сервере Агента, мы используем анонимную функцию для работы в состоянии:

# lib/phoenix_pair/challenge_channel/monitor.ex

def get_challenge_state(challenge) do

Agent.get(__MODULE__, fn state -> get_challenge_state(state, challenge) end)

end

defp get_challenge_state(state, challenge) do

case state[challenge] do

nil ->

state

|> Map.put(challenge, %{language: «ruby»})

|> Map.get(challenge)

data ->

state[challenge]

end

end

Мы передаем анонимную функцию в качестве второго аргумента Agent.get . Эта анонимная функция, в свою очередь, вызывает личную вспомогательную функцию get_challenge_state .

Эта функция использует оператор case для соответствия шаблону результату доступа к идентификатору ключа вызова в состоянии Агента. Если такой ключ существует, мы вернем значение этого ключа. Если такой ключ не существует, мы создадим исходное состояние для этой задачи в формате:

# where challenge ID is 1

%{1 => %{language: «ruby»}}

Функция #update_language

Когда пользователь выберет новый язык из выпадающего меню, мы отправим сообщение на канал «language:update» с полезной нагрузкой только что выбранного языка.

Наш канал ответит на это сообщение, обновив раздел состояния нашего Monitor Agent для этого идентификатора вызова. Затем он будет транслировать новое состояние вызова всем подписчикам.

# challenge_channel.ex

def handle_in(«language:update», %{«response» => response}, socket) do

Monitor.language_update(socket.assigns.challenge.id, response)

broadcast! socket, «language:updated», challenge_state(current_challenge(socket).id)

{:noreply, socket}

end

Наша Monitor.language_update/2 также будет функцией на стороне сервера.

# monitor.ex

def language_update(challenge, language) do

Agent.update(__MODULE__, fn state -> language_update(state, challenge, language) end)

end

 

defp language_update(state, challenge, language) do

put_in(state, [challenge, :language], language)

end

Здесь мы передаем анонимную функцию в качестве второго аргумента Agent.update . Эта анонимная функция вызывает частную вспомогательную функцию language_update , которая создает новую версию состояния Агента, при этом вложенная карта под идентификатором этой задачи обновляется, чтобы отразить выбранный язык.

Вывод

Наш модуль Monitor использует агента для поддержания довольно простого состояния — выбранного в данный момент языка для каждого канала вызова. Мы могли бы легко развить наш модуль Monitor для хранения более сложной информации в состоянии и для работы в этом состоянии с помощью ChallengeChannel .

По мере того, как мы продолжаем использовать каналы Phoenix и WebSockets для создания функций реального времени, мы отходим от парадигмы HTTP-запросов без состояния.

Вместо веб-приложения, которое опирается на ряд циклов запроса / ответа без учета состояния, мы отправляем сообщения через двунаправленные каналы и обмениваемся информацией между запросами и клиентами.

Elixir предлагает ряд инструментов, которые отлично подходят для обмена данными между процессами. Агент — один из таких инструментов, и это отличный способ обмена данными по каналам, позволяя нашим функциям в режиме реального времени поддерживать стойкость.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *