• Время чтения ~6 мин
  • 14.08.2023

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

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

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

.Проект

Страницы администратора электронной коммерции написаны на Blade и немного немного javascript. Таким образом, я мог бы реализовать его с помощью веб-сокет-сервера и нескольких javascript для отображения активных пользователей на каждой странице. Но вместо этого я использовал уже существующий стек Laravel & Redis + Livewire.

Идея

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

Мы можем воплотить эту идею в жизнь с помощью одного компонента Livewire:

"На этой странице" Компонент Livewire

  • При монтировании: используйте имя маршрута в сочетании с его исходными параметрами для создания уникального ключа для маршрута.

  • При рендеринге: Сделайте две вещи:

    1. Save the combination of the route identifier and user ID, setting it to automatically expire after a certain time.
    2. Retrieve all the route identifier keys, extract user IDs from them, and then gather details like names and profile photos of these users.
  • Автообновление: Используйте функцию опроса Livewire, чтобы обновлять компонент каждые заданные интервалы.

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

Установка livewire

На момент написания этой статьи Livewire 3 все еще находится в бета-версии. Тем не менее, код, которым я поделюсь, совместим как с версиями 2, так и с 3, поскольку мы не используем какие-либо эксклюзивные функции из v3. В этом руководстве я использую v3.

1composer require livewire/livewire "^3.0@beta"

На этой странице компонента

Теперь давайте приступим к созданию нашего нового компонента

php artisan make:livewire OnThisPage

Это создаст 2 файла

  • приложение/Livewire/
  • Ресурсы/Просмотры/LiveWire/

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

 class OnThisPage extends Component
 {
     public $routeName;
     
     public $durationInSeconds = 10;
 
     public function mount(Request $request)
     {
         $this->routeName = sprintf(
            '%s:%s', 
            $request->route()->getName(),
            implode(':', $request->route()->originalParameters())
        );
    }
}

В нашем подходе мы получим уникальное имя маршрута в методеmount. Если мы попробуем сделать это в методеrender, то имя маршрута изменится на livewire.update. Это происходит потому, что когда компонент автоматически обновляется с помощью функции опроса Livewire, Livewire отправляет запрос на livewire.update маршрут для получения сведений о компоненте, избегая полной перезагрузки страницы.

  • Приступим к введению метода logOnThisPage :
private function logOnThisPage(Request $request): void
{
    $userId = $request->user()->id;
    $page = $this->routeName;
    $key = "{$page}:{$userId}";

    Redis::setex($key, $this->durationInSeconds, $userId);
}

Этот метод извлекает идентификатор пользователя и уникальное имя маршрута. Затем он объединяет их, чтобы сформировать ключ для Redis, срок действия которого истекает через 10 секунд.

  • Далее мы представим getUserIdsOnThisPage вспомогательный метод для получения идентификаторов пользователей, просматривающих страницу в данный момент:
/** @return array<int> */
private function getUserIdsOnThisPage(Request $request): array
{
    $page = $this->routeName;

    return collect(Redis::keys("{$page}:*"))
        ->map(fn($key) => str_replace("{$page}:", '', $key))
        ->toArray();
}

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

Мы выбираем этот Redis::keys метод Redis::scan , потому что максимальное количество пользователей на странице составляет 50 (общее количество членов рабочей команды). Это управляемое число для памяти нашего приложения.

  • Вот render метод, который является последним методом в нашем компоненте:
 public function render(Request $request)
 {
     $this->logOnThisPage($request);
 
     $users = User::query()
         ->select('id', 'name', 'username', 'avatar_url')
         ->whereIn('id', $this->getUserIdsOnThisPage($request))
         ->get();
 
    return view('livewire.on-this-page', [
        'users' => $users,
        'duration' => $this->durationInSeconds,
    ]);
}

Во-первых, мы регистрируем текущего пользователя с помощью OnThisPage функции. Затем мы извлекаем сведения о пользователе из базы данных для отображения.

on-this-page.blade.php Теперь давайте займемся фронтендом. В этом разделе мы пройдемся по списку пользователей, чтобы отобразить их фотографии. Мы также будем использовать опрос для автоматического обновления компонента каждые 20 секунд на основе переменной duration.

 <div wire:poll.{{ $duration }}s>
     <div class="flex overflow-hidden items-center">
         <div class="text-gray-400">
             On this page
         </div>
         <div class="ml-2 flex -space-x-2">
             @foreach($users as $user)
                 <img class="inline-block h-8 w-8 rounded-full text-white shadow-solid border border-black"
                      src="{{ asset($user->avatar_url) }}" alt="{{ $user->name }}"/>
            @endforeach
        </div>
    </div>
</div>

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

Как использовать

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

Для демонстрации я интегрировал его в базовую CMS. В обоих posts.list posts.show представлениях и я добавил этот компонент в заголовок:

<div>
    <livewire:on-this-page />
</div>

Это позволит членам команды быстро видеть, кто еще просматривает тот же контент.

Рекомендации и последствия демонстрации

on-this-page-demo.gif

  • Накладные расходы на опрос: Мы устанавливаем конкретное время для кэширования и регулярной проверки компонента Livewire. Несмотря на то, что проверка каждые несколько секунд помогает поддерживать все в актуальном состоянии, она также может создать дополнительную нагрузку на систему. Поэтому очень важно следить за установленным временем и видеть, как оно влияет на вашу систему.

  • Соображения по памяти: Система рассчитана на операционную группу, состоящую примерно из 50 человек, но по мере роста пользовательской базы растет и потребление памяти. Это особенно заметно, если срок действия ключей Redis увеличивается. Этот рост использования памяти может повлиять на производительность и экономическую эффективность приложения, если его не отслеживать и не управлять должным образом.

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

  • Попадания в базу данных: Наша система устроена таким образом, что она часто запрашивает у базы данных информацию о пользователях из-за регулярных проверок. Когда много активных пользователей, это может привести к тому, что база данных будет работать более интенсивно. Если мы не исправим это со временем, это может замедлить работу или вызвать другие проблемы. Решением может быть хранение пользовательских данных в Redis, чтобы нам не приходилось так часто обращаться к базе данных.

Как ты думаешь?

Как вы относитесь к такому подходу? Вам когда-нибудь нужно было что-то подобное? Если да, то как вы его реализовали? Мы будем рады услышать ваше мнение или узнать о любых альтернативных методах, которые вы пробовали. Поделитесь с нами своим опытом и отзывами об этой статье!

Удачного кодирования!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297