Latency Compensation

Randfenster 7.5

Zu ...% übersetzt

In diesem Kapitel wirst du:

  • Verstehe das Konzept der Latenz-Kompensation.
  • Die Applikation künstlich verlangsamen und sehen was passiert.
  • Lerne wie Meteor Methoden sich gegenseitig aufrufen.
  • Im letzten Kapitel führten wir ein neues Konzept der Meteor-Welt ein: Methoden.

    Without latency compensation
    Without latency compensation

    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

    With latency compensation
    With latency compensation

    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 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:

    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;
      }
    });
    
    collections/posts.js

    Bemerkung: Für den Fall dass es dich wundert, das this in this.isSimulation ist ein Methodenaufruf-Objekt das Zugriff auf verschiedene nützliche Variablen zur Verfügung stellt.

    Wie genau Futures 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:

    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');
      }
    });
    
    client/views/posts/post_submit.js

    Commit 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)

    Our post as first stored in the client collection
    Our post as first stored in the client collection

    Dann 5 Sekunden später wird dieser sauber mit dem Dokument vom Server ersetzt:

    Our post once the client receives the update from the server collection
    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.