-
Notifications
You must be signed in to change notification settings - Fork 12
/
04s-publications-and-subscriptions.md.erb
217 lines (134 loc) · 13.6 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: Publications and Subscriptions
slug: publications-and-subscriptions
date: 0004/01/02
number: 4.5
sidebar: true
contents: Lernen, wie Veröffentlichungen und Abonnements funktionieren.|Lernen, was das Package Autopublish bewirkt.|Einige weitere Beispiele von Veröffentlichungsmustern kennenlernen.
paragraphs: 52
---
Veröffentlichungen und Abonnements (publications/subscriptions) sind eines der grundlegenden und wichtigen Konzepte in Meteor. Wenn du dich erst kurze Zeit mit Meteor beschäftigst, dann kann es sein, dass das Konzept nicht leicht zu verstehen ist.
Das hat zu einer Reihe von Missverständnissen geführt, etwa dass Meteor unsicher sei oder dass Meteor-Apps nicht in der Lage seien, mit großen Datenmengen umzugehen.
Ein Großteil der Verwirrung geht auf das Konto der „Magie“, die Meteor für uns ausführt. Wenn diese auch letztendlich sehr nützlich ist, kann sie verschleiern, was wirklich hinter den Kulissen vor sich geht. (Was in der Natur von Magie liegt.) Lass uns also die magischen Schichten abtragen um zu verstehen, was da vor sich geht.
### In grauer Vorzeit
Lasst uns zunächst auf die gute alte Zeit von 2011 zurückblicken, als es Meteor noch nicht gab. Nehmen wir an, du entwickelst eine einfache Rails-App. Wenn ein User auf deine Seite kommt, dann sendet der Client (z. B. der Browser) eine Anfrage (request) an deine App, die sich auf dem Server befindet.
Das erste, was die App unternimmt, ist, herauszufinden, was der User sehen möchte. Das könnte Seite 12 von einer Menge von Suchergebnissen sein, die Profildaten von Mary, Bobs 20 neueste Tweets und so weiter. Stell dir einen Angestellten in einem Buchladen vor, der durch die Regalreihen geht, um das Buch, nach dem du gefragt hast, herauszusuchen.
Sobald die richtigen Daten ermittelt sind, ist die zweite Aufgabe der App, diese in hübsches, lesbares HTML umzuformen (oder JSON, wenn ein API im Spiel ist).
Im Bild des Buchladens würde das gewünschte Buch eingepackt und in eine Tüte gesteckt. Das entspricht dem „View“-Teil des bekannten Entwurfsmusters Model-View-Controller.
Schließlich nimmt die App den HTML-Code und schickt ihn an den Browser. Die App hat damit ihre Arbeit getan und nun, wo sie ihre virtuellen Hände wieder frei hat, kann sie sich mit einem Bier zurücklehnen während sie auf die nächste Anfrage wartet.
### Wie Meteor es macht
Schauen wir uns nun an, was Meteor im Vergleich dazu so besonders macht. Wie wir gesehen haben, besitzt Meteor im Gegensatz zur Rails-App, die nur **auf dem Server** existiert, auch einen Anteil **auf dem Client** (dem Browser). Das ist die grundlegende Innovation, die Meteor mitbringt.
<%= diagram "client-server", "Eine Untermenge der Datenbank zum Client senden.", "pull-right" %>
Das ist wie der Angestellte, der dir nicht nur das richtige Buch heraussucht, sondern dich auch nach Hause begleitet um es dir abends vorzulesen (zugegeben, das klingt etwas gruselig).
Diese Architektur lässt Meteor viele erstaunliche Dinge tun, hauptsächlich das, was Meteor [database everywhere](http://docs.meteor.com/#sevenprinciples) nennt. Vereinfacht gesagt nimmt Meteor einen Teil der Datenbank und *kopiert ihn auf den Client*.
Daraus folgt zweierlei: erstens überträgt eine Meteor-App anstatt HTML-Code die **eigentlichen, unformatierten Daten** zum Client und überlässt es diesem, wie damit umzugehen ist ([data on the wire](http://docs.meteor.com/#sevenprinciples)). Zweitens hast du **unmittelbaren Zugriff** auf diese Daten, ohne erst auf eine Server-Antwort zu warten ([latency compensation](http://docs.meteor.com/#sevenprinciples)).
### Veröffentlichen
Die Datenbank einer App kann zehntausende von Dokumenten enthalten, von denen einige auch privater oder vertraulicher Natur sein mögen. Deshalb ist es offensichtlich, dass wir nicht einfach den gesamten Datenbankinhalt auf den Client spiegeln – aus Gründen der Sicherheit und der Stabilität.
Wir brauchen daher eine Methode, wie wir Meteor mitteilen, welche **Untermenge** des Datenbestandes zum Client zu schicken ist. Das erreichen wir duch eine **Veröffentlichung**.
Gehen wir noch einmal zu Microscope zurück. Hier sind alle Beiträge unserer App in der Datenbank gespeichert:
<%= diagram "collections-1", "Alle Beiträge in der Datenbank.", "pull-center" %>
Auch wenn diese Möglichkeit, wie wir zugeben, in Microscope nicht existiert, stellen wir uns einmal vor, dass einige unserer Beiträge wegen darin verwendeter Schimpfworte markiert worden sind. Auch wenn wir sie in der Datenbank behalten wollen, sollten die für die User nicht verfügbar sein, also nicht zum Client gesendet werden.
Unsere erste Aufgabe wird sein, Meteor klarzumachen, was wir zum Client senden wollen. Wir sagen Meteor, nur nicht markierte Beiträge zu **veröffentlichen**.
<%= diagram "collections-2", "Markierte Beiträge ausschließen.", "pull-center" %>
Hier ist der entsprechende Code, der auf dem Server sitzen würde:
~~~js
// on the server
Meteor.publish('posts', function() {
return Posts.find({flagged: false});
});
~~~
Damit ist sichergestellt, dass ein Client **auf keinen Fall** auf einen markierten Beitrag zugreifen kann. Genau so wird eine Meteor-App sicher gemacht: Stelle sicher, dass nur die Daten veröffentlicht werden, der der Client auch sehen soll.
<% note do %>
### DDP
Im Grunde ist das System, das hinter dem Veröffentlichen und Abonnieren (publication/subscription) steht, wie ein Trichter, durch den von einer serverseitigen Collection zu einer clientseitigen Collection transportiert werden.
Das Protokoll, mit dem dieser Trichter arbeitet heißt **DDP**. Das steht für Distributed Data Protocol, auf deutsch etwa Protokoll für verteilte Daten. Um mehr darüber zu erfahren, schaue dir diesen [Vortrag von Matt DeBergalis auf der Real-time-Konferenz](http://2012.realtimeconf.com/video/matt-debergalis) an oder diesen [Screencast von Chris Mather](http://www.eventedmind.com/posts/meteor-subscriptions-and-ddp), die das Konzept von DDP näher beleuchten.
<% end %>
### Abonnieren
Selbst wenn wir jeden nicht markierten Beitrag auf dem Client verfügbar machen wollten, können wir nicht einfach tausende Beiträge auf einmal versenden. Wir brauchen eine Methode, mittels derer der Client bestimmen kann welche Teilmenge der Daten in einem bestimmten Augenblick benötigt wird. Genau dafür gibt es das **Abonnieren**.
Jegliche abonnierte (*subscribed*) Daten werden auf den Client **gespiegelt**. Das ist Minimongo zu verdanken, Meteors Clientseitige Implementierung der MongoDB.
Wir schauen uns beispielsweise gerade die Profilseite von Bob Smith an und da wollen wir nur *seine* Beiträge sehen.
<%= diagram "collections-3", "Abonnieren von Bobs Beiträgen spiegelt diese auf den Client.", "pull-center" %>
Zunächst würden wir unsere Veröffentlichung (*publication*) um einen Parameter erweitern:
~~~js
// on the server
Meteor.publish('posts', function(author) {
return Posts.find({flagged: false, author: author});
});
~~~
Und anschließend würden wir diesen Parameter benutzen, wenn wir die Veröffentlichung im clientseitigen Code abonnieren:
~~~js
// on the client
Meteor.subscribe('posts', 'bob-smith');
~~~
Auf diese Weise wird eine Meteor-App auf der Clientseite skalierbar: anstatt *alle* verfügbaren Daten zu abonnieren, wählen wir die Teile aus, die wir gerade brauchen. So wird vermieden, dass der Speicher des Browsers überlastet wird, egal wie groß die serverseitige Datenbank ist.
### Finden
Jetzt ist es so, dass Bobs Beiträge über mehrere Kategoien verteilt sind (z. B.: „JavaScript“, „Ruby“ und „Python“). Angenommen, wir wollen jetzt zwar alle Beiträge von Bob im Speicher haben aber nur die der Kategorie „JavaScript“ anzeigen. Hier kommt das „Finden“ ins Spiel.
<%= diagram "collections-4", "Eine Teilmenge von Dokumenten vom Client aus selektieren.", "pull-center" %>
Genau wie auf dem Server benutzen wir die Funktion `Posts.find()`, um eine Teilmenge unserer Daten zu selektieren.
~~~js
// on the client
Template.posts.helpers({
posts: function(){
return Posts.find(author: 'bob-smith', category: 'JavaScript');
}
});
~~~
Jetzt haben wir einen Begriff davon, welche Rolle Veröffentlichungen und Abonnements haben. Zeit, tiefer zu graben und einige häufig benutzte Implementierungsmuster anzusehen.
### Autopublish
Wenn du ein neues Meteor-Projekt anslegst (mittels `meteor create`), dann ist das Package `autopublish` automatisch mit installiert. Wozu ist es gut? Lass es uns einmal ansehen.
Die Zielsetzung von `autopublish` ist, den Start in die Programmierung einer Meteor-App sehr einfach zu gestalten. Dazu werden einfach _alle Daten_ vom Server auf den Client gespiegelt. `autopublish` übernimmt also das Veröffentlichen und Abonnieren für dich.
<%= diagram "autopublish", "Autopublish", "pull-center"%>
Wie geht das? Angenommen, du hast eine Collection namens `'posts'` auf dem Server. Dann wird `autopublish` automatisch jeden Eintrag den es in der Mongo-Collection `'posts'` findet, an eine Collection `'posts'` auf dem Client senden, sofern diese existiert.
Wenn du also `autopublish` benutzt, dann brauchst du dir über das Veröffentlichen keine Gedanken machen. Daten sind überall vorhanden und die Dinge liegen sehr einfach. Natürlich gibt es die offensichtlichen Probleme mit einer vollständigen Kopie der Datenbank auf dem Rechner jedes einzelnen Benutzers.
Deshalb ist `autopublish` nur während des Anfangs der Entwicklung sinnvoll, solange du dir noch keine Gedanken über ein Veröffentlichungskonzept gemacht hast.
### Vollständige Collections veröffentlichen
Sobald du das Package `autopublish` aus deinem Projekt entfernt hast, stellst du schnell fest, dass alle Daten vom Client verschwunden sind. Eine einfache Methode, sie zurückzubekommen ist, den Mechanismus von `autopublish` zu kopieren und einfach die ganze Collection zu veröffentlichen. Zum Beispiel:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find();
});
~~~
<%= diagram "fullcollection", "Eine ganze Collection veröffentlichen", "pull-center" %>
Wir veröffentlichen zwar immer noch vollständige Collections aber immerhin haben wir die Kontrolle darüber, welche wir veröffentlichen und welche nicht. In diesem Fall veröffentlichen wir die Collection `Posts` aber nicht `Comments`.
### Teile einer Collections veröffentlichen
Die nächste Ebene von Kontrolle ist es, nur _Teile_ einer Collection zu veröffentlichen. Beispielsweise die Beiträge eines bestimmten Autors:
~~~js
Meteor.publish('somePosts', function(){
return Posts.find({'author':'Tom'});
});
~~~
<%= diagram "partialcollection", "Teile einer Collection veröffentlichen", "pull-center" %>
<% note do %>
### Hinter den Kulissen
Wenn du die [Meteor-Dokumentation zum Veröffentlichen](http://docs.meteor.com/#publishandsubscribe) gelesen hast, dann warst du vermutlich überwältigt von dem Gerede über die verwendung von `added()` und `ready()`, um Attribute von Datensätzen auf dem Client zu setzen und davon, das mit den Meteor-Apps die du bislang gesehen hast in Einklang zu bringen, bei denen du diese Methoden nicht angewandt findest.
Das liegt daran, dass Meteor eine wichtige Komfort-Funktion bereitstellt: die Methode `_publishCursor()`. Die hast du auch noch nie gesehen? Vielleicht nicht direkt, aber wenn du in einer Veröffentlichungsfunktion einen [Cursor](/chapter/meteor-vocabulary/) zurückgibst (z.B. `Posts.find({'author':'Tom'})`), dann ist das genau das, was Meteor intern verwendet.
Wenn Meteor erkennt, dass die Veröffentlichung `somePosts` einen Cursor zurückgegeben hat, dann ruft es die Methode `_publishCursor()`, um -- richtig geraten -- diesen Cursor automatisch zu veröffentlichen.
Hier ist, was `_publishCursor()` tut:
- Prüfen des Namens der serverseitigen Collection
- Holen aller passenden Dokumente aus dem Cursor und senden dieser an die *gleichnamige* clientseitige Collection. (Mittels `.added()`)
- Wann immer ein Dokument dazukommt, entfernt wird oder verändert: Senden dieser Veränderungen an die clientseitige Collection. (Mittels `.observe()` am Cursor bzw. `.added()`, `.changed()` und `removed()`)
In dem Beispiel von oben stellen wir damit sicher, dass für den Benutzer nur die Beiträge, die ihn im Moment interessieren, nämlich die von Tom verfassten, in seinem clientseitigen Cache verfügbar sind.
<% end %>
### Einzelne Properties veröffentlichen
Wir haben eben gesehen, wie wir nur einige der Beiträge veröffentlichen können. Wir können die Daten aber auch noch feiner zerteilen. Schauen wir uns an, wie wir lediglich bestimmte *Properties* veröffentlichen.
Wie zuvor, benutzen wir `find()`, um einen Cursor zurückzugeben. Diesmal aber schließen wir bestimmte Felder aus:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({}, {fields: {
date: false
}});
});
~~~
<%= diagram "partialproperties", "Einzelne Properties veröffentlichen", "pull-center" %>
Selbstvertändlich können wir beide Techniken kombinieren. Wenn wir z.B. alle Beträge von Tom aber nicht deren Datum veröffentlichen wollen, können wir folgendermaßen schreiben:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({'author':'Tom'}, {fields: {
date: false
}});
});
~~~
### Zusammenfassung
Wir haben gesehen, wie wir von `autopublish`, dem Veröffentlichen aller Properties aller Dokumente aus allen Collections zum Veröffenlichen _mancher_ Properties _mancher_ Dokumente aus _manchen_ Collektions kommen.
Das sind die Basisfunktionen dessen, was du mit Veröffentlichungen bei Meteor machen kannst und diese einfachen Techniken sollten für die allermeisten Anwendungsfälle ausreichen.
Manchmal allerdings musst du darüber hinausgehen und Veröffentlichungen miteinander kombinieren, sie verbinden oder zusammenführen. Das werden wir in einem späteren Kapitel behandeln.