gRPC mit Golang auf Server und Clientseite nutzen

gRPC mit Golang auf Server und Clientseite nutzen

Die Kommunikation zwischen einzelnen Services benötigt ein Protokoll, auf das sich die Teilnehmer verständigen und darüber Nachrichten austauschen können. Für die interne Kommunikation von Microservices hatte sich Google früh dafür entschieden ein eigenes RPC Protokoll zu entwickeln und dieses intern zu nutzen. Aus diesem Stubby Protokoll ist schließlich gRPC entstanden. Als OpenSource Protokoll und Framework setzt gRPC stark auf vorhandene Standards wie z. B. HTTP/2.

Mittlerweile ist gRPC ein Cloud Native Computing Foundation (CNCF) Projekt und verbreitet sich dementsprechend immer mehr.

Hervorstechende Features sind z. B.:

  • Einfache Servicedefinition
  • Plattform und sprachneutral
  • Bidirektionales Streaming
  • Direkte Integration von Authorisierung

gRPC is a modern open source high performance RPC framework that can run in any environment.

Basis von gRPC ist ProtoBuffer, ein plattformneutraler Mechanismus um strukturierte Daten zu serialisieren. Im Artikel ProtoBuffer/protobuf mit Go nutzen wird gezeigt wie man mittels Go direkt mit protobuf arbeiten kann.

In diesem Artikel wird ein Server und ein Client entwickelt, die Nachrichten über gRPC austauschen. Es handelt sich um einen primitiven Kommentarservice. Ziel des Beispiels ist die Implementierung eines ersten gRPC basierten Microservice in Go.

Der Server wird zwei Methoden anbieten:

  • Neuen Kommentar anlegen/ hochladen
  • Alle Kommentare ausgeben

Die Implementierung der Persistenz wurde der Einfachkeit halber nicht ausimplementiert.

gRPC Beispiel

gRPC Beispiel

Beide Bestandteile, also Client und Server, werden in Golang implementiert.

Das komplette Beispiel kann auch in GitHub gefunden werden.

Definition des Service

Ausgangspunkt für die Implementierung ist die Schnittstellenbeschreibung. Es werden “zwei” Nachrichtentypen benötigt:

  • Comment - einzelner Kommentar
  • CommentList - Liste mit Kommentaren

Analog zum protobuf Beispiel werden diese Datentypen in einer *.proto Datei beschrieben.

message Comment {
	string message = 1;
	string who = 2;
}

message CommentList {
	repeated Comment comments = 1;
}

Über das Schlüsselwort service wird in protobuf ein neuer Service beschrieben. Einzelne RPC Methoden werden mit dem Schlüsselwort rpc gekennzeichnet. Der oben genannte Kommentarservice sieht dementsprechend so aus:

service Comments {
	rpc createComment(Comment) returns(Void) {};
	rpc getComments(Void) returns(CommentList) {};
}

Protobuf kennt allerdings keine Void Datentypen. Dieser muss selbst definiert werden.

message Void {}

Die komplette Schnittstellenbeschreibung sieht nun wie folgt aus:

comment.proto

syntax = "proto3";

package comment;

message Comment {
	string message = 1;
	string who = 2;
}

message CommentList {
	repeated Comment comments = 1;
}

message Void {}

service Comments {
	rpc createComment(Comment) returns(Void) {};
	rpc getComments(Void) returns(CommentList) {};
}

Generierung des Go Codes

Zur Generierung des Go Codes wird wieder der ProtoBuffer Compiler protoc mit passenden Go und gRPC Plugins benötigt (Die Installation für Go ist im Artikel ProtoBuffer/protobuf mit Go nutzen beschrieben).

Mit dem Parameter --go_out=plugins=grpc:comment teilen wir dem Compiler mit, dass noch gRPC Stubs und Skeletons generiert werden sollen. Die Angabe :comment bezieht sich hier auf das Ausgabeverzeichnis.

protoc --go_out=plugins=grpc:comment *.proto

Eventuell muss das gRPC Go Package noch nachinstalliert werden. Dies kann wie folgt durchgeführt werden:

go get google.golang.org/grpc

Nach der Generierung findet man in der Go Datei (im Beispiel comment.pb.go) unter Anderem die Definition des Server- sowie des Client-Interfaces, die als Basis für die Implementierung dienen.

// CommentsServer is the server API for Comments service.
type CommentsServer interface {
	NewComment(context.Context, *Void) (*CommentList, error)
}

// CommentsClient is the client API for Comments service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CommentsClient interface {
	CreateComment(ctx context.Context, in *Comment, opts ...grpc.CallOption) (*Void, error)
	GetComments(ctx context.Context, in *Void, opts ...grpc.CallOption) (*CommentList, error)
}

Go gRPC Server implementieren

Auf der Serverseite muss das Interface CommentsServer implementiert und in einem gRPC Server angemeldet werden. Zum Anmelden des Service am gRPC Server wurde bei der Generierung bereits die Methode RegisterCommentsServer(..) erzeugt. Als Parameter muss man den eigentlichen gRPC Server sowie die Serviceimplementierung übergeben.

Durch die Package Angabe in der *.proto Datei wurde die Implementierung im Go Package package comment erzeugt.

srv := grpc.NewServer()
comment.RegisterCommentsServer(srv, commentsImpl)

Nach der Registrierung muss der Server noch einen Listener bekommen, dass er auch von außen erreichbar ist.

listener, _ := net.Listen("tcp", ":8080")
err = srv.Serve(listener)

Es fehlt nun nur noch die Implementierung des CommentsServer Interfaces. Wie oben bereits erwähnt liegt der Fokus hier nicht auf einer super Service Umsetzung ;-)

import (
	"context"

	"github.com/kkoehler/golang/grpc/comment"
	"google.golang.org/grpc"
)

type CommentsServerImpl struct {
}

func (cs CommentsServerImpl) CreateComment(ctx context.Context, newComment *comment.Comment) (*comment.Void, error) {

	log.Printf("server called with comment: %s", newComment.Message)
	return &comment.Void{}, nil

}

func (cs CommentsServerImpl) GetComments(context.Context, *comment.Void) (*comment.CommentList, error) {

	log.Println("returning list")
	list := &comment.CommentList{}

	list.Comments = append(list.Comments, &comment.Comment{Message: "eins"})
	list.Comments = append(list.Comments, &comment.Comment{Message: "zwei"})

	return list, nil
}

Der Server kann nun gestartet werden.

Das komplette Beispiel kann auch in GitHub gefunden werden.

Go gRPC Client implementieren

Der zu implementierende Go Client fällt ebenso recht schlank aus. Auch hier wird der generierte Code des Compilers als Basis verwendet. Analog zur Serverseite wurde eine Methode NewCommentsClient erzeugt, mit der der Client eine Verbindung zum Server aufbauen kann. Als Parameter wird hier eine Connection benötigt, die man sich ebenfalls vom gRPC Package erzeugen lassen kann.

Startet man den Client ohne die Option grpc.WithInsecure() bekommt man soft die Fehlermeldung no transport security set (use grpc.WithInsecure() explicitly or set credentials) präsentiert. Im Beispiel soll eine unsichere Verbindung genutzt werden.

con, _ := grpc.Dial(":8080", grpc.WithInsecure())
client := comment.NewCommentsClient(con)

Der eigentliche API Aufruf ist eher unspektakulär.

ctx := context.Background()
commentToCreate := comment.Comment{Message: "Toll!", Who: "kristian"}
_, err = client.CreateComment(ctx, &commentToCreate)

Startet man nun, zusätzlich zum Server, noch die Client, kommunizieren diese über gRPC.

Das komplette Beispiel kann auch in GitHub gefunden werden.

Mehr?

Der Artikel hat nur die rudimentären Möglichkeiten von gRPC mit Go vorgestellt. Weitere Informationen zu gRPC findet man direkt auf der Projektseite. Hier findet man auch Dokumentation zu anderen Programmiersprachen wie Java, Python, JavaScript bzw. Node.js oder C++.

29.03.2019

 

Kennen Sie schon das Buch zum Thema?

Der praktische Soforteinstieg für Developer und Softwarearchitekten, die direkt mit Go produktiv werden wollen.

  • Von den Sprachgrundlagen bis zur Qualitätssicherung
  • Architekturstil verstehen und direkt anwenden
  • Idiomatic Go, gRPC, Go Cloud Development Kit
  • Cloud-native Anwendungen erstellen
Microservices mit Go Buch

zur Buchseite beim Rheinwerk Verlag Rheinwerk Computing, ISBN 978-3-8362-7559-0 (als PDF, EPUB, MOBI und Papier)

Kontakt

Source Fellows GmbH

Source Fellows GmbH Logo

Lerchenstraße 31

72762 Reutlingen

Telefon: (0049) 07121 6969 802

E-Mail: info@source-fellows.com