chevron-left chevron-right

Piotr Nalepa Frontend, architektura i AI

[JS][PHP] Jak zaimplementować powiadomienia typu push w aplikacji JavaScript?

Ta wersja
blog.piotrnalepa.pl/2015/06/11/jsphp-jak-zaimplementowac-powiadomienia-typu-push-w-aplikacji-javascript
Opublikowano
Autor
, Staff Software Engineer
Kategorie
JavaScript, PHP
Uwagi
Napisz do mnie · Napisz na X

Ten wpis ma 11 lat. Od tego czasu przeglądarki i narzędzia mogły się zmienić, więc zanim użyjesz kodu, sprawdź go z aktualną dokumentacją.

Spróbuj sobie wyobrazić korzystanie z Twittera, Facebooka czy też GitHuba bez otrzymywania powiadomień o tym co się właśnie wydarzyło. Brak informacji, że pojawiły się nowe wpisy na Twitterze; brak powiadomień, że ktoś do nas napisał na Facebooku czy chociażby brak powiadomień o tym, że ktoś utworzył pull requesta do naszego kodu na Githubie byłby bardzo uciążliwy. W niektórych przypadkach użyteczność takiej aplikacji czy strony znacząco uległaby zmniejszeniu.

Na szczęście, JavaScript API udostępniane przez HTML5 znacząco ułatwia nam implementację tego typu rozwiązań na stronach internetowych i w aplikacjach internetowych. W przypadku powiadomień typu push idealnym rozwiązaniem wydaje się być SSE - Server Sent Events.

Sekcja 1Czym jest SSE?

Jest to mechanizm jednostronnej komunikacji między serwerem a przeglądarką, która odbywa się za pomocą protokołu HTTP. Połączenie jest ustanawiane raz i jest utrzymywane przez serwer aż do momentu w którym twórca aplikacji zdecyduje się zamknąć połączenie. SSE ma wbudowaną funkcjonalność automatycznego wznawiania połączenia w momencie gdy z jakiegoś powodu połączenie zostało zerwane.

Z racji tego, że komunikacja odbywa się po protokole HTTP to wiadomości przesyłane z serwera zawsze będą w postaci tekstu. Czyli danych binarnych nie będziemy już w stanie przesłać, tak jak to ma miejsce w przypadku korzystania WebSockets.

Ponadto, za pomocą SSE jesteśmy w stanie samodzielnie zdefiniować jakie eventy/zdarzenia po stronie serwera będą udostępniane, oprócz standardowych eventów:

  • open,
  • message,
  • error.

Dodatkowo, możemy przesłać ID eventu, które może się okazać przy wznawianiu zerwanego połączenia. W takim przypadku, ID eventu ostatniego otrzymanego, przez przeglądarkę, eventu będzie przesłane w nagłówku żądania (requestu) HTTP: Last-Event-ID.

Sekcja 2Wsparcie SSE w przeglądarkach

Na chwilę obecną wsparcie przeglądarek dla tej funkcjonalności jest bardzo dobre wyłączając przeglądarkę Internet Explorer oraz system Android poniżej wersji 4.4. Jeśli zależy nam na wsparciu brakujących systemów to wystarczy do aplikacji dołączyć odpowiednią bibliotekę dodającą brakującą funkcjonalność, np. to rozwiązanie.

Sekcja 3Implementacja SSE po stronie przeglądarki

Aby zacząć korzystać z SSE po stronie przeglądarki wystarczy, że zastosujemy następujący zapis:

Przykład 1JavaScript
1
var sse = new EventSource('sse-server.php');

Oczywiście zamiast wpisywać adres sse-server.php możemy wpisać dowolny docleowy adres URL. Jeśli korzystamy ze źródeł danych znajdujących się pod inną domeną, to musimy zastosować odpowiednią politykę CORS na serwerze. W przypadku, gdy backend naszej aplikacji jest napisany w PHP, to wystarczy dodać następujące nagłówki:

Przykład 2PHP
1
2
3
4
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: X-Requested-With');

Dzięki nim będziemy w stanie się połączyć z serwerem z dowolnej przeglądarki, z aplikacji znajdującej się pod innym adresem internetowym niż serwer z danymi.

Wracając do naszego kodu JavaScript, to aby obsłużyć zdarzenia z serwera, musimy zacząć nasłuchiwać je w następujący sposób:

Przykład 3JavaScript
1
2
3
var messageCallback = function (event) { console.log('sse:message', event.data); };
 
sse.addEventListener('message', messageCallback, false);

I to jest wszystko co właściwie trzeba zrobić po stronie strony internetowej lub aplikacji internetowej aby móc korzystać z SSE. W przypadku, gdybyśmy chcieli nasłuchiwać innych eventów wysyłanych przez serwer (niestandardowych eventów), to wystarczy dodać następujący kawałek kodu:

Przykład 4JavaScript
1
2
3
var postUpdatedCallback = function (event) { console.log('post:updated', event);
 
sse.addEventListener('postupdated', postUpdatedCallback, false);

Sekcja 4Implementacja mechanizmu SSE po stronie serwera z wykorzystaniem PHP

Teraz pora na kawałek kodu, który na tym blogu od dawna nie pojawiał. Aplikacja na serwerze została napisana w prostym PHP, dzięki czemu można szybko sprawdzić działanie SSE w przeglądarce:

Przykład 5PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: X-Requested-With');
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
 
$counter = rand(1, 10);
$events = ['update', 'delete', 'create'];
$ids = [1, 12, 34, 65, 234, 18, 5];
 
while (1) {
    $counter--;
 
    if (!$counter) {
        echo 'event: ' . $events[array_rand($events)] . "\n";
        echo 'data: {"id": "' . $ids[array_rand($ids)] . '"}' . "\n\n";
 
        $counter = rand(1, 10);
    } else {
        echo 'event: ping' . "\n";
        echo 'data: {"time": "' . date(DATE_ISO8601) . '"}' . "\n\n";
    }
 
    ob_flush();
    flush();
    sleep(1);
}

Powyższy kod ma na celu losowe generowanie eventów z tablicy 3-ch elementów: update, create, delete z losowo dobranymi ID jakiejkolwiek treści (umówmy się, że będą to ID artykułów w bazie danych). Powyższy kod domyślnie wysyła event ping z danymi dotyczącymi czasu. W momencie gdy upłynie określony czas - zmienna $counter, to wysyła jeden z eventów o których wspomniałem wcześniej. Proste i łatwe do testowania.

Oczywiście, powyższe rozwiązanie ma na celu pokazanie jak SSE działa po stronie serwera. Kodu w postaci jak wyżej raczej nie będziesz pisać, aby obsłużyć swoją aplikację webową. Ze swojej strony mogę podpowiedzieć, że do nasłuchiwania zmiany stanu treści (artykułów, postów na blogu, stron) warto wykorzystać mechanizmy kolejek, np. RabbitMQ.

Sekcja 5Postać danych przesyłanych z serwera za pomocą SSE

Jak wspomniałem wcześniej, dane są przesyłane w postaci tekstu i mogą przyjąć następujący format:

Przykład 6PHP
id: 1234\n
event: update\n
retry: 18000\n
data: Jakieś dane\n
data: {"title":"Tytuł artykułu","author":"Piotr Nalepa"}\n\n

Wyjaśnię, co oznaczają kolejne przedrostki:

id
ID eventu, który może być wykorzystany przy wznawianiu połączenia.
event
Nazwa eventu który jest wysyłany przez serwer.
retry
Odpowiedzialny za ustawienie opóźnienia wznawiania połączenia. Domyślnie jego wartość wynosi 3 sekundy. Stosujemy zapis w milisekundach.
data
Dane jakie chcemy przesłać do przeglądarki. Należy pamiętać aby dane przesyłać w postaci tekstu, czyli obiekty, tablice, klasy. Przykład sformatowanego JSONa został przedstawiony powyżej.

Sekcja 6Podsumowanie

Mam nadzieję, że udało mi się w sposób wyczerpujący wyjaśnić czym jest SSE i jak z tego korzystać. Dzięki SSE można stworzyć wiele ciekawych funkcjonalności na stronach internetowych czy też wewnątrz aplikacji internetowych. Dzięki SSE można w łatwy sposób stworzyć aplikacje powiadamiające o jakichkolwiek zdarzeniach, np. o nowych wpisach na Twitterze czy też przesłać informacje o zmianie wyniku meczu piłkarskiego. Generalnie, to w jaki sposób można wykorzystać zaprezentowaną funkcjonalność zależy od pomysłu jaki ma dana osoba, np. Ty.

O budowaniu aplikacji javascriptowych z wykorzystaniem SSE, systemu CMS eZ Platform i tworzeniu natywnych aplikacji Windows wykorzystujących JavaScript, SSE i eZ Platform mówiłem na konferencji InfoShare 2015 w Gdańsku.

Udostępnij
Uwagi

O autorze

Staff Software Engineer

Piszę na tym blogu od 2009 roku, a strony i aplikacje webowe tworzę zawodowo od 14 lat. Obecnie w Bishop Fox prowadzę frontend platformy Cosmos, od samego początku projektu. Wcześniej pracowałem m.in. w OLX, KUDO, Auto1 i eZ Systems. Piszę tu o tym, czego nauczyłem się w praktyce: o JavaScripcie, TypeScripcie, Reakcie, CSS-ie i architekturze frontendu. Występowałem też jako prelegent na konferencjach w Polsce i Europie. Masz pytanie albo pomysł na wpis? Daj znać!