So richten Sie eine Microservices-Architektur in Ruby ein: Eine Schritt-für-Schritt-Anleitung
Veröffentlicht: 2022-03-11Was sind Microservices?
Microservices sind einer der neuesten Trends im Softwaredesign, bei dem mehrere unabhängige Dienste untereinander kommunizieren und über eigene Prozesse und Ressourcen verfügen. Dieser Ansatz unterscheidet sich von einem typischen Client-Server-Anwendungsdesign. Die übliche Client-Server-Anwendung besteht aus einem oder mehreren Clients, einem monolithischen Back-End, das alle Domänendaten und -logik enthält, und einer API, die Clients den Zugriff auf das Back-End und seine Funktionalität ermöglicht.
In einer Microservices-Architektur wird das beschriebene monolithische Backend stattdessen durch eine Reihe von verteilten Diensten ersetzt. Dieses Design ermöglicht eine bessere Trennung der Verantwortlichkeiten, eine einfachere Wartung, eine größere Flexibilität bei der Auswahl der Technologien für jeden Dienst sowie eine einfachere Skalierbarkeit und Fehlertoleranz. Gleichzeitig haben komplexe verteilte Systeme ihre eigenen Herausforderungen. Sie haben eine größere Chance, mit Rennbedingungen fertig zu werden, und sie sind schwieriger zu debuggen, da Probleme nicht einfach einem einzelnen Dienst zugeordnet werden können, sondern auf viele verteilt sind. Wenn Sie sich beim Aufbau eines solchen Systems nicht bemühen, die bewährten Verfahren zu befolgen, werden Sie möglicherweise von Bränden umgeben, von denen Sie nicht wissen, wie Sie sie löschen können. Besondere Sorgfalt muss bei den Payload-Verträgen der Dienste walten, da Änderungen an einem Dienst alle seine Clients und folglich die gesamte Service-Suite des Back-Ends betreffen können.
Alle diese Überlegungen sind wichtig, aber nehmen wir an, Sie haben bereits darüber nachgedacht. Jetzt möchten Sie einen Weg finden, um selbst ein Microservices-Back-End zu erstellen. Lassen Sie uns also direkt darauf eingehen.
So richten Sie eine Microservices-Architektur ein
Es gibt derzeit viele Möglichkeiten, wie Sie Ihre Microservices einrichten können, und in diesem Leitfaden konzentrieren wir uns auf eine Broker-Architektur.
Eine Broker-Architektur
Eine Broker-Architektur ist eine der Möglichkeiten, wie Sie Ihre Dienste dazu bringen können, untereinander zu kommunizieren. Darin umgeben alle Dienste einen Messaging-Server, den Broker, und alle sind mit ihm verbunden. Dienste senden Nachrichten an den Broker, der dann weiß, welchen anderen Dienst oder welche Dienste er benötigt, um diese Nachrichten weiterzuleiten. Auf diese Weise müssen Dienste keine Informationen über andere Dienste speichern. Stattdessen verlassen sie sich darauf, dass der Broker sich um das gesamte Messaging kümmert, und es ermöglicht ihnen, isoliert zu sein und sich nur auf ihre bestimmte Domäne zu konzentrieren. Ein Broker kann auch Nachrichten speichern, wenn seine Empfänger ausgefallen sind, sodass Sender und Empfänger nicht gezwungen werden, gleichzeitig aktiv zu sein, wodurch eine noch größere Isolierung ermöglicht wird. Natürlich hat diese Lösung auch Nachteile, da der Broker schnell zu einem Engpass werden kann, da die gesamte Kommunikation über ihn laufen muss, und er kann auch zu einem Single Point of Failure für Ihr Backend werden. Es gibt jedoch einige Möglichkeiten, diese Probleme zu entschärfen. Eine Möglichkeit besteht darin, mehrere Instanzen des Brokers parallel laufen zu lassen, was eine bessere Systemfehlertoleranz ermöglichen würde. Eine andere Möglichkeit wäre die Verwendung anderer Architekturen. Alternative Architekturen unterscheiden sich von der Architektur, die wir in diesem Handbuch implementieren, indem sie keinen Broker verwenden, eine andere Brokerarchitektur verwenden oder ein anderes Messaging-Protokoll wie HTTP verwenden.
Kommunikation zwischen Diensten
In diesem Leitfaden verwenden wir ZeroMQ, um die Kommunikation zwischen den Diensten und dem Broker abzuwickeln.
ZeroMQ bietet eine Protokollabstraktionsschicht, die mehrteilige asynchrone Nachrichten über zufällige Transporte verarbeitet. Die Vorteile der Verwendung von ZeroMQ für die Nachrichtenübermittlung zwischen Diensten und Brokern liegen außerhalb des Rahmens dieses Leitfadens, daher gehen wir hier nicht darauf ein, aber wenn Sie mehr darüber erfahren möchten, lesen Sie den folgenden Quora-Artikel. Wenn Sie daran interessiert sind, andere Möglichkeiten herauszufinden, wie Sie Ihre Dienste miteinander kommunizieren lassen können, schlage ich vor, dass Sie sich den Artikel Broker vs. Brokerless ansehen, um zu sehen, was sonst noch erreicht werden kann.
Erstellen der Microservices-Suite
Dieser Artikel führt Sie durch alle Schritte, die zum Erstellen Ihrer Microservices-Suite erforderlich sind. Unser System wird aus einem Broker und einem Service bestehen. Wir werden auch ein kleines Client-Skript verwenden, um Aufrufe an die Service-Suite zu testen, aber denken Sie daran, dass der Client-Code überall problemlos verwendet werden kann.
Also fangen wir an zu bauen.
Einstieg
Stellen wir zunächst sicher, dass Sie über alles verfügen, was Sie zum Ausführen des Brokers und des Dienstes benötigen. Beginnen Sie zunächst mit dem Herunterladen und Installieren von Node.js, ZeroMQ und Git auf Ihrem Computer. Wenn Sie OSX verwenden, gibt es Homebrew-Pakete für jeden von ihnen, und die meisten Linux-Distributionen haben auch ein Paket für jeden von ihnen, also sollten Sie damit kein Problem haben. Windows-Benutzer können einfach die oben angegebenen Download-Links verwenden.
Ausführen des Brokers
Nachdem wir alle erforderlichen Abhängigkeiten installiert haben, lassen Sie uns unseren Broker zum Laufen bringen. In diesem Leitfaden verwenden wir eine Node.js-Implementierung des Brokers, die Teil der ZMQ Service Oriented Suite ist. Den Code und die Dokumentation finden Sie auf GitHub. Um den Broker auszuführen, klonen Sie zuerst einen Broker-Bootstrap auf Ihren Computer. Dieses Repository ist ein Bootstrap für die Verwendung der obigen Broker-Bibliothek. Beachten Sie, dass dieser Schritt nicht erforderlich ist, da die ursprüngliche Bibliothek selbst lauffähig ist, aber der Unterschied zwischen den beiden besteht darin, dass Sie im Bootstrap-Repository die Standardkonfigurationen ändern können.
Verwenden Sie also zunächst den folgenden Git-Befehl, um das Projekt auf Ihren Computer herunterzuladen:
$ git clone [email protected]:dadah/zmq-broker-bootstrap.git
Nachdem Sie das getan haben, wechseln Sie in das erstellte Verzeichnis:
$ cd zmq-broker-bootstrap
Installieren Sie nun die Paketabhängigkeiten:
$ npm install
Der Makler ist nun bereit. Führen Sie den folgenden Befehl aus, um Ihren Broker auszuführen:
$ bin/zss-broker run
Sie finden Konfigurationsdateien für jede Umgebung im Verzeichnis config/
. Dies ist die Standardentwicklungskonfiguration:
{ "broker": { "backend": "tcp://127.0.0.1:7776", "frontend": "tcp://127.0.0.1:7777" }, "log": { "consolePlugin": { "level": "debug" } } }
Der backend
-Parameter definiert die ip:port
-Adresse des Backends und Frontends des Brokers. An der Back-End-Adresse empfängt der Broker Anfragen von und antwortet an die Dienste, und an der Front-End-Adresse empfängt und sendet er an die Dienst-Clients. Sie können die Protokollierungsebene auch festlegen, indem Sie log.consolePlugin.level
. Mögliche Werte sind trace
, debug
, info
, warn
und error
, und sie bestimmen die Menge der Protokollierungsinformationen, die der Broker-Prozess ausgibt.
Ausführen des Dienstes
Nachdem Sie Ihren Broker eingerichtet haben, ist es an der Zeit, Ihren ersten Ruby-Microservice zu entwickeln. Öffnen Sie zunächst ein neues Konsolenfenster. Erstellen Sie dann ein Verzeichnis, in dem Ihre Dienste gespeichert werden, und wechseln Sie dann zu diesem Verzeichnis. In diesem Handbuch verwenden wir den Ruby-Client und -Dienst der ZMQ SOA Suite. Es ist ein Bootstrap-Service „Hello World“ verfügbar, also verwenden wir ihn, um unseren ersten Microservice zum Laufen zu bringen.
Gehen Sie in Ihr Diensteverzeichnis und klonen Sie das Bootstrap-Repository:
$ git clone [email protected]:dadah/zmq-service-suite-ruby-bootstrap.git
Wechseln Sie in das neu erstellte Verzeichnis:
$ cd zmq-service-suite-ruby-bootstrap
Installieren Sie nun alle Abhängigkeiten:
$ bundle install
Führen Sie den folgenden Befehl aus, um den Dienst zu starten:
$ bin/zss-service run
Toll. Sie haben Ihren ersten Dienst eingerichtet und ausgeführt.
Wenn Sie zu dem Konsolenfenster gehen, in dem Sie Ihren Broker ausgeführt haben, sehen Sie die folgende Ausgabe:
2015-12-15 16:45:05 | INFO | BROKER - Async Broker is waiting for messages... 2015-12-15 16:45:14 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 2015-12-15 16:45:14 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 to SMI.UP request... 2015-12-15 16:45:14 | INFO | SMI - SMI register for sid: HELLO-WORD instance: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b! 2015-12-15 16:45:14 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 with status: 200 2015-12-15 16:45:15 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a 2015-12-15 16:45:15 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a to SMI.HEARTBEAT request... 2015-12-15 16:45:15 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a with status: 200 2015-12-15 16:45:16 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 2015-12-15 16:45:16 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 to SMI.HEARTBEAT request... 2015-12-15 16:45:16 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 with status: 200
Dieses Protokoll bedeutet, dass der Broker die Existenz eines neuen Dienstes bestätigt hat und Heartbeat-Meldungen von ihm erhält. Jede Sekunde sendet der Dienst eine Heartbeat-Nachricht an den Broker, damit er weiß, dass die Instanz des Dienstes aktiv ist.
Konsumieren vom Dienst
Jetzt haben wir also einen Dienst am Laufen, wie verwenden wir ihn?
Im Bootstrap-Repository gibt es einen Dummy-Client, mit dem Sie Ihren „Hello World“-Dienst testen können. Öffnen Sie einfach ein neues Konsolenfenster oder eine neue Registerkarte und gehen Sie zu Ihrem Dienstverzeichnis. Sobald Sie dort sind, führen Sie den folgenden Befehl aus:
$ bin/zss-client
Sie sollten so etwas sehen:
15-49-15 16:49:54 | INFO | ZSS::CLIENT - Request 90a88081-3485-45b6-91b3-b0609d64592a sent to HELLO-WORD:*#HELLO/WORLD with 1.0s timeout 15-49-15 16:49:54 | INFO | ZSS::CLIENT - Received response to 90a88081-3485-45b6-91b3-b0609d64592a with status 200 "Hello World"
Wenn Sie zu dem Konsolenfenster gehen, in dem Ihr Dienst ausgeführt wird, sollten Sie Folgendes sehen:
Started hello-word daemon... 15-45-15 16:45:14 | INFO | ZSS::SERVICE - Starting SID: 'HELLO-WORD' ID: 'hello-word#aaa65374-8585-410a-a41d-c8a5b024553b' Env: 'development' Broker: 'tcp://127.0.0.1:7776' 15-49-15 16:49:54 | INFO | ZSS::SERVICE - Handle request for HELLO-WORD:*#HELLO/WORLD 15-49-15 16:49:54 | INFO | ZSS::SERVICE - Reply with status: 200
Gut. Sie haben gerade Ihren „Hello World“-Microservice gestartet und genutzt. Dies ist jedoch nicht das, was wir uns vorgenommen haben. Wir wollen unsere Dienstleistung(en) aufbauen. Kommen wir also dazu.
Aufbau Ihres Dienstes
Stoppen wir zuerst unseren „Hello World“-Dienst. Gehen Sie zum Konsolenfenster des Dienstes und drücken Sie Ctrl+C
, um den Dienst zu stoppen. Als nächstes müssen wir unseren „Hello World“-Dienst in den „Person“-Dienst umwandeln.
Codestruktur
Beginnen wir mit einem Blick auf den Codebaum des Projekts. Es sieht aus wie das:
- Im
bin
-Verzeichnis speichern Sie die Skripts, die Ihren Dienst starten. - Das Verzeichnis
config
speichert alle Konfigurationsdateien.- In der Datei
boot.rb
können Sie alle Ihre Dienstabhängigkeiten hinzufügen. Wenn Sie es öffnen, können Sie feststellen, dass dort bereits viele Abhängigkeiten aufgelistet sind. Wenn Sie weitere hinzufügen müssen, sollten Sie dies hier tun. - Die Datei
application.yml
speichert alle Ihre Anwendungseinstellungen. Wir werden uns diese Datei später ansehen. - Im Verzeichnis
config/initializers
können Sie Ihre Initialisierungsskripte hinzufügen. Hier können Sie beispielsweise Einstellungen für ActiveRecord- oder Redis-Verbindungen hinzufügen. Die Skripts, die Sie diesem Verzeichnis hinzufügen, werden beim Dienststart ausgeführt.
- In der Datei
- Im Verzeichnis
db/migrate
können Sie Ihre ActiveRecord- oder Sequel-Migrationen speichern, falls vorhanden. Falls nicht, können Sie dieses Verzeichnis auch ganz löschen. - Im
lib
-Verzeichnis befindet sich Ihr Hauptanwendungscode.- Die Datei
settings.rb
lädt einfach die Dateiapplication.yml
und stellt sie im gesamten Umfang des Dienstes zur Verfügung, sodass Sie überall auf Ihre Konfigurationen zugreifen können. Beispielsweise gibtSettings.broker.backend
die Broker-Backend-Adresse zurück, die Sie in der obigen YML-Datei definiert haben. - In der Datei
service_register.rb
registrieren Sie Ihre Dienste und Dienstrouten. Wir werden es später erklären. - Die Datei
hello_world_service.rb
definiert die Endpunkte des Dienstes „Hello World“. - Im Verzeichnis
lib/daos
speichern Sie Ihre ActiveModel-Objekte, wenn Sie ActiveRecord verwenden, oder alle anderen Datenzugriffsobjekte, die Sie eventuell erstellen, wie z. B. Ihre Sequel-Modelle. - Das Verzeichnis
lib/dtos
speichert Ihre Datenübertragungsobjekte. Diese Objekte werden schließlich an die Clients des Dienstes zurückgesendet. - Das Verzeichnis
lib/repositories
speichert Ihre Repositories. Repositorys sind Objekte, die es Diensten ermöglichen, auf Daten zuzugreifen, und sie sind die einzigen Objekte, die DAOs verarbeiten dürfen. Wenn also ein Dienst eine Gruppe von „Hello World“-Instanzen wünscht, fragt er das Repository danach. Das Repository wiederum verwendet die entsprechenden DAOs, um die relevanten Daten aus der Datenbank zu holen. Die Daten werden dann einer geeigneten „HelloWorld“-DTO- oder „HelloWorld“-DTO-Sammlung zugeordnet, die an den Dienst zurückgegeben wird. - Im Verzeichnis
lib/repositories/mappers
speichern Sie Ihre Mapper. Mapper sind Objekte, die DAOs in DTOs umwandeln und umgekehrt.
- Die Datei
Die Datei application.yml
aus dem Verzeichnis config
sieht folgendermaßen aus:

defaults: &defaults broker: backend: tcp://127.0.0.1:7776 frontend: tcp://127.0.0.1:7777 logging: console: level: info development: <<: *defaults test: <<: *defaults production: <<: *defaults
Diese Einstellung legt einfach die Back-End- und Front-End-Adresse des Brokers sowie die Protokollierungsstufe fest.
Wenn all dies bisher verwirrend klingt, machen Sie sich keine Sorgen, da es im weiteren Verlauf klarer wird.
Dienst „Person“.
Also weiter mit unserem Service „Person“. Beginnen wir mit der Konfiguration der Datenbankverbindung. Öffnen Sie die Datei config/initializers/active_record.rb
und kommentieren Sie die einzige Zeile dort aus. Fügen Sie dann Ihrer Entwicklungskonfiguration in der application.yml
den folgenden Eintrag hinzu, sodass es so aussieht:
defaults: &defaults broker: backend: tcp://127.0.0.1:7776 frontend: tcp://127.0.0.1:7777 logging: console: level: info database: adapter: postgresql database: zss-tutorial-development
Nachdem Sie Ihre Datenbankkonfiguration hinzugefügt haben, müssen Sie die Datenbank erstellen. Derzeit gibt es keine Möglichkeit, dies automatisch zu tun, es sei denn, Sie verwenden eine standardmäßige PostgreSQL-Datenbank. In diesem Fall können Sie einfach Folgendes ausführen:
$ rake db:create
Wenn Sie eine andere Datenbank bevorzugen, müssen Sie das entsprechende Gem zur Gemfile hinzufügen und dann das Projekt bündeln.
Als nächstes kommt die Migration. Erstellen Sie dazu einfach die Datei db/migrate
migrate mit dem Namen 000_creates_persons.rb
:
$ touch db/migrate/000_creates_persons_table.rb
Öffnen Sie die Datei und erstellen Sie die Migration wie bei einer normalen Rails-Migration:
class CreatesPersons < ActiveRecord::Migration def change create_table :persons do |t| t.name t.timestamps end end end
Als nächstes führen Sie es aus:
$ rake db:migrate == 0 CreatesPersons: migrating ================================================ -- create_table(:persons) DEPRECATION WARNING: `#timestamp` was called without specifying an option for `null`. In Rails 5, this behavior will change to `null: false`. You should manually specify `null: true` to prevent the behavior of your existing migrations from changing. (called from block in change at /Users/francisco/Code/microservices-tutorial/db/migrate/000_creates_persons.rb:6) -> 0.0012s == 0 CreatesPersons: migrated (0.0013s) =======================================
Nachdem wir unsere Tabelle erstellt haben, erstellen wir ein Modell dafür. Erstellen Sie die Datei lib/daos/person.rb
:
$ touch lib/daos/person.rb
Bearbeiten Sie es wie folgt:
module DAO class Person < ActiveRecord::Base end end
Da ist dein Modell. Jetzt müssen Sie ein DTO-Modell für eine „Person“ erstellen, damit Sie es an den Client zurückgeben können. Erstellen Sie die Datei lib/dtos/person.rb
:
$ touch lib/dtos/person.rb
Bearbeiten Sie es wie folgt:
module DTO class Person < Base attr_reader :id, :name end end
Als nächstes müssen Sie einen Mapper erstellen, um das „Person“-DAO in ein „Person“-DTO umzuwandeln. Erstellen Sie die Datei lib/repositories/mappers/person.rb
und bearbeiten Sie sie wie folgt:
module Mapper class Person < Mapper::Base def self.to_dao dto_instance DAO::Person.new id: dto_instance.id, name: dto_instance.name end def self.to_dto dao_instance DTO::Person.new id: dao_instance.id, name: dao_instance.name end end end
Hier erfordert Mapper::Base
die Implementierung von self.to_dao
und self.to_dto
. Wenn Sie dies nicht möchten, können Sie stattdessen self.map
implementieren und die Mapper::Base.map
, die to_dao
oder to_dto
, je nachdem, ob das empfangene Attribut ein DAO oder ein DTO ist.
Jetzt haben Sie ein DAO, um auf Ihre Datenbank zuzugreifen, ein DTO, um es an den Client zu senden, und einen Mapper, um das eine in das andere umzuwandeln. Sie können nun diese drei Klassen innerhalb eines Repositorys verwenden, um die Logik zu erstellen, die es Ihnen ermöglicht, Personen aus der Datenbank abzurufen und eine entsprechende Sammlung von DTOs zurückzugeben.
Lassen Sie uns dann das Repository erstellen. Erstellen Sie die Datei lib/repositories/person.rb
:
$ touch lib/dtos/person.rb
Bearbeiten Sie es wie folgt:
module Repository class Person < Repository::Base def get DAO::Person.all.map do |person| Mapper::Person.map(person) end end end end
Dieses Repository hat nur die Instanzmethode get
die einfach alle Personen aus der Datenbank holt und sie in eine Sammlung von Personen-DTOs abbildet - ziemlich einfach. Bringen wir das jetzt alles zusammen. Jetzt müssen nur noch der Dienst und der Endpunkt erstellt werden, der dieses Repository aufruft. Erstellen wir dazu die Datei lib/person_service.rb
:
$ touch lib/person_service.rb
Bearbeiten Sie es wie folgt:
class PersonService < BaseService attr_reader :person_repo def initialize @person_repo = Repository::Person.new end def get payload, headers persons = person_repo.get() if persons.empty? raise ZSS::Error.new(404, "No people here") else persons.map &:serialize end end end
Der Dienst „Person“ initialisiert das Repository in seinem Initialisierer. Alle öffentlichen Instanzmethoden des Dienstes „Person“ haben Payload und Header, die Sie weglassen können, wenn Sie sie nicht benötigen. Beide sind Hashie::Mash
Instanzen und speichern die an den Endpunkt gesendeten Variablen, entweder als Attribute oder Header, und ihre Antworten ahmen HTTP-Antworten nach, da jede Antwort einen Statuscode hat, den Clients verwenden können, um das Ergebnis der an gesendeten Anforderungen herauszufinden -Dienst zusammen mit der Antwortnutzlast des Dienstes. Antwortcodes sind die gleichen, die Sie von einem HTTP-Server erwarten würden. Beispielsweise gibt eine erfolgreiche Anfrage den Statuscode 200 zusammen mit der Nutzlast der Antwort zurück. Wenn ein Dienstfehler auftritt, lautet der Statuscode 500, und wenn etwas mit den an den Server gesendeten Parametern nicht stimmt, lautet der Statuscode 400. Der Dienst kann mit den meisten HTTP-Statuscodes zusammen mit seiner Nutzlast antworten. Wenn Sie beispielsweise möchten, dass Ihr Dienst seinen Clients mitteilt, wenn sie nicht auf einen bestimmten Endpunkt zugreifen dürfen, können Sie dies tun, indem Sie mit einem 403-Code antworten. Sie können ein weiteres Beispiel für Antwortcodes sehen, wenn Sie sich unseren Servicecode oben ansehen. Im get
-Endpunkt geben wir den Statuscode 404 zusammen mit der optionalen Nachricht „Keine Personen hier“ zurück, wenn keine Personen gefunden werden, genau wie ein HTTP-Server 404 zurückgeben würde, wenn keine Ressourcen verfügbar sind. Wenn das Repository tatsächlich Personen zurückgibt, serialisiert der Dienst die DTOs und gibt sie an den Client zurück. Jedes DTO verfügt über einen Standard-Serializer, der ein JSON-Objekt mit den Schlüsseln und entsprechenden Werten zurückgibt, die in der DTO-Definition entweder als attr_reader
oder attr_accessible
definiert sind. Sie können den Serialisierer natürlich überschreiben, indem Sie Ihre Serialize-Methode in Ihren DTO-Klassen definieren.
Nachdem wir einen Dienst definiert haben, müssen wir ihn registrieren. Dies ist der letzte Schritt. Öffnen Sie die Datei lib/service_register.rb
und ersetzen Sie alle Vorkommen von „HelloWorld“ durch „Person“, sodass die Datei schließlich ungefähr so aussieht:
module ZSS class ServiceRegister def self.get_service config = Hashie::Mash.new( backend: Settings.broker.backend ) service = ZSS::Service.new(:person, config) personInstance = PersonService.new service.add_route(personInstance, :get) return service end end end
Wie Sie wahrscheinlich bemerkt haben, gibt es eine kleine Änderung im add_route
-Aufruf. Wir haben die Zeichenfolge „HELLO/WORLD“ entfernt. Das liegt daran, dass die Zeichenfolge nur benötigt wird, wenn das Dienstverb nicht mit der Methode übereinstimmt, die es implementiert. In unserem Fall ist beim Aufrufen des Personendienstes mit dem Verb GET die aufzurufende Methode get
, sodass wir den String weglassen können.
In der Klasse ServiceRegister
müssen Sie die Methode self.get_service
definieren. Diese Methode initialisiert den Dienst und verbindet ihn mit dem Back-End des Brokers. Dann vergleicht es Routen in diesem Dienst mit Methoden in einer oder mehreren Dienstdefinitionen. Im folgenden Fall erstellt es beispielsweise den Dienst und bindet ihn an den Broker:
config = Hashie::Mash.new( backend: Settings.broker.backend ) service = ZSS::Service.new(:person, config)
Dann instanziiert es einen Service-Handler:
personInstance = PersonService.new
Als nächstes wird der Diensthandler an den Dienst gebunden:
service.add_route(personInstance, :get)
Schließlich muss es die Dienstinstanz zurückgeben.
return service
Jetzt gibt es nur noch einen letzten Schritt, bevor wir unseren „Person“-Dienst starten können; Wir müssen ein ausführbares Skript dafür erstellen. Wir haben bereits einen für den „HelloService“. Öffnen Sie also die Datei bin/zss-service
, ersetzen Sie „hello-word“ durch „person“ und speichern Sie die Datei. Gehen Sie zurück zur Konsole und führen Sie Folgendes aus:
$ bin/zss-service run Starting person: PID: ./log LOGS: ./log Started person daemon... 15-29-15 19:29:54 | INFO | ZSS::SERVICE - Starting SID: 'PERSON' ID: 'person#d3ca7e1f-e229-4502-ac2d-0c01d8c285f8' Env: 'development' Broker: 'tcp://127.0.0.1:7776'
Das ist es. Sie haben Ihren Dienst „Person“ gerade zum ersten Mal gestartet. Jetzt testen wir es. Öffnen Sie die Datei bin/zss-client
, ändern Sie die sid
-Variable in „person“ und ändern Sie den Client-Aufruf von hello_world()
in get()
. Führen Sie anschließend den Client in einem neuen Fenster aus:
$ bin/zss-client /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:41:in `new': No people here (ZSS::Error) from /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:41:in `call' from /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:55:in `method_missing' from bin/zss-client:12:in `<main>'
Wie Sie sehen können, haben Sie einen ZSS::Error
. Dies liegt daran, dass wir einen Fehler auslösen, wenn der Dienst keine Personen findet und wir noch keine Personen in der Datenbank unseres Dienstes haben.
Lassen Sie uns diesen Fehler dann behandeln. Öffnen zss-client
und bearbeiten Sie ihn wie folgt:
begin client = ZSS::Client.new(sid, config) p client.get() rescue ZSS::Client => e if e.code == 404 p e.message else raise e end end
Jetzt drucken wir die Fehlermeldung, wenn der Fehlercode 404 ist, während wir den Fehler auslösen, wenn es sich um einen anderen handelt. Sehen wir es uns in Aktion an, indem wir unseren Client erneut ausführen:
$ bin/zss-client "No people here"
Exzellent. Lassen Sie uns nun einige Personen zu unserer Tabelle hinzufügen und sehen, ob sie vom Dienst an unseren Kunden zurückgegeben werden. Öffnen Sie dazu einfach eine Servicekonsole:
$ rake service:console
Fügen Sie einige Personen hinzu:
$ rake service:console [1] pry(main)> DAO::Person.create name: 'John' => #<DAO::Person:0x007fe51bbe9d00 id: 1, name: "John", created_at: 2015-12-16 13:22:37 UTC, updated_at: 2015-12-16 13:22:37 UTC> [2] pry(main)> DAO::Person.create name: 'Mary' => #<DAO::Person:0x007fe51c1dafe8 id: 2, name: "Mary", created_at: 2015-12-16 13:22:42 UTC, updated_at: 2015-12-16 13:22:42 UTC> [3] pry(main)> DAO::Person.create name: 'Francis' => #<DAO::Person:0x007fe51bc11698 id: 3, name: "Francis", created_at: 2015-12-16 13:22:53 UTC, updated_at: 2015-12-16 13:22:53 UTC> [4] pry(main)> exit
Führen Sie jetzt Ihren Client erneut aus.
$ bin/zss-client [{"id"=>1, "name"=>"John"}, {"id"=>2, "name"=>"Mary"}, {"id"=>3, "name"=>"Francis"}]
Hier hast du es.
Schlussbetrachtungen
Wenn Sie den in diesem Handbuch bereitgestellten Code durchgehen, denken Sie vielleicht, dass es viele unnötige Schritte gibt, wie z. B. das Erstellen von Repositories oder DTOs, und Sie haben Recht. Alles, was Sie für einen funktionierenden Dienst „Person“ benötigen, wäre Ihre Dienstklasse und Ihr DAO, das Sie direkt aus der Dienstklasse aufrufen könnten. Dennoch empfiehlt es sich, dem in diesem Artikel beschriebenen Muster zu folgen, da es Ihnen ermöglicht, die Dienstlogik von Ihrer Datenspeichermanipulation getrennt zu halten. Dienste sollten sich nur auf ihre Logik konzentrieren, und Repositories sollten alle Interaktionen mit Ihrem Datenspeicher abwickeln. DTOs bestimmen die Nutzdaten und Serialisierung Ihrer Dienste, und DAOs kümmern sich nur darum, Daten aus dem Speicher zu erhalten. Die in diesem Handbuch beschriebenen Konventionen und Techniken werden als Repository-Muster bezeichnet, das Sie im Bild unten sehen können.
Abschließend möchte ich jeden bitten, der dies nützlich fand, zur SOA-Service-Suite beizutragen, indem er sie in irgendeiner Weise erweitert und verbessert. Alle Ihre Forks und Pull-Requests sind willkommen.
Ich hoffe, dies hilft Ihnen beim Einstieg in Microservices. Wenn Sie sich den Dienstcode ansehen möchten, steht eine vollständige Version auf GitHub zur Verfügung.