• Czas czytania ~6 min
  • 14.08.2023

Zawsze interesowałem się technologiami czasu rzeczywistego, zwłaszcza gdy mam do czynienia z dużym ruchem i złożonymi problemami. Spotkałem się kiedyś z sytuacją z zespołem operacyjnym sklepu e-commerce. Napotkali problem polegający na tym, że wielu operatorów obsługiwało jedno zamówienie. Ich początkowym rozwiązaniem było stworzenie systemu automatycznego przypisywania, który miał na celu równomierne rozdzielanie zleceń między operatorów. System ten napotykał jednak wyzwania, gdy operatorzy wyjeżdżali na urlopy lub gdy operator miał do wykonania kilka szczególnie trudnych zleceń. Więc pierwszym rozwiązaniem, o którym pomyślałem, jest coś podobnego do tego, jak dokumenty Google pokazują, kto jeszcze czyta/modyfikuje treść w czasie rzeczywistym.

Moja sugestia była taka, aby zacząć od poinformowania zespołu, jeśli ktoś inny już przegląda zamówienie, gdy je otwiera. W ten sposób mogą przypisywać sobie zlecenia bez nakładania się na siebie.

Przygotowałem demo, aby pokazać, jak można to zrobić, i wyjaśnię to krok po kroku. Ale zanim przejdziemy do szczegółów, nadajmy tej funkcji nazwę. Postanowiłem nazwać go "Na tej stronie".

Projekt

Strony administracyjne e-commerce są napisane w Blade i kilku małych javascriptach. Mogłem więc zaimplementować go z serwerem websocket i kilkoma javascriptami, aby wyświetlać aktywnych użytkowników na każdej stronie. Ale zamiast tego użyłem już istniejącego stosu Laravel & Redis + Livewire.

Pomysł

Zespół operacyjny nie jest zbyt duży i liczy około 50 osób. Możemy użyć Redis, aby śledzić, którą stronę przegląda każda osoba. Oznaczymy te informacje unikalnym identyfikatorem strony. Na stronie możemy następnie pokazać zdjęcia przeglądających ją użytkowników. Dodatkowo możemy ustawić regularny interwał aktualizacji i pokazywania nowych użytkowników, którzy zaczynają przeglądać stronę.

Możemy wcielić ten pomysł w życie za pomocą jednego komponentu Livewire:

"Na tej stronie" Livewire Component

  • Po zamontowaniu: Użyj nazwy trasy w połączeniu z jej oryginalnymi parametrami, aby utworzyć unikatowy klucz dla trasy.

  • Podczas renderowania: Wykonaj dwie czynności:

    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.
  • Automatyczne odświeżanie: Wykorzystaj funkcję sondowania Livewire, aby odświeżać komponent co ustawiony interwał.

Do tego zadania wybraliśmy Redis. Jest idealny, ponieważ może obsługiwać ogromną liczbę kluczy, które wygasają automatycznie i oferuje elastyczność odzyskiwania kluczy w podróży.

Zainstaluj livewire

W chwili pisania tego artykułu Livewire 3 jest nadal w wersji beta. Jednak kod, którym się podzielę, jest kompatybilny zarówno z wersją 2, jak i 3, ponieważ nie używamy żadnych ekskluzywnych funkcji z wersji 3. Na potrzeby tego samouczka używam wersji 3.

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

Na tej stronie komponent

Teraz zacznijmy tworzyć nasz nowy komponent

php artisan make:livewire OnThisPage

Spowoduje to utworzenie 2 plików

  • app/Livewire/
  • zasoby/widoki/livewire/

OnThisPage.php Przejdźmy teraz do głównej implementacji. Najpierw zaczniemy od uzyskania unikalnej nazwy trasy.

 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())
        );
    }
}

W naszym podejściu otrzymamy unikatową nazwę trasy w metodziemount. Jeśli spróbujemy to zrobić w metodzierender, nazwa trasy zmieni się na livewire.update. Dzieje się tak, ponieważ gdy komponent jest automatycznie odświeżany przy użyciu funkcji sondowania Livewire, Livewire wysyła żądanie do livewire.update trasy w celu pobrania szczegółów komponentu, unikając pełnego przeładowania strony.

  • Przejdźmy dalej, wprowadzając metodę logOnThisPage :
private function logOnThisPage(Request $request): void
{
    $userId = $request->user()->id;
    $page = $this->routeName;
    $key = "{$page}:{$userId}";

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

Ta metoda pobiera identyfikator użytkownika i unikatową nazwę trasy. Następnie łączy je, tworząc klucz dla Redis, który wygasa po 10 sekundach.

  • Następnie wprowadzimy getUserIdsOnThisPage metodę pomocnika, aby pobrać identyfikatory użytkowników aktualnie wyświetlających stronę:
/** @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();
}

Ta metoda zbiera wszystkie klucze, które zaczynają się od nazwy trasy jako prefiksu. Następnie wyodrębnia tylko identyfikator użytkownika z każdego klucza.

Wybieramy tę Redis::keys metodę Redis::scan , ponieważ maksymalna liczba użytkowników na stronie wynosi 50 (łączna liczba członków zespołu operacyjnego). Jest to liczba, którą można zarządzać w pamięci naszej aplikacji.

  • Oto metoda render , która jest ostateczną metodą w naszym komponencie:
 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,
    ]);
}

Najpierw logujemy bieżącego użytkownika za OnThisPage pomocą funkcji. Następnie pobieramy dane użytkownika z bazy danych w celu wyświetlenia.

on-this-page.blade.php Zajmijmy się teraz frontendem. W tej sekcji przejrzymy listę użytkowników, aby wyświetlić ich zdjęcia. Wykorzystamy również sondowanie, aby automatycznie aktualizować składnik co 20 sekund na podstawie zmiennej czasu trwania.

 <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>

Ten szablon po prostu pokazuje, którzy użytkownicy są aktualnie na stronie, wyświetlając ich zdjęcia profilowe.

Sposób użycia

Aby użyć tego komponentu, po prostu zintegruj go ze stronami, na których chcesz, aby użytkownicy widzieli, kto jeszcze przegląda tę samą stronę.

Dla demonstracji zintegrowałem go z podstawowym CMS-em. W obu widokach posts.list i posts.show dodałem ten komponent do nagłówka:

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

Pozwoli to członkom zespołu szybko zobaczyć, kto jeszcze ogląda tę samą treść.

Uwagi dotyczące pokazów

on-this-page-demo.gif

i implikacje

  • Obciążenie związane z odpytywaniem: Ustawiamy określony czas buforowania i regularnego sprawdzania komponentu Livewire. Mimo że sprawdzanie co kilka sekund pomaga zachować aktualność, może również wymagać dodatkowej pracy w systemie. Dlatego ważne jest, aby obserwować ustawiony czas i zobaczyć, jak wpływa on na twój system.

  • Zagadnienia dotyczące pamięci: System jest dostosowany do zespołu operacyjnego składającego się z około 50 członków, ale wraz ze wzrostem bazy użytkowników rośnie również zużycie pamięci. Jest to szczególnie zauważalne, jeśli czas wygaśnięcia kluczy Redis jest wydłużony. Ten wzrost użycia pamięci może mieć wpływ na wydajność i opłacalność aplikacji, jeśli nie jest odpowiednio monitorowany i zarządzany.

  • Bałagan w interfejsie użytkownika: Wyświetlanie pełnej listy użytkowników może sprawić, że interfejs będzie wyglądał na przeciążony, zwłaszcza gdy jest więcej niż pięciu użytkowników. Korzystne może być ustawienie limitu wyświetlania lub wprowadzenie wyskakującego okienka, które pokazuje pełną listę tylko wtedy, gdy jest to konieczne.

  • Trafienia w bazie danych: Sposób, w jaki nasz system jest skonfigurowany, często prosi bazę danych o informacje o użytkownikach ze względu na regularne kontrole. Gdy wielu użytkowników jest aktywnych, może to sprawić, że baza danych będzie pracować ciężej. Jeśli z czasem tego nie naprawimy, może to spowolnić działanie lub spowodować inne problemy. Rozwiązaniem może być przechowywanie danych użytkowników w usłudze Redis, dzięki czemu nie musimy tak często pytać bazy danych.

Co myślisz?

Co sądzisz o takim podejściu? Czy kiedykolwiek potrzebowałeś czegoś podobnego? Jeśli tak, to w jaki sposób go wdrożyliście? Chętnie poznamy Twoje przemyślenia lub alternatywne metody, które wypróbowałeś. Podziel się z nami swoimi doświadczeniami i opiniami na temat tego artykułu!

Miłego kodowania!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

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...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297