[Aktualisiert am 05. Mai 2019 um 18:00 Uhr] In letzter Zeit scheint gRPC für die Kommunikation zwischen Knoten auf verteilten Systemen beliebter zu sein als REST, daher habe ich es versucht. Darüber hinaus wird gRPC als Bibliothek in Java bereitgestellt, und im Frühjahr wird ein Starter zum Booten von LogNet bereitgestellt. Es scheint, dass es gibt, also verwenden Sie es auf Spring Boot. Außerdem gehe ich davon aus, dass es auf einem verteilten System verwendet wird, wenn es in Zukunft in die Praxis umgesetzt werden soll. Deshalb habe ich beschlossen, es in das Spring Cloud-Projekt aufzunehmen und auszuführen.
Überraschenderweise gab es einige süchtig machende Punkte, bevor ich sie gerade verschoben habe. Dieses Mal werde ich diese Punkte teilen und beschreiben, was ich bis zu dem Punkt bestätigt habe, an dem das Projekt normal funktioniert.
Ich kann erwähnen, was in Spring Cloud separat implementiert ist, aber ich habe im folgenden Projekt die gRPC-Kommunikation für den Lastausgleich durch Eureka und Ribbon versucht.
Wenn der Benutzer eine HTTP-Anfrage an die oben genannte "Eureka-lb-Demo" sendet, API an "Eureka-lb-Demo" → "Eureka-Client-Demo" durch gRPC-Kommunikation. In Form eines Anrufs. Da der Projektname gemäß der Struktur von Eureka angegeben wird, ist das Gegenteil der Fall, jedoch als gRPC
Ist die Beziehung.
eureka-demo
, config-server-demo
Hat nichts mit diesem Thema zu tun. Der Teil, der sich auf das Thema bezieht, ist nur der weiße Teil.
Es ist nicht die Essenz dieser Zeit, aber ich werde es kurz erklären, damit Sie nicht den Überblick verlieren, was Sie tun, wenn Sie sich das Code-Snippet ansehen. Dieses Mal haben wir einen Dienst implementiert, mit dem Sie Geräteinformationen abrufen können, indem Sie den Schulnamen als Parameter übergeben. Wenn die Anfrage von "eureka-lb-demo" empfangen wird, fragt sie intern die API von "eureka-client-demo" ab, die "getEquipmentInfo" genannt wird. Daher heißt die Schnittstellendefinition dieses Mal "EquipmentService".
Bevor ich zur Implementierung übergehe, werde ich kurz die Annahmen von gRPC erläutern.
gRPC definiert eine Datei mit dem Namen `.proto``` und kompiliert sie mit einem Compiler namens protoc. Die Quelle des API-Teils wird automatisch generiert. Gleiches Gefühl wie SOAP. Erstellen Sie nach dem Erstellen dieser Datei einen Klassenpfad von
`src / main / proto``` und erstellen Sie ihn.
Wenn Sie in maven ein Plugin definieren, können Sie dem Build-Pfad ohne Erlaubnis eine automatisch generierte Quelle hinzufügen, indem Sie wie gewohnt erstellen.
Der Grund für den Namen ** proto ** ist, dass er eine Technologie namens Protokollpuffer verwendet, aber die Erklärung wird weggelassen, weil sie gegen den Zweck dieser Zeit verstößt.
Die Protodatei wurde so erstellt. Ich möchte das Objekt als Listentyp zurückgeben, wenn der Name der Parameterschule in der Datenbank vorhanden ist. Wenn es nicht existiert, wird nur die Nachricht zurückgegeben. Daher definiert EquipmentResponse die Nachricht als erstes Feld und die Liste der Java-Objekte als zweites Feld.
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.example.ek.eureka_client_demo.equipment";
package equipment_proto;
service EquipmentService {
rpc getEquipment (EquipmentRequest) returns (EquipmentResponse);
}
message EquipmentRequest{
string schoolName = 1;
}
message EquipmentResponse{
string message = 1;
repeated EquipmentInfo equipmentInfo = 2;
}
message EquipmentInfo{
string catagory = 1;
string name = 2;
}
Die im EquipmentService definierte getEquipment ist die API zum Abrufen von Geräteinformationen.
Unten finden Sie die detaillierte Grammatik von Proto. Protocol Buffers Language Guide (proto3)
<!-- gRPC server -->
<dependency>
<groupId>io.github.lognet</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
</dependency>
Da der oben beschriebene Prototyp verwendet werden muss, fügen Sie als Nächstes Folgendes unter dem Tag `<build>`
hinzu. Bitte beachten Sie auch den Referenzartikel [^ 1] für diesen Bereich.
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
//Abkürzung
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.18.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
Zunächst ist hier ein Fehler aufgetreten. Unter meinem MacOS wurde die Fehlermeldung angezeigt, dass `$ {os.detected.classifier}`
nicht identifiziert werden konnte. Daher habe ich die Eigenschaft angegeben und übertroffen. Es scheint, dass Profileinstellungen mit `` `settings.xml``` verwendet werden können, aber ich gebe Eigenschaften für dieselbe POM-Datei an. Bitte beachten Sie, dass es in Windows möglich war, in den Status von Variablen zu wechseln. Es scheint also, dass dies je nach Betriebssystem möglich oder nicht möglich ist.
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RC2</spring-cloud.version>
<os.detected.classifier>osx-x86_64</os.detected.classifier>
</properties>
Wenn Sie in diesem Status erstellen, wird der Quellcode für gRPC automatisch generiert. Implementieren Sie einen Controller, der Anforderungen von `` `eureka-lb-demo``` (clientseitig) unter Verwendung des automatisch generierten Codes akzeptiert. Der erstellte Controller lautet wie folgt.
package com.example.ek.eureka_client_demo.equipment;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections.CollectionUtils;
import org.lognet.springboot.grpc.GRpcService;
import org.springframework.stereotype.Controller;
import com.example.ek.eureka_client_demo.equipment.EquipmentServiceGrpc.EquipmentServiceImplBase;
import io.grpc.stub.StreamObserver;
@GRpcService // (1)
@Controller
public class EquipmentController extends EquipmentServiceImplBase { // (2)
private static Map<String, List<Equipment>> schoolEquipmentDB; // (3)
@Override
public void getEquipment(EquipmentRequest req, StreamObserver<EquipmentResponse> resObserber) { // (4)
String schoolNm = req.getSchoolName(); // (5)
List<Equipment> list = schoolEquipmentDB.get(schoolNm);
EquipmentResponse equipmentResponsse = null;
if (CollectionUtils.isEmpty(list)) {
equipmentResponsse = EquipmentResponse.newBuilder()
.setMessage(
"There is no equipment in this scool " + schoolNm + System.getProperty("line.separator"))
.build(); // (6)
resObserber.onNext(equipmentResponsse); // (8)
}
//Wann können Geräteinformationen abgerufen werden
else {
EquipmentResponse.Builder responseBuilder = EquipmentResponse.newBuilder();
for (Equipment eq : list) {
EquipmentInfo eqInfo = EquipmentInfo.newBuilder()
.setCatagory(eq.getCategory())
.setName(eq.getName())
.build(); // (7)
responseBuilder.addEquipmentInfo(eqInfo);
}
EquipmentResponse res = responseBuilder.setMessage("").build();
resObserber.onNext(res); //(8)
}
resObserber.onCompleted(); // (9)
}
// (3)
static {
schoolEquipmentDB = new HashMap<String, List<Equipment>>();
List<Equipment> lst = new ArrayList<Equipment>();
Equipment eqp = new Equipment("chair", "High Grade Chair I");
lst.add(eqp);
eqp = new Equipment("desk", "High Grade Desk I");
lst.add(eqp);
schoolEquipmentDB.put("abcschool", lst);
lst = new ArrayList<Equipment>();
eqp = new Equipment("chair", "Low Grade Chair I");
lst.add(eqp);
eqp = new Equipment("desk", "Low Grade Desk I");
lst.add(eqp);
schoolEquipmentDB.put("xyzschool", lst);
//Überprüfen Sie in der DB
System.out.println("School Equipment information are as below.");
for (Entry<String, List<Equipment>> entry : schoolEquipmentDB.entrySet()) {
System.out.println("School: " + entry.getKey());
for (Equipment e : entry.getValue()) {
System.out.println(" Equipment:" + e.getCategory() + "," + e.getName());
}
}
}
No. | Erläuterung |
---|---|
(1) | Fügen Sie diese Anmerkung hinzu, wenn Sie gRPC verwenden. |
(2) | Automatisch erzeugtEquipmentServiceImplBase Erben. Es gibt einige automatisch generierte Dateien,.Von ptoro angegebener Dienstname+ ImplBase Ist. |
(3) | Da das Erstellen einer Datenbank mühsam war, wurde ein DB-Mock erstellt, der nur einen Wert in eine mit static erstellte Variable einfügt. |
(4) | ich auch.Von proto angegebener Methodenname Überschreiben. |
(5) | .proto Wie in der Anfrage definiertschoolName Da ich den Parameter definiert habe, holen Sie ihn aus dem Anforderungsobjekt und den Wert aus dem DB-Mock. Darunter befindet sich die Quelle für die Rückgabe einer Antwort. |
(6) | Die Antwort legt auch den Wert der automatisch generierten Bean mit dem Builder-Muster fest. Hier wird nur eine Meldung gesetzt, wenn die Geräteinformationen nicht abgerufen werden können. |
(7) | Dies ist ein Zweig, wenn ein Wert genommen wird, keine Nachricht, sondern ein Objekt von ListenelementenEquipmentInfo (Dies ist auch eine automatisch generierte Quelle)Stellen Sie den Wert auf ein. Wie das Antwortobjekt wird auch hier ein Objekt mit dem Builder-Muster erstellt. |
(8) | Es ist ein Argument der MethodeStreamObserver vononNext Senden Sie einen Wert an die Clientseite, indem Sie im Methodenargument ein Antwortobjekt festlegen. |
(9) | Zum Schluss die Kommunikation beendenonComplete Rufen Sie die Methode auf. |
Auch Eigenschafteneinstellungen für gRPC
application.yml
## grpc settings
grpc:
port: 6565
Versuchen Sie dann, mit `` `$ mvn clean install spring-boot: run``` zu erstellen und zu booten. Ich habe den folgenden Fehler erhalten.
***************************
APPLICATION FAILED TO START
***************************
Description:
An attempt was made to call the method com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;CLjava/lang/Object;)
V but it does not exist. Its class, com.google.common.base.Preconditions, is available from the following locations:
jar:file:/Users/***/.m2/repository/com/google/guava/guava/16.0/guava-16.0.jar!/com/google/common/base/Preconditions.class
Als ich es nachgeschlagen habe, gab es ein Git-Problem, dass die Guavenbibliothek alt war und aktualisiert werden sollte. Als ich also nach der Quelle der Guave mit `` `$ mvn dependency: tree``` gesucht habe
[INFO] | +- com.netflix.eureka:eureka-client:jar:1.9.8:compile
[INFO] | | +- org.codehaus.jettison:jettison:jar:1.3.7:runtime
[INFO] | | | \- stax:stax-api:jar:1.0.1:runtime
[INFO] | | +- com.netflix.netflix-commons:netflix-eventbus:jar:0.3.0:runtime
[INFO] | | | +- com.netflix.netflix-commons:netflix-infix:jar:0.3.0:runtime
[INFO] | | | | +- commons-jxpath:commons-jxpath:jar:1.3:runtime
[INFO] | | | | +- joda-time:joda-time:jar:2.10.1:runtime
[INFO] | | | | \- org.antlr:antlr-runtime:jar:3.4:runtime
[INFO] | | | | +- org.antlr:stringtemplate:jar:3.2.1:runtime
[INFO] | | | | \- antlr:antlr:jar:2.7.7:runtime
[INFO] | | | \- org.apache.commons:commons-math:jar:2.2:runtime
[INFO] | | +- com.netflix.archaius:archaius-core:jar:0.7.6:compile
[INFO] | | | \- com.google.guava:guava:jar:16.0:compile
Es scheint, dass das Glas von eureka-client und der Boot-Starter von grpc nicht übereinstimmen. Das JAR für Eureka-Client wird wie folgt ausgeschlossen und neu erstellt und ausgeführt.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
Spring Cloud-Projekte enthalten grundsätzlich ein Guavenglas. Wenn Sie also gRPC verwenden möchten, müssen Sie die Version richtig anpassen. Ich habe auch Konfigurationsserver und Hystrix zur Abhängigkeit hinzugefügt, und verschiedene Versionen von Guave waren in jeder enthalten, daher habe ich dort auch Ausschlusseinstellungen hinzugefügt.
Das Build-Ergebnis war erfolgreich.
Started EurekaClientDemoApplication in 15.476 seconds (JVM running for 56.095)
o.l.springboot.grpc.GRpcServerRunner : gRPC Server started, listening on port 6565.
Wie auf der Serverseite schließt pom Guave aus und fügt nur den Boot-Starter von gRPC hinzu, sodass ich ihn weglassen werde. Außerdem ist die `` `.proto```-Datei genau die gleiche wie die auf der Serverseite, und der Build → -Quellcode wird automatisch generiert, was nicht nur für die Client-Seite gilt. Da der auf der Clientseite eindeutige Inhalt nur die aufrufende Methode (Java-Code) ist, wird nur der Controller beschrieben.
@RestController
public class SchoolController {
@Autowired
EurekaClient client; // (1)
//Kürzung
@GetMapping(value = "/getEquipmentInfo/{schoolname}")
public String getEquipments(@PathVariable String schoolname) {
System.out.println("Getting School details for " + schoolname);
InstanceInfo instance = client.getNextServerFromEureka("studentService", false);
ManagedChannel channel = ManagedChannelBuilder.forAddress(instance.getIPAddr(), 6565)
.usePlaintext().build(); // (2)
EquipmentServiceBlockingStub stb = EquipmentServiceGrpc.newBlockingStub(channel);
EquipmentRequest req = EquipmentRequest.newBuilder().setSchoolName(schoolname).build();
EquipmentResponse res = stb.getEquipment(req); // (3)
if (StringUtils.hasLength(res.getMessage())) {
return res.getMessage();
}
StringBuilder sb = new StringBuilder();
for (EquipmentInfo eq : res.getEquipmentInfoList()) {
sb.append(editResponseForEquipment(eq));
sb.append(System.getProperty("line.separator"));
}
return sb.toString();
}
private String editResponseForEquipment(EquipmentInfo eq) {
return "Category: " + eq.getCatagory() + ", " + "EquipmentName: " + eq.getName() + ";";
}
No. | Erläuterung |
---|---|
(1) | eureka-client-demo Um Informationen nebenbei zu bekommenEurekaClient DI. |
(2) | So verwenden Sie gRPCMannagedChannel Benutzen. Zur KanalerzeugungManagedChannelBuilder.forAddress Zueureka-client-demo Übergeben Sie die IP und den Port von. Ich eureka-client-Ich habe den Port fest codiert, weil ich einen REST-Port für die Kommunikation mit der Demo habe, aber wenn ich nur gRPC verwendeinstance.getPort Es ist okay. |
(3) | Dies ist eine automatisch generierte Stub-Klasse, wie diese Bibliothek geschrieben wirdEquipmentServiceBlockingStub Kommunizieren Sie mit. Nach dem Einstellen der Parameter in der Anfrage der Reststb.getEquipment Im Argument der Methode wird eine Anfrage gesetzt und eine Antwort empfangen. |
Wenn Sie dagegen eine HTTP-Anfrage senden, können Sie das Ergebnis wie folgt sicher abrufen.
$ curl http://localhost:8086/getEquipmentInfo/abcschool
Category: chair, EquipmentName: High Grade Chair I;
Category: desk, EquipmentName: High Grade Desk I;
Wenn ein Schulname angegeben wird, der in der Datenbank nicht vorhanden ist
$ curl http://localhost:8086/getEquipmentInfo/defschool
There is no equipment in this school defschool
Daher ist ersichtlich, dass sowohl der Listentyp von Objekten als auch die Nachricht von String wie beabsichtigt kommunizieren können.
Die Quelle ist wie folgt, obwohl verschiedene andere Inhalte gemischt sind. Quellcode
Alle Eigenschaftseinstellungswerte, die nicht mit gRPC zusammenhängen, werden in `` config-server-demo``` zusammengefasst, und jedes Projekt hat
`src / main / resource / bootstrap.yml``` in config-server. Beim Surfen, weil es zum Einstellungswert für das Projekt gehen soll (config) Bitte seien Sie vorsichtig.
Recommended Posts