Subskrypcje GraphQL: jak działają na poziomie transmisji | We.Inc
Czym są subskrypcje GraphQL, reguła jednego pola głównego, omówienie krok po kroku wiadomości graphql-ws, opcja SSE oraz sytuacje, w których polling jest lepszym wyborem.
Subskrypcja GraphQL to długotrwała operacja: klient wysyła dokument subskrypcji raz, a serwer wypycha nowy wynik za każdym razem, gdy zajdzie pasujące zdarzenie. GraphQL nie narzuca transportu. W praktyce działa przez WebSockety z protokołem graphql-ws lub przez Server-Sent Events. Każda operacja subskrypcji musi wybierać dokładnie jedno pole główne.
Subskrypcje, polling i ponowne pobieranie
Podejście
Jak przychodzą aktualizacje
Dobre zastosowanie
Subskrypcja
Serwer wypycha wynik dla każdego zdarzenia
Małe, częste zmiany, które użytkownicy chcą widzieć od razu
Polling
Klient powtarza zapytanie według timera
Dane, które zmieniają się od czasu do czasu
Ponowne pobranie po akcji
Klient ponawia zapytanie po kroku użytkownika
Ekrany, które zmieniają się tylko wtedy, gdy użytkownik działa
Powiadomienie push
Wiadomość poza aplikacją
Użytkownicy mobilni z zamkniętą aplikacją
Poniższy krokowy podgląd odtwarza jedną subskrypcję w protokole graphql-ws, wiadomość po wiadomości. Każdy krok pokazuje ramkę JSON przechodzącą przez gniazdo i stronę, która ją wysyła, od pierwszego handshake do końcowego complete.
Naciśnij Dalej, aby przechodzić przez rozmowę po jednej ramce.
Przeczytaj, kto wysyła każdą ramkę i co ona oznacza.
Obserwuj id operacji: każda ramka jednej subskrypcji ma to samo id.
Użyj Wstecz lub Od nowa, aby jeszcze raz obejrzeć krok.
Zacznij od pollingu i przejdź na subskrypcję dopiero wtedy, gdy kilka sekund opóźnienia naprawdę psuje doświadczenie.
W każdym wyniku wysyłaj małe zdarzenie, na przykład nową wiadomość, zamiast całej listy, i pozwól klientowi je scalić.
Zaplanuj ponowne połączenia: po zerwaniu gniazda klient musi ponownie zasubskrybować i pobrać wszystko, co przegapił.
Sprawdzaj uprawnienia przy otwarciu połączenia i ponownie w każdym resolverze, bo prawa użytkownika mogą się zmienić podczas długiej sesji.
Najczęściej zadawane pytania
Dlaczego tylko jedno pole główne na subskrypcję?
Każda subskrypcja odpowiada jednemu strumieniowi zdarzeń. graphql.org pokazuje, że umieszczenie dwóch pól głównych w jednej operacji subskrypcji jest niepoprawne; zamiast tego napisz dwie nazwane subskrypcje.
Jakiego podprotokołu WebSocket używa graphql-ws?
Podczas handshake negocjuje graphql-transport-ws, z typami ramek takimi jak connection_init, subscribe, next i complete. Inne biblioteki GraphQL dla WebSocketów mogą używać innego protokołu, więc zanim zaczniesz debugować cokolwiek innego, sprawdź, czy klient i serwer są zgodne.
Czy subskrypcje mogą działać bez WebSocketów?
Tak. Protokół graphql-sse wysyła każdy wynik jako Server-Sent Event o nazwie next i kończy się zdarzeniem complete. Działa przez zwykłe HTTP, co pomaga za proxy blokującymi upgrade do WebSocket.
Czy subskrypcje skalują się jak zapytania?
Nie. graphql.org zaznacza, że każdy subskrybujący klient jest przywiązany do instancji serwera i wymaga przechowywania stanu przez cały czas trwania subskrypcji, więc potrzebujesz też systemu pub/sub, który rozprowadzi zdarzenia między serwerami.
Co się dzieje, gdy subskrybujesz
Klient wysyła dokument zaczynający się od słowa kluczowego subscription, wybiera jedno pole główne i wymienia pola, które chce dostać w każdym zdarzeniu. Serwer waliduje go raz i utrzymuje otwartym.
Gdy coś opublikuje pasujące zdarzenie, na przykład nową recenzję, serwer wykonuje zestaw wyboru na tym zdarzeniu i wysyła wynik. Każdy wynik ma zwykły kształt data i errors, więc kod klienta czyta go tak samo jak wynik zapytania.
Protokół graphql-ws w skrócie
Po upgrade do WebSocket klient wysyła connection_init, często z tokenem uwierzytelniającym w payloadzie, i czeka na connection_ack. Dopiero wtedy może wysyłać ramki subscribe. Każde subscribe ma unikalne id, a każda ramka next, error lub complete dla tej operacji je powtarza, więc wiele subskrypcji współdzieli jedno gniazdo.
Każda strona może wysłać ping, a druga odpowiada pong. Serwer zamyka gniazdo kodem 4408, jeśli connection_init nie przyjdzie na czas, i kodem 4409, gdy klient ponownie użyje id operacji, która wciąż trwa.
Kiedy polling jest lepszym wyborem
Subskrypcje wymagają trwałych połączeń, warstwy pub/sub i starannego kodu klienta do ponownego subskrybowania i obsługi wyścigów. Dla dashboardu odświeżanego co minutę zapytanie według timera jest prostsze i łagodniej znosi awarie.
graphql.org zaleca subskrypcje dla danych, które zmieniają się często i małymi krokami, a polling, powiadomienia push lub ponowne pobieranie dla wolniej zmieniających się danych lub mobilnych klientów z ograniczonym transferem.
Schemat i kod klienta
Deklaracja pola subskrypcji
Subscription to typ główny, tak jak Query i Mutation. Każde pole to jeden strumień, który klient może otworzyć.
Subskrybowanie klientem graphql-ws
createClient otwiera gniazdo leniwie, a iterate zwraca asynchroniczny iterator, który zwraca jeden wynik na zdarzenie.
Reguła jednego pola głównego
Rozdziel dwa strumienie na dwie operacje
Pojedyncza subskrypcja z reviewCreated i innym polem głównym nie przechodzi walidacji. Dwie nazwane operacje działają.
Klient wysyła connection_init
Wysyłane zaraz po otwarciu gniazda. Payload ma dowolną postać; serwery często odczytują z niego token.
Serwer wysyła connection_ack
Serwer zaakceptował połączenie. Wcześniej ramka subscribe zamknęłaby gniazdo z kodem 4401 Unauthorized.
Klient wysyła subscribe
Id wybiera klient i musi być unikalne wśród jego trwających operacji. Payload to zwykłe zapytanie i zmienne.
Serwer wysyła next
Opublikowano recenzję, więc serwer wykonał zestaw wyboru i wysłał wynik pod tym samym id.
Serwer ponownie wysyła next
Kolejne zdarzenie, kolejna ramka. Powtarza się to tak długo, jak subskrypcja pozostaje otwarta.
Klient wysyła complete
Użytkownik opuścił stronę, więc klient zatrzymuje operację. Gniazdo może pozostać otwarte dla innych subskrypcji.