[Mis à jour à 18h00, le 5 mai 2019] Récemment, gRPC semble être plus populaire que REST pour la communication inter-nœuds sur les systèmes distribués, alors je l'ai essayé. De plus, gRPC est fourni en tant que bibliothèque en java, et dans Spring, un démarreur pour le démarrage est fourni à partir de LogNet. Il semble que ce soit le cas, alors utilisez-le sur Spring Boot. De plus, je suppose qu'il sera utilisé sur un système distribué s'il doit être mis en pratique à l'avenir, j'ai donc décidé de l'intégrer dans le projet Spring Cloud et de l'exécuter.
Étonnamment, il y avait des points addictifs avant de les déplacer, donc cette fois je vais partager ces points et décrire ce que j'ai confirmé au point où le projet fonctionne normalement.
Je peux mentionner ce qui est implémenté dans Spring Cloud séparément, mais j'ai essayé la communication gRPC dans le projet suivant pour l'équilibrage de charge par Eureka et Ribbon.
Lorsque l'utilisateur envoie une requête HTTP à l'API ci-dessus eureka-lb-demo
, API à eureka-lb-demo
→ eureka-client-demo
Sous la forme d'aller appeler. Puisque le nom du projet est donné selon la structure d'Eureka, ce sera le contraire, mais comme gRPC
eureka-client-demo
eureka-lb-demo
Est la relation.
eureka-demo
, config-server-demo
N'a rien à voir avec ce thème. La partie liée au thème n'est que la partie blanche.
Ce n'est pas l'essence de cette époque, mais je vais l'expliquer brièvement afin que vous ne perdiez pas de vue ce que vous faites lorsque vous regardez l'extrait de code.
Cette fois, nous avons mis en place un service qui vous permet d'obtenir des informations sur l'équipement en passant le nom de l'école en paramètre. Lorsque la requête est reçue par eureka-lb-demo
, il interroge en interne l'API de eureka-client-demo
appelée
getEquipmentInfo. Par conséquent, la définition de l'interface est cette fois nommée
EquipmentService```.
Avant de passer à la mise en œuvre, je vais expliquer brièvement les hypothèses de gRPC.
gRPC définit un fichier appelé .proto``` et le compile à l'aide d'un compilateur appelé protoc, et la source de la partie API est automatiquement générée. Même sensation que SOAP. Après avoir créé ce fichier, créez un chemin de classe de
`` src / main / proto``` et construisez-le.
Dans maven, si vous définissez un plugin, vous pouvez ajouter une source générée automatiquement sur le chemin de construction sans autorisation simplement en construisant comme d'habitude.
La raison du nom ** proto ** est qu'il utilise une technologie appelée Protocol Buffers, mais l'explication est omise car elle va à l'encontre de l'objectif de cette fois.
Le fichier proto a été créé comme ceci. Ce que je veux faire est de renvoyer l'objet en tant que type de liste si le nom de l'école de paramètre existe dans la base de données. S'il n'existe pas, seul le message est renvoyé. Par conséquent, EquipmentResponse définit le message comme le premier champ et la liste des objets Java comme le deuxième champ.
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;
}
Le getEquipment défini dans EquipmentService est l'API permettant d'acquérir des informations sur l'équipement.
Voir ci-dessous pour la grammaire détaillée de proto. Protocol Buffers Language Guide (proto3)
eureka-client-demo
)<!-- gRPC server -->
<dependency>
<groupId>io.github.lognet</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
</dependency>
Ensuite, puisqu'il est nécessaire d'utiliser le prototype décrit ci-dessus, ajoutez ce qui suit sous la balise ``
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
//Abréviation
<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>
Tout d'abord, une erreur s'est produite ici. J'ai eu une erreur indiquant que $ {os.detected.classifier} '' ne pouvait pas être identifié sur mon MacOS, j'ai donc spécifié une propriété pour la surmonter. Il semble que les paramètres de profil puissent être utilisés avec
`` settings.xml```, mais je spécifie les propriétés sur le même fichier pom. Veuillez noter que dans Windows, il était possible de passer à l'état des variables, il semble donc que cela puisse ou non être possible selon le système d'exploitation.
<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>
Ensuite, si vous créez dans cet état, le code source de gRPC sera automatiquement généré.
Implémentez un Controller qui accepte les requêtes de eureka-lb-demo
(côté client) en utilisant le code généré automatiquement.
Le contrôleur créé est le suivant.
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)
}
//Quand des informations sur l'équipement peuvent être obtenues
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);
//Vérifiez à l'intérieur de la base de données
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. | La description |
---|---|
(1) | Ajoutez cette annotation lors de l'utilisation de gRPC. |
(2) | Généré automatiquementEquipmentServiceImplBase Hériter. Il y a des fichiers générés automatiquement,.Nom du service spécifié par ptoro+ ImplBase Est. |
(3) | Comme il était difficile de créer une base de données, une maquette de base de données qui met simplement une valeur dans une variable créée avec static. |
(4) | moi aussi.Nom de méthode spécifié par proto Passer outre. |
(5) | .proto Tel que défini dans, dans la demandeschoolName Depuis que j'ai défini le paramètre, récupérez-le à partir de l'objet de requête et obtenez la valeur de la maquette DB. En dessous se trouve la source pour renvoyer une réponse. |
(6) | La réponse définit également la valeur du bean généré automatiquement avec le modèle de générateur. Ici, seul un message est défini lorsque les informations sur l'équipement ne peuvent pas être obtenues. |
(7) | Il s'agit d'une branche lorsqu'une valeur est prise, pas un message, mais un objet d'éléments de listeEquipmentInfo (C'est aussi une source générée automatiquement)Définissez la valeur sur. Comme l'objet de réponse, cela crée également un objet avec le modèle de générateur. |
(8) | C'est un argument de la méthodeStreamObserver deonNext Envoyez une valeur côté client en définissant un objet de réponse dans l'argument de méthode. |
(9) | Enfin pour terminer la communicationonComplete Appelez la méthode. |
En outre, les paramètres de propriété pour gRPC
application.yml
## grpc settings
grpc:
port: 6565
Ensuite, essayez de construire et de démarrer avec
$ mvn clean install spring-boot: run```. J'ai eu l'erreur suivante.
***************************
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
Quand je l'ai recherché, il y avait un problème Git selon lequel la bibliothèque de goyave était ancienne et devait être mise à jour, alors quand j'ai recherché la source de goyave avec
$ mvn dependency: tree```
[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
Il semble que le pot de eureka-client et le boot-starter de grpc ne correspondent pas. Le pot pour eureka-client est exclu comme suit et reconstruit et exécuté.
<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>
Les projets Spring Cloud incluent essentiellement un pot de goyave, donc si vous souhaitez utiliser gRPC, vous devez faire correspondre correctement la version. J'ai également ajouté le serveur de configuration et l'hystrix à la dépendance, et chacun avait une version différente de goyave, donc j'ai également ajouté des paramètres d'exclusion.
Le résultat de la construction a réussi.
Started EurekaClientDemoApplication in 15.476 seconds (JVM running for 56.095)
o.l.springboot.grpc.GRpcServerRunner : gRPC Server started, listening on port 6565.
eureka-lb-demo
)Comme le côté serveur, pom exclut la goyave et ajoute simplement le démarreur de démarrage de gRPC, donc je vais l'omettre. De plus, le fichier `` .proto '' est exactement le même que celui du côté serveur, et le code source build → est automatiquement généré, ce qui n'est pas unique du côté client. Puisque le contenu unique côté client n'est que la méthode d'appel (code java), seul le contrôleur est décrit.
@RestController
public class SchoolController {
@Autowired
EurekaClient client; // (1)
//réduction
@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. | La description |
---|---|
(1) | eureka-client-demo Pour obtenir des informations sur le côtéEurekaClient DI. |
(2) | Pour utiliser gRPCMannagedChannel Utiliser. Pour la génération de canauxManagedChannelBuilder.forAddress Àeureka-client-demo Passez l'adresse IP et le port de. Je eureka-client-J'ai codé en dur le port car j'ai un port REST pour la communication avec la démo, mais si j'utilise uniquement gRPCinstance.getPort Est correct. |
(3) | Il s'agit d'une classe stub générée automatiquement comme la façon dont cette bibliothèque est écriteEquipmentServiceBlockingStub Communiquez en utilisant. Après avoir défini les paramètres dans la requête, le restestb.getEquipment Une requête est définie dans l'argument de la méthode et une réponse est reçue. |
En revanche, si vous envoyez une requête HTTP, vous pouvez obtenir le résultat en toute sécurité comme suit.
$ curl http://localhost:8086/getEquipmentInfo/abcschool
Category: chair, EquipmentName: High Grade Chair I;
Category: desk, EquipmentName: High Grade Desk I;
Lorsqu'un nom d'école qui n'existe pas dans la base de données est spécifié
$ curl http://localhost:8086/getEquipmentInfo/defschool
There is no equipment in this school defschool
Par conséquent, on peut voir que le type de liste d'objets et le message de String peuvent communiquer comme prévu.
La source est la suivante, bien que divers autres contenus soient mélangés. Code source
Toutes les valeurs de paramétrage de propriété non liées à gRPC sont agrégées dans config-server-demo '', et chaque projet a
src / main / resource / bootstrap.yml '' dans config-server. Lors de la navigation car il est censé aller à la valeur de réglage du projet (config) S'il vous plaît soyez prudente.
Recommended Posts