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.:
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:
Die Implementierung der Persistenz wurde der Einfachkeit halber nicht ausimplementiert.
Beide Bestandteile, also Client und Server, werden in Golang implementiert.
Das komplette Beispiel kann auch in GitHub gefunden werden.
Ausgangspunkt für die Implementierung ist die Schnittstellenbeschreibung. Es werden “zwei” Nachrichtentypen benötigt:
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) {};
}
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)
}
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 Packagepackage 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.
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.
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
Der praktische Soforteinstieg für Developer und Softwarearchitekten, die direkt mit Go produktiv werden wollen.
zur Buchseite beim Rheinwerk Verlag Rheinwerk Computing, ISBN 978-3-8362-7559-0 (als PDF, EPUB, MOBI und Papier)
Source Fellows GmbH
Lerchenstraße 31
72762 Reutlingen
Telefon: (0049) 07121 6969 802
E-Mail: info@source-fellows.com