• Час читання ~2 хв
  • 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 секунд на основі змінної тривалості.

 <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 у поданнях andposts.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