Aufbau einer Microservice-Architektur auf Golang und gRPC, Teil 1

Einführung in die Microservice-Architektur


Teil 1 von 10


Anpassung von Ewan Valentine Artikeln.


Dies ist eine Serie von zehn Teilen. Ich werde versuchen, einmal im Monat über den Aufbau von Mikrodiensten auf Golang zu schreiben. Ich werde Protobuf und gRPC als Haupttransportprotokoll verwenden.


Der Stapel, den ich verwendet habe: Golang, Mongodb, Grpc, Docker, Google Cloud, Kubernetes, NATS, CircleCI, Terraform und Go-Micro.


Wozu brauche ich das? Da ich lange gebraucht habe, um es herauszufinden und die angesammelten Probleme zu lösen. Ich wollte Ihnen auch mitteilen, was ich über das Erstellen, Testen und Bereitstellen von Microservices on Go und anderen neuen Technologien gelernt habe.


In diesem Teil möchte ich die grundlegenden Konzepte und Technologien für den Aufbau von Microservices zeigen. Schreiben wir eine einfache Implementierung. Das Projekt wird die folgenden Entitäten haben:


  • Ladung
  • Inventar
  • Versuch
  • die Benutzer
  • die Rollen
  • Authentifizierung


Um weiter zu gehen, müssen Sie Golang und die erforderlichen Bibliotheken installieren und ein Git-Repository erstellen.


Theorie


Was ist Microservice-Architektur?


Microservices isolieren eine separate Funktionalität in einen Dienst, die hinsichtlich der von diesem Dienst ausgeführten Funktion autark ist. Zur Kompatibilität mit anderen Diensten verfügt es über eine bekannte und vordefinierte Schnittstelle.
Microservices kommunizieren miteinander über Nachrichten, die über einen zwischengeschalteten Nachrichtenbroker übertragen werden.



Dank der Microservice-Architektur kann die Anwendung nicht ganz, sondern teilweise skaliert werden. Wenn der Autorisierungsdienst beispielsweise häufiger als andere zuckt, können wir die Anzahl der Instanzen erhöhen. Dieses Konzept steht im Einklang mit den Konzepten des Cloud Computing und der Containerisierung im Allgemeinen.


Warum Golang?


Microservices werden in fast allen Sprachen unterstützt, schließlich sind Microservices ein Konzept, keine bestimmte Struktur oder ein bestimmtes Werkzeug. Einige Sprachen sind jedoch besser geeignet und unterstützen darüber hinaus Microservices besser als andere. Eine Sprache mit großer Unterstützung ist Golang.


Treffen Sie protobuf / gRPC


Wie bereits erwähnt, sind Microservices in separate Codebasen unterteilt. Eines der wichtigsten Probleme bei Microservices ist die Kommunikation. Wenn Sie einen Monolithen haben, können Sie den Code einfach direkt von einer anderen Stelle in Ihrem Programm aus aufrufen.


Um das Kommunikationsproblem zu lösen, können wir den traditionellen REST-Ansatz verwenden und Daten im JSON- oder XML-Format über HTTP übertragen. Dieser Ansatz hat jedoch die Nachteile, dass Sie vor dem Senden einer Nachricht Ihre Daten verschlüsseln und auf der Empfangsseite wieder entschlüsseln müssen. Dies ist ein Overhead und erhöht die Komplexität des Codes.


Es gibt eine Lösung! Dies ist das gRPC- Protokoll - ein leichtes, binärbasiertes Protokoll, das die Übertragung von HTTP-Headern eliminiert und uns einige Bytes spart. Zukünftiges HTTP2 impliziert auch die Verwendung von Binärdaten, was wiederum für gRPC spricht. HTTP2 ermöglicht bidirektionale Kommunikation und es ist fantastisch!


Mit GRPC können Sie auch die Schnittstelle zu Ihrem Dienst in einem benutzerfreundlichen Format definieren - dies ist> protobuf .


Übe


Erstellen Sie die Datei /project/consigment.proto.
Offizielle Protobuf-Dokumentation


consigment.proto
//consigment.proto syntax = "proto3"; package go.micro.srv.consignment; service ShippingService { rpc CreateConsignment(Consignment) returns (Response) {} } message Consignment { string id = 1; string description = 2; int32 weight = 3; repeated Container containers = 4; string vessel_id = 5; } message Container { string id = 1; string customer_id = 2; string origin = 3; string user_id = 4; } message Response { bool created = 1; Consignment consignment = 2; } 

Dies ist ein einfaches Beispiel, das den Dienst enthält, den Sie für andere Dienste bereitstellen möchten: Dienst ShippingService. Anschließend definieren wir unsere Nachrichten. Protobuf ist ein statisch typisiertes Protokoll, und wir können benutzerdefinierte Typen erstellen (ähnlich wie Strukturen in Golang). Hier ist der Container im Stapel verschachtelt.


Installieren Sie die Bibliotheken, den Compiler und kompilieren Sie unser Protokoll:


 $ go get -u google.golang.org/grpc $ go get -u github.com/golang/protobuf/protoc-gen-go $ sudo apt install protobuf-compiler $ mkdir consignment && cd consignment $ protoc -I=. --go_out=plugins=grpc:. consignment.proto 

Die Ausgabe sollte eine Datei sein:


consignment.pb.go
 // Code generated by protoc-gen-go. DO NOT EDIT. // source: consignment.proto package consignment import ( fmt "fmt" proto "github.com/golang/protobuf/proto" context "golang.org/x/net/context" grpc "google.golang.org/grpc" math "math" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Consignment struct { Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` Weight int32 `protobuf:"varint,3,opt,name=weight,proto3" json:"weight,omitempty"` Containers []*Container `protobuf:"bytes,4,rep,name=containers,proto3" json:"containers,omitempty"` VesselId string `protobuf:"bytes,5,opt,name=vessel_id,json=vesselId,proto3" json:"vessel_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Consignment) Reset() { *m = Consignment{} } func (m *Consignment) String() string { return proto.CompactTextString(m) } func (*Consignment) ProtoMessage() {} func (*Consignment) Descriptor() ([]byte, []int) { return fileDescriptor_3804bf87090b51a9, []int{0} } func (m *Consignment) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Consignment.Unmarshal(m, b) } func (m *Consignment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Consignment.Marshal(b, m, deterministic) } func (m *Consignment) XXX_Merge(src proto.Message) { xxx_messageInfo_Consignment.Merge(m, src) } func (m *Consignment) XXX_Size() int { return xxx_messageInfo_Consignment.Size(m) } func (m *Consignment) XXX_DiscardUnknown() { xxx_messageInfo_Consignment.DiscardUnknown(m) } var xxx_messageInfo_Consignment proto.InternalMessageInfo func (m *Consignment) GetId() int32 { if m != nil { return m.Id } return 0 } func (m *Consignment) GetDescription() string { if m != nil { return m.Description } return "" } func (m *Consignment) GetWeight() int32 { if m != nil { return m.Weight } return 0 } func (m *Consignment) GetContainers() []*Container { if m != nil { return m.Containers } return nil } func (m *Consignment) GetVesselId() string { if m != nil { return m.VesselId } return "" } type Container struct { Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` CustomerId string `protobuf:"bytes,2,opt,name=customer_id,json=customerId,proto3" json:"customer_id,omitempty"` Origin string `protobuf:"bytes,3,opt,name=origin,proto3" json:"origin,omitempty"` UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Container) Reset() { *m = Container{} } func (m *Container) String() string { return proto.CompactTextString(m) } func (*Container) ProtoMessage() {} func (*Container) Descriptor() ([]byte, []int) { return fileDescriptor_3804bf87090b51a9, []int{1} } func (m *Container) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Container.Unmarshal(m, b) } func (m *Container) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Container.Marshal(b, m, deterministic) } func (m *Container) XXX_Merge(src proto.Message) { xxx_messageInfo_Container.Merge(m, src) } func (m *Container) XXX_Size() int { return xxx_messageInfo_Container.Size(m) } func (m *Container) XXX_DiscardUnknown() { xxx_messageInfo_Container.DiscardUnknown(m) } var xxx_messageInfo_Container proto.InternalMessageInfo func (m *Container) GetId() int32 { if m != nil { return m.Id } return 0 } func (m *Container) GetCustomerId() string { if m != nil { return m.CustomerId } return "" } func (m *Container) GetOrigin() string { if m != nil { return m.Origin } return "" } func (m *Container) GetUserId() string { if m != nil { return m.UserId } return "" } type Response struct { Created bool `protobuf:"varint,1,opt,name=created,proto3" json:"created,omitempty"` Consignment *Consignment `protobuf:"bytes,2,opt,name=consignment,proto3" json:"consignment,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor_3804bf87090b51a9, []int{2} } func (m *Response) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Response.Unmarshal(m, b) } func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Response.Marshal(b, m, deterministic) } func (m *Response) XXX_Merge(src proto.Message) { xxx_messageInfo_Response.Merge(m, src) } func (m *Response) XXX_Size() int { return xxx_messageInfo_Response.Size(m) } func (m *Response) XXX_DiscardUnknown() { xxx_messageInfo_Response.DiscardUnknown(m) } var xxx_messageInfo_Response proto.InternalMessageInfo func (m *Response) GetCreated() bool { if m != nil { return m.Created } return false } func (m *Response) GetConsignment() *Consignment { if m != nil { return m.Consignment } return nil } func init() { proto.RegisterType((*Consignment)(nil), "Consignment") proto.RegisterType((*Container)(nil), "Container") proto.RegisterType((*Response)(nil), "Response") } func init() { proto.RegisterFile("consignment.proto", fileDescriptor_3804bf87090b51a9) } var fileDescriptor_3804bf87090b51a9 = []byte{ // 281 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xbf, 0x4e, 0x33, 0x31, 0x10, 0xc4, 0xbf, 0xcb, 0xff, 0x5b, 0x7f, 0x02, 0xc5, 0x05, 0x58, 0x50, 0x70, 0xba, 0x2a, 0xa2, 0x70, 0x11, 0x9e, 0x00, 0xa5, 0x4a, 0xeb, 0xd0, 0xa3, 0x60, 0xaf, 0x2e, 0x2b, 0x11, 0xfb, 0x64, 0x3b, 0xe1, 0x75, 0x78, 0x54, 0x74, 0xbe, 0x1c, 0x18, 0x51, 0xce, 0xac, 0x67, 0xf7, 0xa7, 0x31, 0x2c, 0xb5, 0xb3, 0x81, 0x1a, 0x7b, 0x44, 0x1b, 0x65, 0xeb, 0x5d, 0x74, 0xf5, 0x67, 0x01, 0x6c, 0xf3, 0xe3, 0xf2, 0x2b, 0x18, 0x91, 0x11, 0x45, 0x55, 0xac, 0xa6, 0x6a, 0x44, 0x86, 0x57, 0xc0, 0x0c, 0x06, 0xed, 0xa9, 0x8d, 0xe4, 0xac, 0x18, 0x55, 0xc5, 0xaa, 0x54, 0xb9, 0xc5, 0x6f, 0x60, 0xf6, 0x81, 0xd4, 0x1c, 0xa2, 0x18, 0xa7, 0xd4, 0x45, 0xf1, 0x47, 0x00, 0xed, 0x6c, 0xdc, 0x93, 0x45, 0x1f, 0xc4, 0xa4, 0x1a, 0xaf, 0xd8, 0x1a, 0xe4, 0x66, 0xb0, 0x54, 0x36, 0xe5, 0xf7, 0x50, 0x9e, 0x31, 0x04, 0x7c, 0x7f, 0x25, 0x23, 0xa6, 0xe9, 0xc6, 0xa2, 0x37, 0xb6, 0xa6, 0x3e, 0x42, 0xf9, 0x9d, 0xfa, 0xc3, 0xf7, 0x00, 0x4c, 0x9f, 0x42, 0x74, 0x47, 0xf4, 0x5d, 0xb6, 0xe7, 0x83, 0xc1, 0xda, 0x9a, 0x0e, 0xcf, 0x79, 0x6a, 0xc8, 0x26, 0xbc, 0x52, 0x5d, 0x14, 0xbf, 0x85, 0xf9, 0x29, 0xf4, 0xa1, 0x49, 0x3f, 0xe8, 0xe4, 0xd6, 0xd4, 0x2f, 0xb0, 0x50, 0x18, 0x5a, 0x67, 0x03, 0x72, 0x01, 0x73, 0xed, 0x71, 0x1f, 0xb1, 0x3f, 0xb9, 0x50, 0x83, 0xe4, 0x12, 0x58, 0x56, 0x66, 0xba, 0xcb, 0xd6, 0xff, 0x65, 0x56, 0xa5, 0xca, 0x1f, 0xac, 0x9f, 0xe1, 0x7a, 0x77, 0xa0, 0xb6, 0x25, 0xdb, 0xec, 0xd0, 0x9f, 0x49, 0x23, 0x97, 0xb0, 0xdc, 0xa4, 0x6d, 0x79, 0xff, 0xbf, 0x56, 0xdc, 0x95, 0x72, 0x40, 0xa9, 0xff, 0xbd, 0xcd, 0xd2, 0x8f, 0x3d, 0x7d, 0x05, 0x00, 0x00, 0xff, 0xff, 0x84, 0x5c, 0xa4, 0x06, 0xc6, 0x01, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 // ShippingServiceClient is the client API for ShippingService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ShippingServiceClient interface { CreateConsignment(ctx context.Context, in *Consignment, opts ...grpc.CallOption) (*Response, error) } type shippingServiceClient struct { cc *grpc.ClientConn } func NewShippingServiceClient(cc *grpc.ClientConn) ShippingServiceClient { return &shippingServiceClient{cc} } func (c *shippingServiceClient) CreateConsignment(ctx context.Context, in *Consignment, opts ...grpc.CallOption) (*Response, error) { out := new(Response) err := c.cc.Invoke(ctx, "/ShippingService/CreateConsignment", in, out, opts...) if err != nil { return nil, err } return out, nil } // ShippingServiceServer is the server API for ShippingService service. type ShippingServiceServer interface { CreateConsignment(context.Context, *Consignment) (*Response, error) } func RegisterShippingServiceServer(s *grpc.Server, srv ShippingServiceServer) { s.RegisterService(&_ShippingService_serviceDesc, srv) } func _ShippingService_CreateConsignment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(Consignment) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ShippingServiceServer).CreateConsignment(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/ShippingService/CreateConsignment", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ShippingServiceServer).CreateConsignment(ctx, req.(*Consignment)) } return interceptor(ctx, in, info, handler) } var _ShippingService_serviceDesc = grpc.ServiceDesc{ ServiceName: "ShippingService", HandlerType: (*ShippingServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "CreateConsignment", Handler: _ShippingService_CreateConsignment_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "consignment.proto", } 

Wenn ja, dann ist etwas schief gelaufen. Achten Sie auf die Argumente -I ist der Pfad, in dem der Compiler nach Dateien sucht, --go_out, in dem eine neue Datei erstellt wird. Es gibt immer Hilfe


 $ protoc -h 

Dies ist der Code, der automatisch von den gRPC / protobuf-Bibliotheken generiert wird, damit Sie Ihre protobuf-Definition mit Ihrem eigenen Code verknüpfen können.


Wir werden main.go schreiben


main.go
 package seaport import ( "log" "net" //    pbf "seaport/consignment" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) const ( port = ":50051" ) //IRepository -   type IRepository interface { Create(*pbf.Consignment) (*pbf.Consignment, error) } // Repository -    , //        type Repository struct { consignments []*pbf.Consignment } //Create -    func (repo *Repository) Create(consignment *pbf.Consignment) (*pbf.Consignment, error) { updated := append(repo.consignments, consignment) repo.consignments = updated return consignment, nil } //         //       .     //         . . type service struct { repo IRepository } // CreateConsignment -        , //    create,      //     gRPC. func (s *service) CreateConsignment(ctx context.Context, req *pbf.Consignment) (*pbf.Response, error) { //      consignment, err := s.repo.Create(req) if err != nil { return nil, err } //   `Response`, //        return &pbf.Response{Created: true, Consignment: consignment}, nil } func main() { repo := &Repository{} //   gRPC    tcp lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() //      gRPC,    //        //  `Response`,       pbf.RegisterShippingServiceServer(s, &service{repo}) //      gRPC. reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } 

Bitte lesen Sie die Kommentare im Code sorgfältig durch. Anscheinend erstellen wir hier eine Implementierungslogik, in der unsere gRPC-Methoden unter Verwendung der generierten Formate interagieren und einen neuen gRPC-Server auf Port 50051 erstellen. Jetzt wird unser gRPC-Dienst dort leben.
Sie können dies mit $ go run main.go ausführen , aber Sie sehen nichts und können es nicht verwenden ... Erstellen wir also einen Client, um es in Aktion zu sehen.


Erstellen wir eine Befehlszeilenschnittstelle, die eine JSON-Datei verwendet und mit unserem gRPC-Dienst interagiert.


Erstellen Sie im Stammverzeichnis ein neues Unterverzeichnis von $ mkdir consignment-cli . Erstellen Sie in diesem Verzeichnis eine cli.go-Datei mit folgendem Inhalt:


cli.go
 package main import ( "encoding/json" "io/ioutil" "log" "os" pbf "seaport/consignment" "golang.org/x/net/context" "google.golang.org/grpc" ) const ( address = "localhost:50051" defaultFilename = "consignment.json" ) //    func parseFile(file string) (*pbf.Consignment, error) { var consignment *pbf.Consignment data, err := ioutil.ReadFile(file) if err != nil { return nil, err } json.Unmarshal(data, &consignment) return consignment, err } func main() { //     conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("  : %v", err) } defer conn.Close() client := pbf.NewShippingServiceClient(conn) //    consignment.json, //          file := defaultFilename if len(os.Args) > 1 { file = os.Args[1] } consignment, err := parseFile(file) if err != nil { log.Fatalf("   : %v", err) } r, err := client.CreateConsignment(context.Background(), consignment) if err != nil { log.Fatalf("  : %v", err) } log.Printf(": %t", r.Created) } 

Erstellen Sie nun einen Stapel (consignment-cli / consignment.json):


 { "description": "  ", "weight": 100, "containers": [ { "customer_id": "_001", "user_id": "_001", "origin": " " } ], "vessel_id": "_001" } 

Wenn Sie nun $ go ausführen, führen Sie main.go aus dem Seehafenpaket aus und führen Sie dann $ go run cli.go in einem separaten Terminalfenster aus. Sie sollten die Meldung "Erstellt: wahr" sehen.
Aber wie können wir überprüfen, was genau erstellt wurde? Lassen Sie uns unseren Service mithilfe der GetConsignments-Methode aktualisieren, damit wir alle von uns erstellten Stapel anzeigen können.


consigment.proto
 //consigment.proto syntax = "proto3"; service ShippingService{ rpc CreateConsignment(Consignment) returns (Response) {} //    rpc GetConsignments(GetRequest) returns (Response) {} } message Consignment { int32 id = 1; string description = 2; int32 weight = 3; repeated Container containers = 4; string vessel_id = 5; } message Container { int32 id =1; string customer_id =2; string origin = 3; string user_id = 4; } //    message GetRequest {} message Response { bool created = 1; Consignment consignment = 2; //     //     repeated Consignment consignments = 3; } 

Also haben wir hier eine neue Methode für unseren Service namens GetConsignments erstellt. Außerdem haben wir eine neue GetRequest erstellt , die noch nichts enthält. Wir haben unserer Antwortnachricht auch ein Feld mit gesendeten Stapeln hinzugefügt. Sie werden feststellen, dass der Typ hier das wiederholte Schlüsselwort bis zum Typ hat. Wie Sie wahrscheinlich vermutet haben, bedeutet dies einfach, dieses Feld als Array dieser Typen zu behandeln.


Beeilen Sie sich nicht, um das Programm auszuführen. Die Implementierung unserer gRPC-Methoden basiert auf der Übereinstimmung der von der protobuf-Bibliothek erstellten Schnittstelle. Wir müssen sicherstellen, dass unsere Implementierung unserer Protodefinition entspricht.


 //seaport/main.go //IRepository -   type IRepository interface { Create(*pbf.Consignment) (*pbf.Consignment, error) GetAll() []*pbf.Consignment } //GetAll -       func (repo *Repository) GetAll() []*pbf.Consignment { return repo.consignments } //GetConsignments -         func (s *service) GetConsignments(ctx context.Context, req *pbf.GetRequest) (*pbf.Response, error) { consignments := s.repo.GetAll() return &pbf.Response{Consignments: consignments}, nil } 

Hier haben wir unsere neue GetConsignments-Methode aufgenommen, unser Repository und unsere Schnittstelle aktualisiert und jeweils in der Definition von consignments.proto erstellt. Wenn Sie $ go run main.go erneut ausführen , sollte das Programm erneut funktionieren.


Lassen Sie uns unser CLI-Tool aktualisieren, um die Möglichkeit zu bieten, diese Methode aufzurufen, und es ist möglich, unsere Parteien aufzulisten:


cli.go
 package main import ( "encoding/json" "io/ioutil" "log" "os" pbf "seaport/consignment" "golang.org/x/net/context" "google.golang.org/grpc" ) const ( address = "localhost:50051" defaultFilename = "consignment.json" ) //    func parseFile(file string) (*pbf.Consignment, error) { var consignment *pbf.Consignment data, err := ioutil.ReadFile(file) if err != nil { return nil, err } json.Unmarshal(data, &consignment) return consignment, err } func main() { //     conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("  : %v", err) } defer conn.Close() client := pbf.NewShippingServiceClient(conn) //    consignment.json, //          file := defaultFilename if len(os.Args) > 1 { file = os.Args[1] } consignment, err := parseFile(file) if err != nil { log.Fatalf("   : %v", err) } r, err := client.CreateConsignment(context.Background(), consignment) if err != nil { log.Fatalf("  : %v", err) } log.Printf(": %t", r.Created) getAll, err := client.GetConsignments(context.Background(), &pbf.GetRequest{}) if err != nil { log.Fatalf("    : %v", err) } for _, cns := range getAll.Consignments { fmt.Printf("Id: %v\n", cns.GetId()) fmt.Printf("Description: %v\n", cns.GetDescription()) fmt.Printf("Weight: %d\n", cns.GetWeight()) fmt.Printf("VesselId: %v\n", cns.GetVesselId()) for _, cnt := range cns.GetContainers() { fmt.Printf("\tId: %v\n", cnt.GetId()) fmt.Printf("\tUserId: %v\n", cnt.GetUserId()) fmt.Printf("\tCustomerId: %v\n", cnt.GetCustomerId()) fmt.Printf("\tOrigin: %v\n", cnt.GetOrigin()) } } } 

Fügen Sie den obigen Code zu cli.go hinzu und führen Sie $ go run cli.go erneut aus . Der Client führt CreateConsignment aus und ruft dann GetConsignments auf. Und Sie sollten sehen, dass in der Antwortliste die Zusammensetzung der Partei enthält.


Somit haben wir den ersten Microservice und Client, der mit Protobuf und gRPC mit ihm interagiert.


Der nächste Teil dieser Reihe wird die Go-Micro-Integration beinhalten, die eine leistungsstarke Grundlage für die Erstellung von gRPC-basierten Microservices darstellt. Wir werden auch unseren zweiten Service erstellen. Betrachten Sie die Arbeit unserer Dienste in Docker-Containern im nächsten Teil dieser Artikelserie.

Source: https://habr.com/ru/post/de425653/


All Articles