GRPC commençant par le serveur Go et le client Dart

introduction

J'écrirai sur la façon d'utiliser gRPC pour la communication entre le serveur Go API et le client Dart (en supposant Flutter).

Hibiya Music Festival Osanpo App 2020 Development Behind the Scenes / Server Edition Les 20 nouveaux diplômés et 21 diplômés de DeNA J'ai fait référence à l'article de la personne, je vais donc le partager.

Aussi, pourquoi est-il agréable d'utiliser gRPC? Une session de Nao Minami de Cloud Native Days Tokyo 2020 "Real World Migration from HTTP to gRPC" C'est facile à comprendre lorsque vous écoutez, alors je vais également partager ceci. Vous pouvez accéder à la vidéo archivée à partir du lien.

Préparation

Pour générer automatiquement à partir d'un fichier .proto, vous devez utiliser la commande protoc et les plug-ins périphériques, mais je ne veux pas installer diverses choses localement, donc je lance un conteneur Docker et génère du code pour chaque langue. Vous pouvez vérifier la dernière version des tampons de protocole actuels à partir du référentiel GitHub (https://github.com/protocolbuffers/protobuf/releases).

Dockerfile



FROM golang:1.15.0

ENV DEBIAN_FRONTEND=noninteractive

ARG PROTO_VERSION=3.13.0

WORKDIR /proto

COPY ./proto .

RUN mkdir /output /output/server /output/client

RUN apt-get -qq update && apt-get -qq install -y \
  unzip

RUN curl -sSL https://github.com/protocolbuffers/protobuf/releases/download/v${PROTO_VERSION}/protoc-${PROTO_VERSION}-linux-x86_64.zip -o protoc.zip && \
  unzip -qq protoc.zip && \
  cp ./bin/protoc /usr/local/bin/protoc && \
  cp -r ./include /usr/local

# Go
RUN go get -u github.com/golang/protobuf/protoc-gen-go

# Dart
RUN apt-get install apt-transport-https
RUN sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
RUN sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
RUN apt-get update
RUN apt-get install dart -y
ENV PATH="${PATH}:/usr/lib/dart/bin/"
ENV PATH="${PATH}:/root/.pub-cache/bin"
RUN pub global activate protoc_plugin

Exécutez le script suivant dans ce conteneur

protoc.sh


#!/bin/sh

set -xe

SERVER_OUTPUT_DIR=/output/server
CLIENT_OUTPUT_DIR=/output/client

protoc --version
protoc -I=/proto/protos hoge.proto fuga.proto\
  --go_out=plugins="grpc:${SERVER_OUTPUT_DIR}" \
  --dart_out="grpc:${CLIENT_OUTPUT_DIR}"

#horodatage dans le fichier proto.Requis lors de l'importation de proto, etc.
protoc -I=/proto/protos timestamp.proto wrappers.proto\
  --dart_out="grpc:${CLIENT_OUTPUT_DIR}"

Lorsque vous utilisez docker-compose, si vous définissez la commande pour exécuter protoc.sh plus tôt, le code sera automatiquement généré au démarrage. Synchronisez le répertoire contenant le fichier proto et protoc.sh avec les volumes, le répertoire de sortie côté serveur et le répertoire de sortie côté client.

docker-compose.yml


version: '3.8'

services:
  proto:
    build:
      context: .
      dockerfile: docker/proto/Dockerfile
    command: ./protoc.sh
    volumes:
      - ./proto:/proto
      - ./client:/output/client
      - ./server:/output/server

Cependant, lorsque vous spécifiez le package go du côté du fichier proto, par exemple

hoge.proto


syntax = "proto3";

package hoge.hoge;

option go_package = "hoge/fuga/foo/bar";

service Hoge{
}

Si tel est le cas, il sera affiché dans `` / output / server / hoge / fuga / foo '' du côté du conteneur.

Côté serveur (Go)

hoge.pb.go``` est généré dans les volumes spécifiés dans docker-compose.yml. Puisqu'il contient la définition d'Inerface de Clinet (il est facile à trouver si vous recherchez dans le fichier avec ctx), nous allons implémenter la méthode en regardant cette partie.

go:hoge.pb.go


// HogeClient is the client API for Hoge service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HogeClient interface {
	CreateHoge(ctx context.Context, in *CreateHogeMessage, opts ...grpc.CallOption) (*CreateHogeResponse, error)
}

Créez HogeController en tant que classe qui implémente HogeClient. Le point à noter ici est que l'argument de longueur variable est reçu après le troisième argument, mais il n'est pas nécessaire d'écrire des opts dans la méthode côté implémentation.

hoge_controller.go


type HogeController struct{
}

func (ctrl HogeController) CreateHoge(ctx context.Context, in *CreateHogeMessage) (*CreateHogeResponse, error){
	// TODO: return *CreateHogeResponse, error
}

Enregistrez l'instance HogeController créée ci-dessus avec l'instance de serveur gRPC avec RegisterHogeServer ().

main.go


func main() {
	listenPort, err := net.Listen("tcp", ":8000")
	if err != nil {
		log.Fatalln(err)
	}
	server := grpc.NewServer()

	hogeCtrl := NewHogeController()
	pb.RegisterHogeServer(server, &hogeCtrl)

	if err := server.Serve(listenPort); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

Côté client (Dart)

Le client est hoge.pb.dart```, hoge.pbgrpc.dart, `` hoge.pbjson.dart, `` hoge. Si vous utilisez enum. pbenum.dart '' est généré.

Si timestamp.dart est utilisé du côté du fichier proto, une erreur se produira dans le chemin généré, changez donc simplement l'instruction d'importation dans `hoge.pb.dart``` en` `ʻimport'timestamp.pb.dart ' Vous pouvez l'utiliser en le changeant comme $ 1; ``. Dans le cas d'un appel unaire, vous pouvez communiquer avec les fonctions suivantes.

hoge.dart


Future<void> createHoge(dartSideParam1, dartSideParam2, dartSideParam3) async {
    final channel = ClientChannel('localhost',
        port: 8000,
        options:
            const ChannelOptions(credentials: ChannelCredentials.insecure()));
    final grpcClient = KitchenClient(channel,
        options: CallOptions(timeout: Duration(seconds: 10)));
    try {
      await grpcClient.createHoge(CreateHogerMessage()
        ..param1 = dartSideParam1
        ..param2 = dartSideParam2
        ..param3 = dartSideParam3;
      await channel.shutdown();
    } catch (error) {
      developer.log('Caught error: $error');
      await channel.shutdown();
      return Future.error(error);
    }
}

Sommaire

C'était un moyen de communiquer entre Go et Dart en utilisant gRPC. Dans certaines entreprises, nous mettons en œuvre en utilisant la méthode de communication Server Streaming, je voudrais donc partager ces informations à une date ultérieure.

Recommended Posts

GRPC commençant par le serveur Go et le client Dart
Essayons gRPC avec Go et Docker
GRPC commençant par Python
Processus d'authentification avec gRPC et authentification Firebase
Communiquez entre Elixir et Python avec gRPC
Code-Server x Dart avec ffi x Go x Clang
"Première recherche élastique" commençant par un client python
Comparez les vitesses d'analyse XML avec Python et Go
Lancer un serveur Web avec Python et Flask
Serveur de lecture de musique avec NanoPi-NEO, MPD et OLED
Trading système commençant par Python 3: investissement et risque
Python avec Go
Création d'un environnement distribué avec la série Raspberry PI (Partie 4: Création d'un serveur NFS et importation d'un système d'exploitation client)
Analyse d'images par apprentissage profond à partir de Kaggle et Keras
Serveur HTTP facile et paramètres de démarrage automatique de Systemd dans Go
Obtenez swagger.json avec Flask-RESTX (Flask-RESTPlus) sans démarrer le serveur
Comment créer un serveur HTTPS avec Go / Gin
Automatisation des tests à partir de la caméra Web intégrée L-Chika (5) et de l'OCR
Serveur HTTP et client HTTP utilisant Socket (+ navigateur Web) --Python3
Appelez la fonction C avec dart: ffi et rappelez la fonction Dart