-
Notifications
You must be signed in to change notification settings - Fork 12
/
07s-latency-compensation.md.erb
131 lines (91 loc) · 6.96 KB
/
07s-latency-compensation.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
---
title: Latency Compensation
slug: latency-compensation
date: 0007/01/02
number: 7.5
sidebar: true
contents: Verstehe das Konzept der Latenz-Kompensation.|Die Applikation künstlich verlangsamen und sehen was passiert.| Lerne wie Meteor Methoden sich gegenseitig aufrufen.
paragraphs: 28
---
Im letzten Kapitel führten wir ein neues Konzept der Meteor-Welt ein: Methoden.
<%= diagram "latency1", "Without latency compensation", "pull-right" %>
Eine Meteor Methode ist die Möglichkeit, eine Abfolge von Befehlen auf dem Server in einer strukturierten Weise auszuführen. In unserem Beispiel brauchten wir eine Methode, weil wir sicherstellen wollten, dass dem neuen Post auch der Name des Autors und die aktuelle Serverzeit hinzugefügt wird.
Wenn eine Meteor Methode allerdings einfach ausgeführt wird, haben wir ein Problem. Stelle dir folgenden Ablauf vor: (Anmerkung: Die Timestamps sind zufällig generiert und nur für illustrative Zwecke gedacht)
- *+0ms:* Der Benutzer klickt den Senden-Button und der Browser feuert einen Methodenaufruf.
- *+200ms:* Der Server macht Änderungen an der Mongo Datenbank.
- *+500ms:* Der Client erhält diese Änderungen und aktualisiert das UI.
Wenn Meteor so operieren würde, dann wäre da eine kurze Verzögerung zwischen der Ausführung solcher Aktionen und der Abbildung der Änderungen (Diese Verzögerung wäre je nach geografischer Nähe zum Server grösser oder kleiner). Für eine moderne Webapplikation ist dies nicht akzeptabel.
### Latenz Kompensierung
<%= diagram "latency2", "With latency compensation", "pull-right" %>
Um diese Probleme zu vermeiden, führt Meteor das Konzept der Latenz-Kompensation ein. Die Definition der `post` Methode wurde in einer Datei im `collection/` Ordner gemacht. Das heisst, dass der Code auf dem Server und dem Client verfügbar ist und auf beiden Seiten zur selben Zeit ausgeführt wird.
Wenn du eine Methode aufrufst, sendet der Client diesen Aufruf an den Server, simuliert aber simultan dazu die Aktion auch auf der Client Collection aus. Somit sieht unser Workflow wie folgt aus:
- *+0ms:* Der Benutzer klickt auf den Senden Knopf und der Browser feuert den Methodenaufruf ab.
- *+0ms:* Der Client simuliert die Aktion des Methodenaufrufs auf der Client Collection und ändert das UI entsprechend.
- *+200ms:* Der Server macht Änderungen an der Mongo Datenbank.
- *+500ms:* Der Client erhält diese Änderungen unmittelbar vom Server zurück, macht die simulierten Änderungen rückgängig und ersetzt sie mit denen vom Server (In der Regel sind diese identisch). Das UI ändert dementsprechend.
### Latenz-Kompensation beobachten
Wir können eine kleine Änderung an der `post` Methode vornehmen, um das Konzept in Aktion zu beobachten. Dafür müssen wir fortgeschrittene Coding-Technik im npm `futures` Package anwenden, damit das Einfügen von Objekten in die Server Collection in unserer Methode künstlich verzögert wird.
Wir benützen `isSimulation` um Meteor zu fragen, ob die Methode im aktuellen Kontext als "stub" ausgeführt wird. Ein [stub](http://docs.meteor.com/#methods_header) ist eine Methodensimulation, welche Meteor auf dem Client ausführt, während die "wirkliche" Methode auf dem Server ausgeführt wird.
So werden wir Meteor fragen, ob der Code gerade auf dem Client ausgeführt wird. Wenn ja fügen wir den String am Ende des Titels vom Post hinzu, wenn nicht, fügen wir den String auf dem Server hinzu:
~~~js
Meteor.methods({
post: function(postAttributes) {
// […]
// pick out the whitelisted keys
var post = _.extend(_.pick(postAttributes, 'url', 'message'), {
title: postAttributes.title + (this.isSimulation ? '(client)' : '(server)'),
userId: user._id,
author: user.username,
submitted: new Date().getTime()
});
// wait for 5 seconds
if (! this.isSimulation) {
var Future = Npm.require('fibers/future');
var future = new Future();
Meteor.setTimeout(function() {
future.return();
}, 5 * 1000);
future.wait();
}
var postId = Posts.insert(post);
return postId;
}
});
~~~
<%= caption "collections/posts.js" %>
<%= highlight "6, 7, 13~22" %>
Bemerkung: Für den Fall dass es dich wundert, das `this` in `this.isSimulation` ist ein [Methodenaufruf-Objekt](http://docs.meteor.com/#meteor_methods) das Zugriff auf verschiedene nützliche Variablen zur Verfügung stellt.
Wie genau [Futures](https://npmjs.org/package/future) funktioniert, ist ausserhalb des Scopes dieses Buches, wir haben einfach ausgedrückt Meteor mitgeteilt, 5 Sekunden Pause zu machen bevor die Daten in die Server-Collection eingefügt werden.
Wir machen auch einen redirect zur Post Liste:
~~~js
Template.postSubmit.events({
'submit form': function(event) {
event.preventDefault();
var post = {
url: $(event.target).find('[name=url]').val(),
title: $(event.target).find('[name=title]').val(),
message: $(event.target).find('[name=message]').val()
}
Meteor.call('post', post, function(error, id) {
if (error)
return alert(error.reason);
});
Router.go('postsList');
}
});
~~~
<%= caption "client/views/posts/post_submit.js" %>
<%= highlight "15" %>
<%= scommit "7-5-1", "Demonstrate the order that posts appear using a sleep." %>
Wenn wir jetzt einen neuen Post kreieren, sehen wir die Latenz-Kompensation eindeutig. Zuerst wird ein Post clientseitig eingefügt (client) im Titel (als erster Post, linkt zu github)
<%= screenshot "s5-1", "Our post as first stored in the client collection" %>
Dann 5 Sekunden später wird dieser sauber mit dem Dokument vom Server ersetzt:
<%= screenshot "s5-2", "Our post once the client receives the update from the server collection" %>
### Client Collection Methoden
Man könnte denken Methoden seien kompliziert, aber in der Tat sind sie ziemlich simpel. Wir haben nämlich schon drei sehr einfache Methoden kennengelernt: Die Collection Mutations-Methoden: `insert`, `update` und `remove`.
Wenn du eine Server-Collection namens `posts` kreierst, definierst du automatisch drei Methoden: `posts/insert`, `posts/update` und `posts/delete`. Mit anderen Worten: Wenn du `Posts.insert()` in einer Client-Collection aufrufst, rufst du eine latenz-kompensierte Methode auf, die zwei Dinge macht:
1. Sie überprüft, ob wir die Mutation vornehmen dürfen (mittels `allow` und `deny` feedback (dies braucht in der Simulation allerdings nicht passieren))
2. Sie führt die eigentliche Modifikation auf dem Data-Store aus.
### Methoden rufen Methoden auf
Wenn du aufgepasst hast, hast du vielleicht festgestellt, dass unsere `post` Methode eine andere Methode (`posts/insert`) aufruft, sobald wir unseren `post` einfügen. Wie funktioniert das?
Wenn die Simulation (Client-side Version der Methode) ausgeführt ist, führen wir insert's Simulation aus (also wir fügen den `post` in unsere Client Collection ein), wenn die Server-side post Methode insert aufgerufen wird, brauchen wir uns nicht um die Simulation zu kümmern und das Einfügen findet lautlos statt.