forked from DiscoverMeteor/DiscoverMeteor_Pl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path04s-publications-and-subscriptions.md.erb
217 lines (134 loc) · 13.3 KB
/
04s-publications-and-subscriptions.md.erb
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
---
title: Publikacje i subskrypcje
slug: publications-and-subscriptions
date: 0004/01/02
number: 4.5
sidebar: true
contents: Jak działają publikacje i subskrypcje.|Do czego służy domyślny pakiet Autopublish.|O kilku przykładach wzorców publikacji.
paragraphs: 52
---
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).
<%= diagram "client-server", "Przesyłanie podzbioru bazy danych do klienta.", "pull-right" %>
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](http://docs.meteor.com/#sevenprinciples). 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](http://docs.meteor.com/#sevenprinciples)). Po drugie, będziesz miał *natychmiastowy dostęp do danych** bez konieczności na oczekiwanie na odpowiedź serwera ([kompensacja lagów](http://docs.meteor.com/#sevenprinciples)).
### 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:
<%= diagram "collections-1", "Wszystkie posty znajdujące się w bazie danych.", "pull-center" %>
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.
<%= diagram "collections-2", "Wyłączenie zaznaczonych postów.", "pull-center" %>
Oto kod odpowiadający tej funkcjonalności po stronie serwera:
~~~js
// 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.
<% note do %>
### 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`](http://2012.realtimeconf.com/video/matt-debergalis) (jednego z założycieli Meteora), lub [ten screencast](http://www.eventedmind.com/posts/meteor-subscriptions-and-ddp) autorstwa Chrisa Mathera, który wprowadza w ten protokół bardziej szczegółowo.
<% end %>
### 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.
<%= diagram "collections-3", "Subskrybowanie postów Boba skopiuje je po stronie klienta.", "pull-center" %>
Na początek zmodyfikujemy naszą publikację i dodamy do niej parametr:
~~~js
// 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:
~~~js
// 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`.
<%= diagram "collections-4", "Zaznaczanie podzbioru dokumentów po stronie klienta.", "pull-center" %>
Tak, jak po stronie serwera, użyjemy funkcję `Posts.find()` do zaznaczenia pozdbioru danych.
~~~js
// 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.
<%= diagram "autopublish", "Autopublikacja", "pull-center"%>
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:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find();
});
~~~
<%= diagram "fullcollection", "Publishing a full collection", "pull-center" %>
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:
~~~js
Meteor.publish('somePosts', function(){
return Posts.find({'author':'Tom'});
});
~~~
<%= diagram "partialcollection", "Częściowe publikowanie kolekcji", "pull-center" %>
<% note do %>
### Za kulisami
Jeżeli czytałeś [dokumentację publikacji Meteora](http://docs.meteor.com/#publishandsubscribe), 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](/chapter/meteor-vocabulary/) (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.
<% end %>
### 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:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({}, {fields: {
date: false
}});
});
~~~
<%= diagram "partialproperties", "Publikowanie niektórych pól", "pull-center" %>
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:
~~~js
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!