[JAVA] Essayez gRPC dans le projet Spring Boot et Spring Cloud (Mac OS)

[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.

environnement

Structure du projet

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-demoeureka-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

Est la relation.

eureka-demo, config-server-demoN'a rien à voir avec ce thème. La partie liée au thème n'est que la partie blanche.

プロジェクト構成2

Contenu du service

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```.

supposition

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.

la mise en oeuvre

Commun au serveur et au client

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)

Côté serveur (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 `` ''. Veuillez également vous référer à l'article de référence [^ 1] pour ce domaine.


<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é automatiquementEquipmentServiceImplBaseHériter. Il y a des fichiers générés automatiquement,.Nom du service spécifié par ptoro+ ImplBaseEst.
(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 protoPasser outre.
(5) .protoTel que défini dans, dans la demandeschoolNameDepuis 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éthodeStreamObserverdeonNextEnvoyez une valeur côté client en définissant un objet de réponse dans l'argument de méthode.
(9) Enfin pour terminer la communicationonCompleteAppelez 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.

Côté client (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-demoPour obtenir des informations sur le côtéEurekaClientDI.
(2) Pour utiliser gRPCMannagedChannelUtiliser. Pour la génération de canauxManagedChannelBuilder.forAddressÀeureka-client-demoPassez 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.getPortEst correct.
(3) Il s'agit d'une classe stub générée automatiquement comme la façon dont cette bibliothèque est écriteEquipmentServiceBlockingStubCommuniquez en utilisant. Après avoir défini les paramètres dans la requête, le restestb.getEquipmentUne requête est définie dans l'argument de la méthode et une réponse est reçue.

Courir

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.

Code source

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.

référence

Recommended Posts

Essayez gRPC dans le projet Spring Boot et Spring Cloud (Mac OS)
Essayez Spring Boot sur Mac
Lancer un (ancien) projet Spring Boot avec IntelliJ
Créer un projet Java Spring Boot avec IntelliJ
Exécuter un projet Spring Boot avec VS Code
Conseils Java - Créez un projet Spring Boot avec Gradle
Afficher la tâche Gradle dans le projet Spring Boot
Microservices dans Spring Cloud
Définir le paramètre contextuel dans Spring Boot
Essayez Spring Boot de 0 à 100.
Multi-projets Spring Boot 2 avec Gradle
Changements majeurs dans Spring Boot 1.5
NoHttpResponseException dans Spring Boot + WireMock
Essayez d'utiliser Spring Boot Security
Spring Boot Hello World dans Eclipse
Développement d'applications Spring Boot dans Eclipse
gRPC sur Spring Boot avec grpc-spring-boot-starter
Spring Boot 2.2 prend désormais en charge RSocket, alors essayez-le
Essayez de mélanger le langage C et Swift dans un seul projet (OS X, Linux)
Écrire du code de test avec Spring Boot
Spring Boot: exemple de projet d'API Restful
Qu'est-ce que @Autowired dans Spring Boot?
Google Cloud Platform avec Spring Boot 2.0.0
Implémenter l'application Spring Boot dans Gradle
Essayez d'exécuter Spring Boot sur Kubernetes
Comment utiliser Thymeleaf avec Spring Boot
Essayez de mélanger plus de langage C et Swift dans un seul projet (OS X, Linux)
Créez un projet Spring Boot avec intellij et quittez immédiatement après le lancement
Une histoire sur un projet Spring Boot écrit en Java qui prend en charge Kotlin
Essayez de résoudre Project Euler en Java
Mémo de construction de l'environnement Spring Boot sur Mac
Créer une image Spring Boot + Docker avec Gradle
Priorité d'accès aux fichiers statiques dans Spring Boot
Sortie du journal Spring Boot au format json
Mémorandum de téléchargement de fichier local avec Spring Boot
Essayez d'utiliser Spring Boot avec VS Code
Utiliser la méthode de requête DynamoDB avec Spring Boot
Essayez Spring Boot 1 (Construction de l'environnement ~ Démarrage de Tomcat)
DI SessionScope Bean dans le filtre Spring Boot 2
Modifier le délai d'expiration de la session dans Spring Boot
Cookie SameSite dans Spring Boot (Spring Web MVC + Tomcat)
Testez le contrôleur avec Mock MVC dans Spring Boot
Traitement asynchrone avec exécution régulière dans Spring Boot
Sortie des journaux de demande et de réponse avec Spring Boot
Mettre plusieurs classes principales dans un seul projet Spring
Essayez l'authentification LDAP avec Spring Security (Spring Boot) + OpenLDAP
Essayez d'utiliser l'API Cloud Vision de GCP en Java
Essayez d'implémenter la fonction de connexion avec Spring Boot
Utiliser le filtre de servlet avec Spring Boot [compatible Spring Boot 1.x, 2.x]
Comment ajouter un chemin de classe dans Spring Boot
Créez des projets Spring Boot par environnement avec Gradle
Comment se lier avec un fichier de propriétés dans Spring Boot
Essayez d'automatiser la migration avec Spring Boot Flyway
Annotations utilisées dans les outils de gestion des tâches Spring Boot
Créez un serveur Spring Cloud Config en toute sécurité avec Spring Boot 2.0
Jusqu'à ce que vous créiez un projet Spring Boot dans Intellij et que vous le transmettiez à Github
Construction de l'environnement de développement Java (Mac + Pleiades All in One Eclipse 4.7 + Spring Boot + Gradle (Buildship))