Hallo allerseits!
In diesem Artikel werden die grundlegenden Komponenten zum Erstellen von RESTful-Microservices mithilfe der Consul-Serviceregistrierung, Spring Boot für alle Gerüste, Abhängigkeitsinjektion, Maven für die Montage sowie Spring REST und die Java RESTful Jersey / JaxRS-API demonstriert.
Die Hauptvorteile von Microservices:
- Microservices helfen dabei, Ihren Code zu lockern
- Mit Microservices können verschiedene Teams mithilfe unabhängiger Technologien an kleinen Komponenten arbeiten und so eine sicherere und häufigere Bereitstellung ermöglichen. Spring Boot unterstützt verschiedene Implementierungen zum Erstellen einer REST-API
- Die Ermittlung und der Aufruf von Diensten hängen nicht von der Dienstplattform ab
- Swagger erstellt eine robuste API-Dokumentation und eine Aufrufschnittstelle
Wenn Sie Microservices noch nicht nutzen, haben Sie es nicht erfolgreich geschafft, in die Phase der frühen Follower auf der Technologie-Wahrnehmungskurve einzusteigen, und es ist wahrscheinlich genau der richtige Zeitpunkt, um zu beginnen.

In den letzten zwei Jahrzehnten ist das Unternehmen in unserem SDLC-Prozess sehr flexibel geworden, aber unsere Anwendungen bleiben in der Regel immer noch monolithisch, und riesige Jars unterstützen alle verschiedenen APIs und Versionen auf dem Markt. Gegenwärtig besteht jedoch der Wunsch nach schlankeren DevOps-Prozessen, und die Funktionalität wird „serverlos“. Das Refactoring auf Microservices kann die Verstrickung von Code und Ressourcen verringern, Assemblys verkleinern, Releases sicherer machen und APIs stabiler machen.
In diesem Artikel erstellen wir eine einfache Anwendung zur Verwaltung des Börsenportfolios, mit der Kunden ihr Aktienportfolio bewerten können (Börsenticker und Werte). Der Portfolio-Mikroservice ruft das Portfolio des Kunden ab, sendet es an den Preis-Mikroservice, um die neuesten Preise anzuwenden, und gibt dann das vollständig bewertete und subtotalisierte Portfolio zurück, um dies durch einen Restaufruf zu demonstrieren.

Bevor wir mit der Erstellung unserer Microservices beginnen, bereiten wir unsere Umgebung vor, indem wir Consul einrichten.
Konsul herunterladen
Wir werden den Hashicorp-Konsul verwenden, um Dienste zu ermitteln. Gehen Sie also zu
www.consul.io/downloads.html und laden Sie Consul für Windows, Linux, Mac usw. herunter. Dadurch erhalten Sie eine ausführbare Datei, die Sie Ihrem Pfad hinzufügen müssen.
Konsul starten
Starten Sie Consul an der Eingabeaufforderung im Entwicklungsmodus:
consul agent -dev
Um zu überprüfen, ob es ausgeführt wird, rufen Sie Ihren Browser auf und greifen Sie auf die Konsulschnittstelle
http: // localhost: 8500 zu . Wenn alles gut geht, muss der Konsul melden, dass er gesund und munter ist. Durch Klicken auf den Konsulendienst (links) erhalten Sie zusätzliche Informationen (rechts).

Wenn im Moment Probleme auftreten, stellen Sie sicher, dass Sie Consul zum Ausführungspfad hinzufügen und die Ports 8500 und 8600 verfügbar sind.
Erstellen Sie eine SpringBoot-Anwendung
Wir werden den
Spring Initializr verwenden , der in die meisten IDEs integriert ist, um unsere SpringBoot-Anwendungen zu erstellen. Die folgenden Screenshots verwenden IntelliJ IDEA.
Wählen Sie "Datei / Neues Projekt", um eine neue Projektvorlage zu öffnen, und dann "Spring Initializr".

Im Allgemeinen können Sie Gerüste ohne IDE konfigurieren, indem Sie ein Online-Formular über die SpringBoot Initializr-Webseite
start.spring.io ausfüllen , auf der eine Zip-Datei für Ihr leeres Projekt erstellt wird, die zum Download bereitsteht.
Klicken Sie auf "Weiter" und geben Sie die Projektmetadaten ein. Verwenden Sie die folgende Konfiguration:

Klicken Sie auf "Weiter", um die Abhängigkeiten auszuwählen, und geben Sie bei der Suche nach Abhängigkeiten "Jersey" und "Consul Discovery" ein. Fügen Sie diese Abhängigkeiten hinzu:

Klicken Sie auf "Weiter", um den Namen des Projekts und seinen Standort anzugeben. Behalten Sie den Standardnamen "Portfolio" bei und geben Sie den bevorzugten Speicherort des Projekts an. Klicken Sie dann auf "Fertig stellen", um das Projekt zu erstellen und zu öffnen:

Wir können die generierten application.properties verwenden, aber SpringBoot erkennt auch das YAML-Format, das etwas einfacher zu visualisieren ist. Benennen Sie es daher in application.yml um.
Wir nennen den Microservice „Portfolio-Service“. Wir können einen Port angeben oder Port 0 verwenden, damit die Anwendung einen verfügbaren Port verwendet. In unserem Fall verwenden wir 57116. Wenn Sie diesen Dienst als Docker-Container hosten, können Sie ihn jedem von Ihnen ausgewählten Port zuordnen. Benennen Sie die Anwendung und geben Sie unseren Port an, indem Sie unserer application.yml Folgendes hinzufügen:
spring: application: name: portfolio-service server: port: 57116
Fügen Sie unserer SpringBoot-Anwendungsklasse Anmerkungen hinzu, um unseren Service verfügbar zu machen. Öffnen Sie die PortfolioApplication-Anwendung und fügen Sie über der Klassendeklaration @EnableDiscoveryClient hinzu.
Bestätigen Sie die Importe. Die Klasse sollte folgendermaßen aussehen:
package com.restms.demo.portfolio; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; . . . @SpringBootApplication @EnableDiscoveryClient public class PortfolioApplication { public static void main(String[] args) { SpringApplication.run(PortfolioApplication.class, args); } }
(Um zu demonstrieren, wie Microservices aus unabhängigen Plattformen bestehen können, werden wir Jersey für diesen Service und Spring REST für den nächsten verwenden.)
Um den RESTful-Webdienst auf Jersey zu konfigurieren, müssen Sie die ResourceConfig-Konfigurationsklasse angeben. Fügen Sie die JerseyConfig-Klasse hinzu (zur Demonstration speichern wir sie im selben Paket wie unsere Anwendungsklasse). Es sollte so aussehen, plus das richtige Paket und Import:
@Configuration @ApplicationPath("portfolios") public class JerseyConfig extends ResourceConfig { public JerseyConfig() { register(PortfolioImpl.class); } }
Beachten Sie, dass es von ResourceConfig erbt, um es als Jersey-Konfigurationsklasse zu kennzeichnen. Das Attribut @ApplicationPath ("Portfolios") definiert den Kontext des Aufrufs. Dies bedeutet, dass Aufrufe mit dem Pfadelement "Portfolioios" beginnen müssen. (Wenn Sie es weglassen, ist der Standardkontext "/").
Die PortfolioImpl-Klasse bedient zwei Anforderungen: Portfolios / Kunde / {Kunden-ID} gibt alle Portfolios zurück und Portfolios / Kunde / {Kunden-ID} / Portfolio / {Portfolio-ID} gibt ein Portfolio zurück. Ein Portfolio besteht aus einer Reihe von Tickern und der Anzahl der von diesem Ticker gehaltenen Aktien. (Zur Demonstration gibt es drei Clients mit den Kennungen 0, 1 und 2, von denen jeder drei Portfolios mit den Kennungen 0, 1 und 2 hat.)
Ihre IDE fordert Sie auf, PortfolioImpl zu erstellen. Mach es jetzt. Fügen Sie es zur Demonstration demselben Paket hinzu. Geben Sie den folgenden Code ein und bestätigen Sie alle Importe:
@Component @Path("/") public class PortfolioImpl implements InitializingBean { private Object[][][][] clientPortfolios; @GET @Path("customer/{customer-id}") @Produces(MediaType.APPLICATION_JSON) // a portfolio consists of an array of arrays, each containing an array of // stock ticker and associated shares public Object[][][] getPortfolios(@PathParam("customer-id") int customerId) { return clientPortfolios[customerId]; } @GET @Path("customer/{customer-id}/portfolio/{portfolio-id}") @Produces(MediaType.APPLICATION_JSON) public Object[][] getPortfolio(@PathParam("customer-id") int customerId, @PathParam("portfolio-id") int portfolioId) { return getPortfolios(customerId)[portfolioId]; } @Override public void afterPropertiesSet() throws Exception { Object[][][][] clientPortfolios = { { // 3 customers, 3 portfolios each {new Object[]{"JPM", 10201}, new Object[]{"GE", 20400}, new Object[]{"UTX", 38892}}, {new Object[]{"KO", 12449}, new Object[]{"JPM", 23454}, new Object[]{"MRK", 45344}}, {new Object[]{"WMT", 39583}, new Object[]{"DIS", 95867}, new Object[]{"TRV", 384756}}, }, { {new Object[]{"GE", 38475}, new Object[]{"MCD", 12395}, new Object[]{"IBM", 91234}}, {new Object[]{"VZ", 22342}, new Object[]{"AXP", 385432}, new Object[]{"UTX", 23432}}, {new Object[]{"IBM", 18343}, new Object[]{"DIS", 45673}, new Object[]{"AAPL", 23456}}, }, { {new Object[]{"AXP", 34543}, new Object[]{"TRV", 55322}, new Object[]{"NKE", 45642}}, {new Object[]{"CVX", 44332}, new Object[]{"JPM", 12453}, new Object[]{"JNJ", 45433}}, {new Object[]{"MRK", 32346}, new Object[]{"UTX", 46532}, new Object[]{"TRV", 45663}}, } }; this.clientPortfolios = clientPortfolios; } }
Die
Komponentenanmerkung bezeichnet dies als Klasse der Spring-Komponente und macht sie als Endpunkt verfügbar. Pfadanmerkungen zur Klassendeklaration geben an, dass auf die Klasse über das Pfadelement "/" zugegriffen wird. Zwei unterstützte API-Aufrufe sind über Portfolios / Kunde / {Kunden-ID} und Portfolios / Kunde / {Kunden-ID} / Portfolio / {Portfolio- verfügbar. id}, wie wir aus den Anmerkungen der Methode sehen. Beachten Sie, dass der Pfad ("/") die Standardeinstellung ist, wir ihn jedoch als Referenz belassen. Methoden werden über @GETannotation als HTTP GET bezeichnet. Unsere Methode wurde entwickelt, um ein Array zurückzugeben, und mit Anmerkungen versehen, um Json zurückzugeben, sodass ein Json-Array zurückgegeben wird. Beachten Sie, wie Pfadparameteranmerkungen in der Methodensignatur verwendet werden, um die angezeigten Parameter aus den angezeigten Abfragen zu extrahieren.
(Für unsere Demo geben wir fest codierte Werte zurück. In der Praxis fragt die Implementierung natürlich die Datenbank oder einen anderen Dienst oder eine andere Datenquelle anstelle des feststehenden Codes ab.)
Erstellen Sie nun ein Projekt und führen Sie es aus. Wenn Sie IntelliJ verwenden, wird eine ausführbare Standarddatei erstellt. Klicken Sie einfach auf den grünen Pfeil "Ausführen". Sie können auch verwenden
mvn spring-boot:run
Oder Sie können die Maven-Installation durchführen und die Anwendung mit Java-Jar ausführen, wobei Sie auf das generierte JAR im Zielverzeichnis verweisen:
java -jar target\portfolio-0.0.1-SNAPSHOT.jar
Jetzt sollte dieser Dienst in Consul angezeigt werden.
Kehren wir also zu unserem Browser zurück und laden Sie
http: // localhost: 8500 / ui / # / dc1 / services herunter (oder aktualisieren Sie ihn, wenn Sie bereits dort sind).

Hmm, wir sehen unseren Service dort, aber er wird als fehlgeschlagen angezeigt. Dies liegt daran, dass Consul von unserem Service ein „gesundes“ Herzschlagsignal erwartet.
Um Heartbeat-Signale zu generieren, können wir dem POM unserer Anwendung eine Abhängigkeit vom
Spring Actuator- Dienst hinzufügen.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Während wir in Pom sind, beachten Sie, dass es einen Versionskonflikt mit Jersey zwischen dem Consul-Starter und dem Jersey-Starter gibt. Um dies auszugleichen, bezeichnen Sie den Jersey-Starter als erste Sucht.
Ihr Pom sollte jetzt die folgenden Abhängigkeiten enthalten:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
Durch den Neustart von Consul zeigt der Portfolio-Service ein zufriedenes an:

Jetzt gibt es im Portfolio-Service zwei Übertragungsknoten: Einer davon ist die Implementierung des Portfolio-Service und der andere ist der Herzschlag.
Lassen Sie uns den zugewiesenen Port überprüfen. Sie können das in der Ausgabe der Anwendung sehen:
INFO 19792 --- [ main] sbcetTomcatEmbeddedServletContainer : Tomcat started on port(s): 57116 (http)
Sie können den Port auch direkt in der Consul-Benutzeroberfläche anzeigen. Klicken Sie auf "Kundenservice" und wählen Sie dann den Link "Service 'Kundenservice' Check Link", der den Service-Port anzeigt, in diesem Fall 57116.
Wenn Sie http: // localhost: 57116 / portfolios / customer / 1 / Portfolio / 2 anfordern, wird das JSON-Array [["IBM", 18343], ["DIS", 45673], ["AAPL", 23456]] angezeigt.
Unser erster Microservice ist geöffnet!
Preisservice
Als Nächstes erstellen wir unseren Preisservice, diesmal mit Spring RestController anstelle von Jersey.
Der Preisservice akzeptiert die Kunden- und Portfolio-ID als Parameter und verwendet RestTemplate, um Portfolio-Services anzufordern, Ticker und Aktien zu erhalten und aktuelle Preise zurückzugeben. (Ich muss Ihnen nicht sagen, dass diese Werte gefälschte Nachrichten sind, verwenden Sie sie also nicht, um Handelsentscheidungen zu treffen!)
Erstellen Sie ein neues Projekt mit den folgenden Informationen:

Wählen Sie dieses Mal die Abhängigkeiten Web, Consul Discovery und Actuator aus:

Behalten Sie den Standardnamen des Projekts "Pricing" bei und erstellen Sie ein Projekt im Verzeichnis Ihrer Wahl.
Dieses Mal verwenden wir application.properties anstelle von application.yml.
Legen Sie den Namen und den Port in application.properties wie folgt fest:
spring.application.name=pricing server.port=57216
Kommentieren Sie die Preisanwendung mit @EnableDiscoveryClient. Die Klasse sollte so aussehen, plus Paket und Import.
@SpringBootApplication @EnableDiscoveryClient public class PricingApplication { public static void main(String[] args) { SpringApplication.run(PricingApplication.class, args); } }
Dann erstellen wir die PricingEndpoint-Klasse. Hier werde ich ein detaillierteres Beispiel geben, da es einige wichtige Funktionen demonstriert, einschließlich der Serviceerkennung (Suche nach Portfolio-Service) und der Verwendung von RestTemplate für die Abfrage:
@RestController @RequestMapping("/pricing") public class PricingEndpoint implements InitializingBean { @Autowired DiscoveryClient client; Map<String, Double> pricingMap = new HashMap<>(); RestTemplate restTemplate = new RestTemplate(); @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio( @PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId) { List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream() .findFirst() .orElseThrow(() -> new RuntimeException("not found")); String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId); // query for the portfolios, returned as an array of List // of size 2, containing a ticker and a position (
Um einen Portfolio-Service zu finden, benötigen wir Zugriff auf DiscoveryClient. Mit der @ Autowired-Annotation von Spring ist dies leicht zu erreichen.
@Autowired DiscoveryClient client;
Diese DiscoveryClient-Instanz wird dann verwendet, um im Aufruf nach dem Dienst zu suchen:
List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream().findFirst().orElseThrow(() -> new RuntimeException("not found"));
Nachdem der Service gefunden wurde, können wir ihn verwenden, um unsere Anfrage zu erfüllen, die wir gemäß dem in unserem Portfolio-Service erstellten API-Aufruf erstellen.
String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId);
Schließlich verwenden wir RestTemplate, um unsere GET-Anforderung auszuführen.
Object[] portfolio = restTemplate.getForObject(url, Object[].class);
Beachten Sie, dass für Rest-Controller (sowie für Spring MVC Request Controller) Pfadvariablen mithilfe der Annotation "Pfadvariable" abgerufen werden, im Gegensatz zu Jersey, das, wie wir gesehen haben,
Path Param verwendet.
Damit ist unsere Preisgestaltung mit Spring RestController abgeschlossen.
Die Dokumentation
Wir haben all diese Probleme gelöst, um unsere Microservices zu erstellen, aber sie werden nicht genug Nutzen bringen, wenn wir der Welt kein Wissen darüber geben, wie man sie verwendet.
Zu diesem Zweck verwenden wir das praktische und benutzerfreundliche
Swagger- Tool, das nicht nur unsere API-Aufrufe dokumentiert, sondern auch einen praktischen Webclient zum Aufrufen bereitstellt.
Lassen Sie uns zuerst Swagger in unserem Pom angeben:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
Dann müssen wir Swagger mitteilen, welche unserer Klassen wir dokumentieren möchten. Lassen Sie uns die neue SwaggerConfig-Klasse vorstellen, die die Swagger-Spezifikation enthält.
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.regex("/pricing.*")) .build(); } }
Mal sehen, was diese Klasse macht. Zuerst haben wir dies als Swagger-Konfiguration mit der Annotation @ EnableSwagger2 bezeichnet.
Dann haben wir eine Docket-Komponente erstellt, die Swagger mitteilt, welche APIs angezeigt werden sollen. Im obigen Beispiel haben wir Swagger angewiesen, jeden Pfad zu demonstrieren, der mit "/ price" beginnt. Eine Alternative wäre, Klassen für die Dokumentation und nicht für Pfade anzugeben:
.apis(RequestHandlerSelectors.basePackage("com.restms.demo")) .paths(PathSelectors.any())
Starten Sie den Preis-Microservice neu und rufen
Sie im Browser
http: // localhost: 57216 / swagger-ui.html auf
Klicken Sie auf Vorgänge auflisten, um die Vorgänge im Detail anzuzeigen.
Klicken Sie auf Vorgänge erweitern, um eine Anforderung basierend auf dem Formular zu erstellen. Stellen Sie einige Parameter ein und klicken Sie auf "Probieren Sie es aus!" und warte auf die Antwort:

Sie können viel mehr Farben hinzufügen, indem Sie Ihren Methoden Swagger-Anmerkungen hinzufügen.
Dekorieren Sie beispielsweise die vorhandene PricingImpl.getPricedPortfolio-Methode mithilfe der Annotation @ApiOperation, wie unten gezeigt:
@ApiOperation(value = "Retrieves a fully priced portfolio", notes = "Retrieves fully priced portfolio given customer id and portfolio id") @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio(@PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId)
Laden Sie swagger-ui neu und aktualisieren Sie es, um die neue aktualisierte Dokumentation anzuzeigen:

Und das ist noch nicht alles, was Sie mit Swagger tun können. Lesen Sie also die Dokumentation.
Yuri Dvorzhetsky , Dozent in unserem Kurs
"Developer on the Spring Framework", wird Ihnen mehr über Spring Boot erzählen:
Originalartikel