Publikacje i subskrypcje

Pasek boczny 4.5

procent przetłumaczone

W tym rozdziale dowiesz się:

  • Jak działają publikacje i subskrypcje.
  • Do czego służy domyślny pakiet Autopublish.
  • O kilku przykładach wzorców publikacji.
  • Publikacje i subskrypcje należą do fundamentalnych konceptów Meteora, ale ciężko je zrozumieć jeżeli dopiero zaczynasz przygodę z Meteorem.

    Prowadzi to do wielu nieporozumień, tak jak plotka, że Meteor nie jest bezpieczny, lub że Meteor nie jest w stanie poradzić sobie z dużą liczbą danych.

    W dużym stopniu powodem, dla którego ludzie są zdezorientowani, jest “magia”, którą Meteor za nas wykonuje. Mimo to, że ta “magia” jest bardzo użyteczna, może ukryć wiele kwestii, które są ukryte za kulisami implementacji (jak to zwykle bywa, gdy wiele rzeczy jest robione za nas). Zobaczmy zatem pod maskę i spróbujmy zrozumieć, co naprawdę się dzieje.

    Po staremu

    Najpierw wróćmy do roku 2011, gdy nie było jeszcze Meteora. Przypuśćmy, że budujesz prostą aplikację Rails. W momencie, gdy użytkownik wejdzie na Twoją stronę, klient (czyli przeglądarka) wysyła żądania do aplikacji, która jest uruchomiona na serwerze.

    Pierwszym zadaniem aplikacji jest sprawdzenie, jakie dane udostępnić użytkownikowi. Może to być strona 12 rezultatów wyszukiwania, informacja o profilu Marysi, ostatnie 20 tweetów Bartka itd. Można o tym myśleć jako o sprzedawcy pracującemu w księgarni, który przegląda półki na twoje żądanie.

    Gdy dane zostały znalezione, kolejnym zadaniem aplikacji jest przetłumaczenie ich w format HTML zrozumiały dla człowieka (lub JSON w przypadku API).

    Używając metafory księgarni, można to skojarzyć z opakowaniem właśnie zakupionej książki i wrzucenie jej do torby. Jest to część nazywana “View” w znanym modelu MVC (Model-View-Controller).

    Ostatecznie, aplikacja wysyła wygenerowany kod HTML do przeglądarki. Praca aplikacji jest zakończona i kontrola zostaje przekazana z dala od jej wirtualnych rąk, oczekująć na kolejny dostęp.

    Jak to robi Meteor

    Zaznajomimy się z tym, co czyni Meteor tak specjalnym, w porównaniu do wcześniejszych rozwiązań. Jak już zdążyliśmy zauważyć, główną innowacją Meteora jest to, że aplikacje Rails są uruchamiane wyłącznie po stronie serwera, aplikacje Meteora zawierają również komponent po stronie klienta, który jest uruchamiany po stronie klienta (czyli w przeglądarce).

    Przesyłanie podzbioru bazy danych do klienta.
    Przesyłanie podzbioru bazy danych do klienta.

    Odpowiada to sprzedawcy, który nie tylko znajduje dla Ciebie książki, ale również idzie z Tobą do domu i czyta je dla Ciebie w nocy (przyznajmy, że przyprawia to trochę o gęsią skórkę).

    Taka architektura pozwala na osiągnięcie Meteorowi wielu ciekawych rzeczy, między innymi bardzo znane śą metody erwerowym (ang. Meteor calls) database everywhere. Mowiąc prostym językiem, Meteor kopiuje część bazy danych i przesyła ją klientowi.

    Ma to dwie duże konsekwencje: po pierwsze, zamiast wysyłać klientowi kod HTML, aplikacja Meteora wyśle prawdziwe, surowe dane i pozwoli klientowi nimi zarządzać i je wyświetlać (data on the wire). Po drugie, będziesz miał natychmiastowy dostęp do danych* bez konieczności na oczekiwanie na odpowiedź serwera (kompensacja lagów).

    Publikowanie kolekcji

    Baza danych aplikacji może zawierać dziesiątki tysięcy dokumentów, niektóre z nich mogą być prywatne lub utajnione. Z tego względu nie powinniśmy robić wiernej kopii całej bazy po stronie klienta, z uwagi na prywatność jak i skalowanie aplikacji.

    Potrzebujemy zatem znaleźć sposób na przekazanie przez Meteora jedynie podzbioru danych, które mogą zostać wysłane do klienta. Osiągniemy to za pomocą publikacji.

    Wróćmy do Microscope. Poniżej znajdziesz wszystkie posty znajdujące się w bazie danych aplikacji:

    Wszystkie posty znajdujące się w bazie danych.
    Wszystkie posty znajdujące się w bazie danych.

    Co prawda taka funkcjonalność nie istnieje w aplikacji Microscope, ale wyobraźmy sobie, że niektóre z postów zostały zaznaczone jako posiadające obraźliwe słownictwo. Mimo to, że chcemy je dalej przechowywać w bazie danych, nie powinny być udostępniane użytkownikom (tj. wysyłane do klienta).

    Naszym pierwszym zadaniem będzie przekazanie Meteorowi które dane chcemy wysłać klientowi. Bedą publikowane wyłącznie te posty, które nie zostały oznaczone flagą jako obraźliwe.

    Wyłączenie zaznaczonych postów.
    Wyłączenie zaznaczonych postów.

    Oto kod odpowiadający tej funkcjonalności po stronie serwera:

    // na serwerze
    Meteor.publish('posts', function() {
      return Posts.find({flagged: false}); 
    });
    

    Upewnia się to, że nie ma możliwości tego, że klient będzie miał dostęp do oflagowanych postów. Jest to dokładnie sposób, w jaki zabezpieczysz aplikację: przez upewnienie się, że publikujesz tylko te dane, do których bieżący użytkownik ma dostęp.

    DDP

    Zasadniczo możesz wyobrazić sobie system publikacji/subskrypcji jako rurę, która transferuje dane ze strony serwera (źródłowej kolekcji) do klienta (docelowej kolekcji).

    Protokół, przez który odbywa się komunikacja nazywa się DDP (ang. Distributed Data Protocol). Aby dowiedzieć się więcej o protokole DDP, możesz obejrzeć wystąpienie Matta DeBergalisa z The Real-time Conference (jednego z założycieli Meteora), lub ten screencast autorstwa Chrisa Mathera, który wprowadza w ten protokół bardziej szczegółowo.

    Subskrybowanie kolekcji

    Mimo tego, że chcemy udostępnić klientowi wszystkie nieoflagowane posty, nie możemy tak po prostu wysłać tysięcy postów jednocześnie. Potrzebujemy znaleźć sposób na przekazanie klientowi jaki podzbiór danych potrzebują w danej chwili i właśnie to zapewniają subskrypcje.

    Jakiekolwiek dane, które będziesz subskrybował, będą miały w całości kopiowane klientowi dzięki MiniMongo, implementacji bazy MongoDB po stronie klienta.

    Przykładowo, załóżmy, że przeglądamy stronę z profilem Boba Smitha i chcemy wyświetlić wyłącznie jego posty.

    Subskrybowanie postów Boba skopiuje je po stronie klienta.
    Subskrybowanie postów Boba skopiuje je po stronie klienta.

    Na początek zmodyfikujemy naszą publikację i dodamy do niej parametr:

    // na serwerze
    Meteor.publish('posts', function(author) {
      return Posts.find({flagged: false, author: author});
    });
    

    A następnie zdefiniujemy ten parametr, gdy subskrybujemy tą publikację w kodzie aplikacji po stronie klienta:

    // po stronie klienta
    Meteor.subscribe('posts', 'bob-smith');
    

    Właśnie w ten sposób zapewniasz skalowalność aplikacji po stronie klienta: zamiast subskrybować wszystkie dostępne dane, po prostu wybierasz te, które bieżąco potrzebujesz. W ten sposób unikasz zapełnienia pamięci przeglądarki niezależnie od wielkości bazy danych po stronie serwera.

    Szukanie w bazie danych

    Okazuje się, że post Boba jest przyporządkowany do kliku kategorii (np. “JavaScript”, ”Ruby” i ”Python”). Możliwe jest, że nadal chcemy wczytać wszystkie posty Boba do pamięci, ale chcemy wyświetlić tylko te z kategorii “JavaScript”. Pomocne w tym będzie tworzenie zapytań w bazie za pomocą find.

    Zaznaczanie podzbioru dokumentów po stronie klienta.
    Zaznaczanie podzbioru dokumentów po stronie klienta.

    Tak, jak po stronie serwera, użyjemy funkcję Posts.find() do zaznaczenia pozdbioru danych.

    // po stronie klienta
    Template.posts.helpers({
      posts: function(){
        return Posts.find(author: 'bob-smith', category: 'JavaScript');
      }
    });
    

    Teraz gdy mamy ogólne pojęcie na temat roli, jaką odgrywają publikacje i subskrypcje, spójrzmy na te pojęcia bardziej szczegółowo i poznajmy kilka powszechnych wzorców implementacji.

    Autopublikacja

    Jeżeli tworzysz projekt Meteora od podstaw, (tj. używając meteor create), projekt będzie automatycznie wyposażony w pakiet autopublish. Aby zacząć porozmawiajmy o tym, jak ten pakiet dokładnie działa.

    Celem autopublish jest ułatwienie implementacji aplikacji Meteora poprzez automatyczne kopiowanie wszystkich danych przechowywanych na serwerze do klienta, obsługując za Ciebie wszystkie publikacje i subskrypcje za jednym zamachem.

    Autopublikacja
    Autopublikacja

    Jak to działa? Przyjmijmy, że posiadasz aplikację 'posts' po stronie serwera. W takim przypadku autopublish wyśle wszystkie posty, które znajdzie w kolekcji posts po stronie serwera do kolekcji 'posts' po stronie klienta (zakładając, że istnieje).

    Jeżeli zatem używasz pakietu autopublish, nie musisz się troszczyć o publikacje. Dane są wszechobecne i wszystko jest proste. Oczywiście możesz napotkać oczywiste problemy związane z utrzymywaniem kompletnej kopii bazy danych po stronie klienta na każdej maszynie po stronie klienta.

    Z tego powodu użycie autopublish jest odpowiednie wyłącznie wtedy gdy zaczynasz implementację i nie uzwględniłeś publikacji.

    Publikowanie całych kolekcji

    Zaraz po tym, jak usuniesz autopublish, szybko zdasz sobie sprawę, że zniknęły dane po stronie klienta. Łatwym sposobem na ich odzyskanie jest zduplikowanie funkcjonalności pakietu autopublish przez publikowanie całej kolekcji. Przykładowo:

    Meteor.publish('allPosts', function(){
      return Posts.find();
    });
    
    Publishing a full collection
    Publishing a full collection

    W dalszym ciągu publikujemy całą kolekcję, ale przynajmniej mamy kontrolę nad tym, które kolekcje publikować, a które nie. W tym przypadku, publikujemy kolekcję Posts, ale nie publikujemy Comments.

    Częściowe publikowanie kolekcji

    Następnym poziomem kontroli publikacji danych jest publikacja części kolekcji. Na przykład publikacja postów należących do danego autora:

    Meteor.publish('somePosts', function(){
      return Posts.find({'author':'Tom'});
    });
    
    Częściowe publikowanie kolekcji
    Częściowe publikowanie kolekcji

    Za kulisami

    Jeżeli czytałeś dokumentację publikacji Meteora, mogłeś być przytłoczony fragmentem nakazującym używania funkcji added() i ready() do ustawiania atrybutów rekordów po stronie klienta i mogłeś być zaskoczony tym, że nie widziałeś nigdy użycia powyższych metod w żadnej widzianej aplikacji Meteora.

    Jest to spowodowane bardzo ważnym ułatwieniem, które dostarcza Meteor: metodą _publishCursor(). Nie widziałeś również tej metody? Może nie bezpośrednio, ale jeżeli zwracasz kursor (np. Posts.find({'author':'Tom'})) w funkcji dokonującej publikacji, to Meteor używa właśnie tej funkcji.

    Gdy Meteor rozpoznaje, że publikacjia somePosts zwraca kursor, woła metodę _publishCursor() aby – tak, zgadłeś – automatycznie publikować powyższy kursor.

    Pokrótce opiszmy sposób działania _publishCursor():

    • Sprawdza nazwę kolekcji po stronie serwera.
    • Wczytuje z bazy danych wszystkie pasujące do danego kursora dokumenty i wysyła je do kolekcji po stronie klienta która ma tą samą nazwę, co na serwerze. (Wewnętrznie używa metodę .added() aby to osiągnąć).
    • Za każdym razem, gdy dodano, zmieniono lub usunięto dokument, wysyła zmiany do kolekcji po stronie klienta. (Wewnętrznie używa metodę .observe() na kursorze i .added(), .changed() oraz removed()).

    Zatem w powyższym przykładzie byliśmy w stanie upewnić się, że użytkownik otrzymał wyłącznie te posty, które go interesowały (te napisane przez Toma), dostępne w cache'u po stronie klienta.

    Publikowanie wyłącznie niektórych pól kolekcji

    Poznaliśmy jak publikować niektóre posty, ale możemy w większym stopniu ograniczyć liczbę przesyłanych danych! Zobaczmy jak publikować wyłącznie wybrane pola kolekcji.

    Tak, jak poprzednio, użyjemy metody find() , aby zwróciła kursor, ale tym razem wyłączymy z zapytania niektóre pola:

    Meteor.publish('allPosts', function(){
      return Posts.find({}, {fields: {
        date: false
      }});
    });
    
    Publikowanie niektórych pól
    Publikowanie niektórych pól

    Oczywiście możemy połączyć oba rozwiązania. Na przykład, jeżeli chcemy zwrócić wszystkie posty napisane przez Toma, ale nie publikować daty napisania postów, napisalibyśmy:

    Meteor.publish('allPosts', function(){
      return Posts.find({'author':'Tom'}, {fields: {
        date: false
      }});
    });
    

    Podsumowanie

    Przeszliśmy drogę od opublikowania wszystkich pól wszystkich dokumentów danej kolekcji (za pomocą autopublish) do publikowania wyłącznie niektórych pól niektórych dokumentów wybranych kolekcji.

    To podsumowuje podstawę pracy z publikacjami Meteora i techniki poznane powyżej są w stanie pokryć znaczną większość powszechnych zastosowań.

    Czasami jednak będziesz musiał wyjść poza te ramy przez łączenie, linkowanie lub mergowanie publikacji. Opiszemy to w kolejnym rozdziale!