RESTful Services Teil II: Einschränkungen und Ziele

RESTful Services Teil II: Einschränkungen und Ziele

In Teil I dieser Serie habe ich über HTTP und seine Konstrukte geschrieben, die für das Webdienstdesign gelten.

HTTP ist nur ein kleiner Teil dessen, was zum Schreiben moderner Webdienste gehört.

In diesem Beitrag geht es darum, wie Sie diese Konstrukte anwenden , um wartbare, robuste Dienste zu erstellen.

REST definieren

REST steht für RE Präsentations - S tate T ransfer. Es ist ein architektonischer Stil. Dies bedeutet, dass REST keinen formalen Standard auferlegt, um zu bestimmen, ob ein Webdienst RESTful ist oder nicht. Vielmehr gibt es eine Reihe allgemeiner Einschränkungen, die jeweils ein bestimmtes Ziel verfolgen.

Diese Einschränkungen wurden erstmals von Roy Fielding eingeführt, der bereits im Jahr 2000 einer der Mitautoren der HTTP-Spezifikation war.

Fieldings Einschränkungen

Fielding hat diese Einschränkungen mit dem Ziel geschaffen, Anwendungen schneller, zuverlässiger und einfacher zu skalieren.

Als Webdienstdesigner sollte Ihr Dienst versuchen, diese Einschränkungen so genau wie möglich einzuhalten, um ihre Vorteile zu nutzen. Also lasst uns in sie eintauchen.

Einschränkung Nr. 1: Client-Server-Architektur

Die erste von REST vorgeschlagene Einschränkung ist die Trennung des Servers von seinem Client. Sie sollten nach Möglichkeit die Trennung von Bedenken zwischen Ihrem Server und den Clients fördern. Ihr Ziel sollte es sein, die Arbeitsteilung zwischen beiden zu maximieren und Überschneidungen zu minimieren.

Der Server oder das Back-End ist in der Regel für die Speicherung der persistenten Daten Ihrer Anwendung sowie der gesamten Geschäftslogik verantwortlich, die für die Interaktion mit ihr erforderlich ist. Dies kann Benutzerauthentifizierung, Autorisierung, Datenvalidierung usw. umfassen.

Der Client oder das Front-End ist dafür verantwortlich, Anfragen an den Service zu richten und dann mit der erhaltenen Antwort etwas Sinnvolles zu tun.

Der Client selbst kann ein Webdienst sein. In diesem Fall verbraucht er einfach die Daten. Alternativ kann es dem Benutzer zugewandt sein. Ein Beispiel hierfür wäre eine Web- oder mobile App. Dabei ist es auch für beide präsentieren die Daten an den Benutzer und stellt eine Schnittstelle für den Benutzer mit ihm interagieren .

Sie sollten in der Lage sein, jede dieser beiden Komponenten als Black Box in Bezug aufeinander zu behandeln. Auf diese Weise können sie unabhängig voneinander geändert werden. Dies fördert die Modularität innerhalb der Anwendung.

Dieses Konzept gilt nicht nur für RESTFul-Anwendungen oder sogar Webanwendungen. Die meisten Entwickler versuchen ohnehin, ihre Projekte in unabhängige Komponenten aufzuteilen. Indem Fielding dies als explizite Einschränkung des RESTful-Designs bezeichnet, fördert es diese Praxis weiter.

Schließlich reduziert das Reduzieren der Anzahl der Dinge, für die der Server verantwortlich ist, den erforderlichen Logikaufwand. Dies ermöglicht wiederum eine bessere Skalierbarkeit und eine höhere Leistung.

Einschränkung Nr. 2: Staatenlosigkeit

Die nächste wichtige Einschränkung, die von REST vorgeschlagen wird, ist die Staatenlosigkeit.

Im Großen und Ganzen besteht das Hauptziel eines zustandslosen Dienstes darin, eingehende Anforderungen autark zu machen und sie vollständig isoliert auszuführen.

Jede Anforderung muss alle Informationen enthalten, die der Server möglicherweise benötigt, um sie ordnungsgemäß zu verarbeiten und zu beantworten. Mit anderen Worten, der Server muss keine Informationen aus früheren Anforderungen verwenden. Die Verantwortung für die Aufrechterhaltung des Anwendungsstatus eines Kunden wird somit auf den Kunden selbst übertragen.

Um dies zu verstehen, sollten Sie einen sehr einfachen Webdienst in Betracht ziehen, der für die Beantwortung der Suchanfragen eines Benutzers verantwortlich ist. Die genaue Darstellung der gesuchten Entität spielt keine Rolle. Wichtig ist, dass der Server nicht Hunderte von Suchergebnissen auf einmal zurückgibt, sondern eine Paginierung verwendet : Es werden jeweils nur 10 Ergebnisse aus einer beliebig großen Ergebnismenge zurückgegeben.

In einem traditionellen "Stateful" -Entwicklungsmodell kann der Server so gestaltet sein, dass er alle seine Clients sowie alle Seiten, auf die sie bereits zugegriffen haben, im Auge behält.

Wenn eine Anforderung für eine neue Seite eingeht, kann der Server den Client in seinem System nachschlagen und die zuletzt empfangene Seite ermitteln.

Anschließend kann der Server mit der nächsten Seite antworten und sein System aktualisieren, um dies widerzuspiegeln. Dies wird fortgesetzt, während der Client weiter in der Ergebnismenge navigiert.

In einem alternativen, staatenlosen Ansatz wird die Verantwortung für die Aufrechterhaltung des Zustands dezentralisiert und auf den Kunden übertragen. Der Client muss dann die tatsächlichen Seitenzahlen des gewünschten Ergebnisses angeben , anstatt nach der nächsten Seite zu fragen. Zum Beispiel:

GET //my-awesome-web-service.com/pages/1GET //my-awesome-web-service.com/pages/3

Ein staatenloser Ansatz bringt einige wesentliche Vorteile mit sich. Zunächst einmal wird das Verfolgen des Clientstatus auf einem Server mit zunehmender Anzahl von Clients zunehmend anstrengend.

Zweitens, und was noch wichtiger ist, ist ein zustandsloser Dienst auch leicht verteilbar . Wenn ein Server für die Verwaltung von Informationen zum Status einer Anwendung verantwortlich ist, müssen zukünftige Anforderungen unbedingt an den Server weitergeleitet werden, auf dem diese Informationen gespeichert sind .

Wenn Hunderte von Servern für die Verarbeitung eingehender Anforderungen verantwortlich sind, muss ein Mechanismus vorhanden sein, um sicherzustellen, dass Anforderungen von einem bestimmten Client immer auf einer bestimmten Serverinstanz landen.

Für den Fall, dass eine Serverinstanz ausfällt, fallen alle Informationen über den Status eines Clients, der auf diesem Server gespeichert wurde, aus.

Natürlich könnten Sie eine Architektur entwickeln, in der Serverinstanzen Daten untereinander austauschen können. Dies erhöht jedoch die Komplexität.

Im Gegensatz dazu erleichtert ein zustandsloser Dienst das Hinzufügen und Entfernen von Serverinstanzen auf Ad-hoc-Basis erheblich. Sie können die Last dann nach Bedarf weiter zwischen ihnen ausgleichen.

Da die Server unabhängig von eingehenden Anforderungen sind, müssen beim Skalieren nur weitere Server zum Load Balancer hinzugefügt werden. Ebenso wirkt sich das absichtliche oder sonstige Beenden von Servern nicht auf die Zuverlässigkeit eines Dienstes aus.

Natürlich ist diese Einfachheit mit Kosten verbunden. Wenn der Client bei jeder Anforderung identische Daten anfügt, ist dies eine potenzielle Redundanzquelle. Die Bandbreite ist nicht frei, daher erhöhen zusätzliche übertragene Informationen den Overhead.

Einschränkung Nr. 3: Cache

Die dritte Einschränkung ist die explizite Cachefähigkeit. Die Idee ist, von einem Dienst zurückgegebene Nachrichten als explizit zwischenspeicherbar oder nicht zwischenspeicherbar zu markieren. Wenn sie zwischengespeichert werden können, sollte der Server die Dauer ermitteln, für die die Antwort gültig ist.

Wenn der Client Zugriff auf eine gültige zwischengespeicherte Antwort für eine bestimmte Anforderung hat, wird vermieden, dass dieselbe Anforderung wiederholt wird. Stattdessen wird die zwischengespeicherte Kopie verwendet. Dies erleichtert die Arbeit des Servers und trägt somit zur Skalierbarkeit und Leistung bei.

Dies ist eine Form der optimistischen Replikation - auch als verzögerte Replikation bezeichnet -, bei der der Dienst nicht versucht, eine 100% ige Konsistenz zwischen sich und seinen Clients zu gewährleisten, es sei denn, dies ist absolut kritisch. Stattdessen bringt es dieses Opfer im Austausch für einen Gewinn an wahrgenommener Leistung.

Beispielsweise kann eine API, die einer Blogging-Plattform entspricht, die Liste der Blog-Posts für einige Minuten zwischenspeichern, wenn sie weiß, dass die Häufigkeit, mit der Personen versuchen, auf die Posts zuzugreifen, die Häufigkeit, mit der neue Posts erstellt werden, bei weitem überschreitet. Infolgedessen werden Benutzern gelegentlich veraltete Daten angezeigt , aber das gesamte System bietet eine bessere Leistung.

Natürlich sind die Cachefähigkeit einer Ressource und ihre Dauer nicht universell und erfordern einige Überlegungen. Wenn Sie falsch gewählt haben, kann dies Ihre Benutzer frustrieren.

Webdienste erreichen normalerweise die Cachefähigkeit mithilfe des Standard- Cache-Control- Headers. Manchmal tun sie dies in Verbindung mit anderen von HTTP angegebenen Headern.

Der Cache-Control-Header dient effektiv als Switch und bestimmt, ob ein Browser die betreffende Antwort zwischenspeichern soll.

Als privat gekennzeichnete Ressourcen werden nur vom Client zwischengespeichert und sind daher nur auf diesen einen Client beschränkt.

Ressourcen, die als öffentlich gekennzeichnet sind , können dagegen von einem oder mehreren Zwischenproxys zwischen dem Dienst und dem Client zwischengespeichert werden.

Infolgedessen können diese Ressourcen möglicherweise mehreren Benutzern bereitgestellt werden. Alternativ kann man das Argument no-cache übergeben und das Zwischenspeichern der Ressource vollständig stoppen.

So sieht einer dieser Cache-Control-Header aus:

Cache-Control: public;max-age=3431901

In der Kopfzeile können Sie auch die Dauer angeben, für die die Ressource gültig ist. Dadurch wird dem Client mitgeteilt, wann er die zwischengespeicherte Kopie nicht mehr verwenden und eine neue Kopie anfordern soll.

Hier ist die Logik dahinter:

Abgesehen davon verfügt HTTP auch über Mechanismen, um eine so genannte bedingte Anforderung auszuführen . Ziel ist es, dass der Server bestimmte Ressourcen nur dann an den Client zurückgibt, wenn bestimmte Bedingungen erfüllt sind .

Angenommen, der Client hat eine gespeicherte Kopie einer Ressource in seinem Cache, kann er eine Anforderung an den Server senden, um festzustellen, ob eine aktualisierte Kopie derselben Ressource vorhanden ist. Wenn es eine gibt, gibt der Server die neue Kopie zurück. Andernfalls wird der Client angewiesen, seine lokale Kopie weiter zu verwenden.

Auf diese Weise wird eine redundante Datenübertragung über das Netzwerk verhindert und gleichzeitig sichergestellt, dass der Client jederzeit Zugriff auf neue Daten hat.

Mit HTTP können Sie dies auf verschiedene Arten erreichen:

Caching-Ansatz Nr. 1: If-Modified-Since / Last-Modified

Zusammen mit jeder Antwort, die der Server zurücksendet, kann er einen Zeitstempel für die letzte Änderung anhängen . Dies zeigt an, wann die Ressource zuletzt geändert wurde.

Wenn der Client die Ressource in Zukunft erneut anfordern muss, sendet er die Anforderung wie gewohnt an den Server, jedoch mit einem relevanten If-Modified-Since- Header. Dadurch wird der Server angewiesen, die neue Kopie der Ressource zurückzugeben, sofern eine vorhanden ist.

Andernfalls gibt der Server den Statuscode 304 zurück, derweist den Client an, die bereits vorhandene Kopie weiter zu verwenden.

Caching-Ansatz Nr. 2: If-None-Match / ETag

Dieses Schema funktioniert ähnlich wie das vorherige, außer dass Ressourcen identifiziert werden. Anstatt Zeitstempel zu verwenden, sendet der Server mit jeder Antwort einen eindeutigen Hash zurück, der den Status der Ressource zu diesem Zeitpunkt erklärt (bekannt als ETag).

Für zukünftige Anfragen sendet der Client das entsprechende ETag an den Server. Wenn eine Ressource mit demselben ETag vorhanden ist, weist der Server den Client an, die zwischengespeicherte Kopie weiterhin zu verwenden. Andernfalls sendet der Server einen neuen an den Client zurück.

Das Caching ist kompliziert. Wenn Ihr Dienst mehr Benutzer hinzufügt, möchten Sie mehr über das Caching erfahren und erfahren, wie Sie es zu Ihrem Vorteil nutzen können.

Einschränkung Nr. 4: Einheitliche Schnittstelle

Die einheitliche Schnittstelle (oder der einheitliche Vertrag ) sagt aRESTful Service, was zu dienen ist, in Form eines Dokuments, eines Bildes, eines nicht virtuellen Objekts usw.

REST bestimmt jedoch nicht, wie Sie mit diesen Ressourcen interagieren, solange sie konsistent und gut verstanden sind.

Bevor ein Kunde mit einem RESTful-Service interagieren kann, muss er sich im Allgemeinen auf Folgendes einigen:

  1. Identifizierung: Es muss eine Möglichkeit geben, jede Ressource, die der Service bietet, eindeutig zu identifizieren.
  2. Manipulation: Es muss eine Reihe von Standardoperationen geben, die für jede Ressource mit vorhersehbaren Ergebnissen ausgeführt werden können. Die Ergebnisse dieser Operationen müssen auch selbstbeschreibend und eindeutig verstanden sein.

HTTP verwendet beispielsweise URLs zur Identifizierung von Ressourcen. Es werden auch eine Handvoll Aktionsverben und gut dokumentierte Statuscodes verwendet, um die Interaktion mit Ressourcen zu erleichtern. (Für eine detailliertere Erläuterung der HTTP-Konstrukte können Sie Teil I dieser Reihe lesen.)

Bis zu diesem Zeitpunkt haben wir RESTful-Services als streng an HTTP gebunden betrachtet. In Bezug auf Webdienste ist dies fast immer korrekt.

Theoretisch kann REST jedoch über jedes Protokoll implementiert werden, das einen angemessenen Weg bietet, um die beiden oben beschriebenen Bedingungen zu erreichen. Aus diesem Grund wird REST manchmal auch als REST über HTTP bezeichnet , um zu verdeutlichen, dass es über das Web verwendet wird.

Einschränkung Nr. 5: Ein geschichtetes System

Ein geschichtetes System baut auf der zuvor diskutierten Client-Server-Einschränkung auf und erzwingt eine noch stärkere Trennung von Bedenken. Die Gesamtarchitektur Ihres Dienstes kann in einzelne Ebenen unterteilt werden, die jeweils eine bestimmte Funktion erfüllen.

Noch wichtiger ist, dass jede Schicht unabhängig agieren und nur mit den unmittelbar angrenzenden Schichten interagieren muss. Dies erzwingt, dass sich Anforderungen auf vorhersagbare Weise ausbreiten, ohne Schichten zu umgehen.

Zum Skalieren können Sie beispielsweise einen Proxy verwenden, der sich wie ein Load Balancer verhält. Der einzige Zweck des Proxys besteht dann darin, eingehende Anforderungen an die entsprechende Serverinstanz weiterzuleiten.

Der Kunde hingegen muss sich dieser Aufteilung nicht bewusst sein. Es werden einfach weiterhin Anfragen an dieselbe URL gestellt, ohne sich um die Details der Verarbeitung der Anfragen zu kümmern.

In ähnlicher Weise kann es eine andere Schicht in der Architektur geben, die für das Zwischenspeichern von Antworten verantwortlich ist, um die vom Server zu erledigende Arbeit zu minimieren.

Eine andere Schicht kann sich wie ein Gateway verhalten und HTTP-Anforderungen in andere Protokolle übersetzen.

Eine Möglichkeit, dies zu verwenden, besteht darin, einen FTP-Server zu implementieren. Der Client würde weiterhin Anfragen an einen HTTP-Server stellen, während tatsächlich ein FTP-Server die Arbeit unter der Haube erledigt.

Genau wie bei der Client-Server-Unterscheidung minimiert diese mehrschichtige Systemeinschränkung das Risiko der Kopplungsfunktionalität in Ihrem Dienst, jedoch auf Kosten des zusätzlichen Overheads im System.

Fazit

Zusammenfassend haben wir uns die wichtigen Einschränkungen angesehen, die Sie beim Entwerfen von RESTful-Webdiensten berücksichtigen sollten. Ich möchte auch betonen, dass dies zwar technisch schwierige Anforderungen sind, die ein Service erfüllen muss, um als RESTful zu gelten, dies in der Praxis jedoch nicht immer der Fall ist.

Beim Aufbau realer Dienste geht es mehr darum, die anstehenden Probleme zu lösen, als technische Definitionen zu erfüllen. Infolgedessen werden diese Einschränkungen am häufigsten von Entwicklern und Architekten als Richtlinien verwendet, die dann entscheiden, welche Regeln sie befolgen müssen, um ihre eigenen spezifischen Ziele zu erreichen.

Hierher kommen die Begriffe teilweise erholsam und vollständig erholsam. Und tatsächlich,Die meisten Dienste, denen Sie online begegnen, sind technisch nicht vollständig REST-fähig.

Im nächsten und letzten Teil dieser Reihe werde ich die Prinzipien von HATEOAS sowie das Richardson Maturity Model diskutieren. Dies bietet einen etwas quantitativeren Ansatz, um festzustellen, wie RESTful ein Webdienst wirklich ist. Finden Sie es hier!

Ich hoffe, dies war eine nützliche Einführung in die Erstellung einer RESTful-Anwendung. Das Verständnis der Prinzipien von REST wird Ihnen sicherlich helfen, wenn Sie mit vielen APIs von Drittanbietern arbeiten. Oder sogar, wenn Sie Ihre eigenen Anwendungen im Web, auf Mobilgeräten oder anderswo erstellen.

Als Bonus habe ich hier auch eine für dieses Thema relevante Präsentation hochgeladen. Das Dia-Deck stammt aus einem kurzen Vortrag, den ich vor einigen Monaten an meiner Universität mit dem Titel „Der Einfluss einer RESTful-Architektur auf die Anwendungsleistung und Skalierbarkeit“ gehalten habe. Ich hoffe du findest es nützlich :)

Lassen Sie mich in den Kommentaren wissen, ob Sie Feedback haben oder sich über mein LinkedIn an mich wenden können.

Hier sind einige Ressourcen für weitere Informationen zu REST:

Schlüsselprinzipien der Softwarearchitektur - MSDN

Rest Explained, eine Präsentation

Erholsame Webdienste - Sam Ruby

WhatIsRest.com

Ruhe in der Praxis