Verteilte Entwicklung in Go
RESTful APIs und gRPC
Frank Müller
•Oldenburg, Deutschland


•1965 geboren


•Kubermatic


•Team Lead Development


•@themue
Einführung Frank Müller
Modularisierung hat sich verändert
• Laufzeitumgebung sind Clouds und Cluster


• Komponenten sind Microservices


• Aufteilung bringt Flexibilität und Skalierbarkeit


• APIs auf Basis von REST oder gRPC bringen
Sprachunabhängigkeit
Runtime im Netz Frank Müller
RESTful APIs in Go
• HTTP Bibliothek ist genug


• URL bezeichnet Typ und gegebenenfalls Ressource


• HTTP Methode bestimmt Aktion


• Create / Read / Update / Delete ist POST / GET / PUT / DELETE


• Body enthält die Daten


• Content Type ist in der Regel JSON
Einfache Struktur Frank Müller
POST /api/cards/12345/items


Content-Type: application/json


{


"articleNo": "9876",


"spec": {


"size": "XL",


"color": "red"


},


"number": 2


}
Beispiel Frank Müller
• Package net/http enthält Server, Request und Response


• Es definiert den Handler und implementiert ihn als ServeMux


• Nicht hinreichend, aber leicht zu ergänzen


• Ein eigenes Package httpx hilft


‣ Pfade abbilden


‣ Methoden abbilden
Standardbibliothek genügt beinahe Frank Müller
// NestedHandler allows to nest handler following the


// pattern /handlerA/{entityID-A}/handlerB/{entityID-B}.


type NestedHandler struct {


resource string


handler http.Handler


nested map[string]http.Handler


}


func NewNestedHandler(resource string, handler http.Handler) *NestedHandler {


return &NestedHandler{


resource: resource,


handler: handler,


nested: make(map[string]http.Handler),


}


}
Pfade abbilden (1) Frank Müller
// Nest nests another nested handler.


func (h *NestedHandler) Nest(handler *NestedHandler) *NestedHandler {


h.nested[handler.resource] = handler


return handler


}


// NestHandler nests any handler as new nested handler.


func (h *NestedHandler) NestHandler(


resource string, handler http.Handler) *NestedHandler {


nh := NewNestedHandler(resource, handler)


h.nested[resource] = nh


return nh


}
Pfade abbilden (2) Frank Müller
func (h *NestedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {


// Check if own or one of the nested handler is addressed.


eh, ok := h.analyzePath(r.URL.Path)


if ok {


eh.ServeHTTP(w, r)


return


}


// Handler not found.


http.NotFound(w, r)


}


Pfade abbilden (3) Frank Müller
// CreateHandler care for create requests, aka POST.


type CreateHandler interface {


Create(w http.ResponseWriter, r *http.Request)


}


// ReadHandler care for read requests, aka GET.


type ReadHandler interface {


Read(w http.ResponseWriter, r *http.Request)


}


.


.


.
Methoden abbilden (1) Frank Müller
// MethodHandler maps requests to according methods.


type MethodHandler struct {


handler http.Handler


}


// NewMethodHandler wraps a handler for automatic method mapping.


func NewMethodHandler(h http.Handler) *MethodHandler {


return &MethodHandler{


handler: h,


}


}


Methoden abbilden (2) Frank Müller
// ServeHTTP implements http.Handler and cares for the mapping.


func (h *MethodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {


	
switch r.Method {


	
case http.MethodPost:


	
	
if ch, ok := h.handler.(CreateHandler); ok {


	
	
	
ch.Create(w, r)


	
	
	
return


	
	
}


case http.MethodGet:


...


}


// Fallback.


h.handler.ServeHTTP(w, r)


}
Methoden abbilden (3) Frank Müller
Teamwork
• Handler jeweils für Logik von Shopping Cards und Items


• Wrapping durch MethodHandler


• Kombination durch NestedHandler
Beispiel Shopping API (1) Frank Müller
type CardsHandler struct { ... }


// Create to handle POST requests.


func (h *CardsHandler) Create(w http.ResponseWriter, r *http.Request) { ... }


// Read to handle GET requests.


func (h *CardsHandler) Read(w http.ResponseWriter, r *http.Request) { ... }


type ItemsHandler struct { ... }


// Create to handle POST requests.


func (h *ItemsHandler) Create(w http.ResponseWriter, r *http.Request) { ... }


// Read to handle GET requests.


func (h *ItemsHandler) Read(w http.ResponseWriter, r *http.Request) { ... }
Beispiel Shopping API (2) Frank Müller
// Little helper for convenience, wrapping with logging,


// authentication/authorization, and method handling.


wrap := func(h http.Handler) http.Handler {


return httpx.NewLogHandler(httpx.NewAuthHandler(httpx.NewMethodHandler(h)))


}


// Wrap the handlers for business logic.


ch := wrap(NewCardsHandler())


ih := wrap(NewItemsHandler())


// Nest the handlers for business logic of cards and their items.


ncih := httpx.NewNestedHandler("cards", ch).Nest("items", ih)


Beispiel Shopping API (3) Frank Müller
// Use standard multiplexer for all handlers.


mux := http.NewServeMux()


mux.Handle("/cards/", ncih)


// Configure and start server using the multiplexer.


s := &http.Server{


	
Addr: ":8080",


	
Handler: mux,


	
ReadTimeout: 10 * time.Second,


	
WriteTimeout: 10 * time.Second,


	
MaxHeaderBytes: 1 << 20,


}


log.Fatal(s.ListenAndServe())
Beispiel Shopping API (4) Frank Müller
• Analyse des Pfads an einer Position für Resource Identifier


• Analyse des Content Type


• Analyse weiterer Header


• Marshal und Unmarshal direkt in oder aus dem Body
Weitere Helfer für das Package Frank Müller
• Wenig Dependencies, wenig Komplexität, leichte Erweiterbarkeit


• Wrapping ist praktisch für modulare Erweiterung


‣ Authentisierung und Autorisierung


‣ Logging


• Testing der Geschäftslogik ist losgelöst von diesen Aspekten
Vorteile Frank Müller
• Eigenes Package erfordert Vorarbeit und eigene Wartung


• Marshalling und Unmarshalling der Transportobjekte bringt
manuelle Aufwände mit sich
Nachteile Frank Müller
Entfernt aufrufen
•Remote Procedure Calls


•Bei Google entwickelt


•Nutzen in der Regel Protocol
Buffers


•Server bietet Dienste an


•Clients nutzen sie
Eigentlich nichts besonderes Frank Müller
Go Service
JS Client Java Client
gRPC


Server
gRPC


Stub
gRPC


Stub
Proto Request
Proto Request
Proto Response
Proto Responses
• Protocol Buffers beschreiben Datenstrukturen


‣ sprachunabhängig


‣ plattformunabhängig


‣ erweiterbar


• Serialisiert Daten für den Transport


•Stubs werden für Zielsprache und -plattform generiert
Grundlagen Frank Müller
• Definition in Proto-Datei


• Übertragung der Daten als Messages


• Verschiedene Felder stehen zur Verfügung


‣ Verschiedene Integers, String, Bool, Floats, Slice of Bytes


‣ Optionales, OneOf, Enumerations, Wiederholungen


‣ Default Value und Schachtelungen
Transport der Daten Frank Müller
// Message to add one item to an existing card.


message AddItemRequest {


required string card_id = 1;


required string article_no = 2;


optional group spec = 3 {


optional string size = 4;


optional string color = 5;


}


required uint64 number = 6;


}


// Message to reply to the adding of an article.


message AddItemReply { ... }
Nachrichten Frank Müller
// Service for the management of a shopping card.


service Cards {


// Create a new card.


rpc NewCard () returns (NewCardReply) {}


// Add an item.


rpc AddItem (AddItemRequest) returns (AddItemReply) {}


...


}
Services Frank Müller
Vorbereitung Frank Müller
## Installation der Plugins


$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26


$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1


## Pfad erweitern


$ export PATH="$PATH:$(go env GOPATH)/bin"


## gRPC Code generieren


$ protoc -I $SRCDIR --go_out $DSTDIR $SRCDIR/myshop.proto
// AddItem handels the AddItem call.


func (s *cardService) AddItem(


ctx context.Context, in *pb.AddItemRequest) (*pb.AddItemReply, error) {


// Use data of AddItemRequest in to add the item, e.g.


card, err := s.loadCard(in.CardId)


if err != nil {


return nil, err


}


...


// Return the reply.


return &pb.AddItemReply{


...


}, nil


}
Server (1) Frank Müller
// Start listener and GRPC server.


lis, err := net.Listen("tcp", ":10000")


if err != nil {


log.Fatalf("failed to listen: %v", err)


}


grpcServer := grpc.NewServer()


// Register card service.


myshop.RegisterCardServer(grpcServer, &cardService{})


// Start serving.


grpcServer.Serve(lis)
Server (2) Frank Müller
// Connect the server.


conn, err := grpc.Dial("localhost:10000")


if err != nil {


log.Fatalf("failed to connect the server: %v", err)


}


defer conn.Close()


// Start the client.


client := calculator.NewCardClient(conn)


// Perform requests using the created client.


reply, err := client.AddItem(context.Background(), &pb.AddItemRequest{ ... })


...
Client Frank Müller
• Statische Datenformate


• Effiziente Serialisierung


• Streaming möglich


• Connections können beibehalten werden


• Umfangreicher Stack für Security und Credentials


• Problemloser Mix verschiedener Sprachen
Vorteile Frank Müller
• Nicht für alle Sprachen verfügbar


• Proto-Dateien sind eigene Artefakte (bei Mehrsprachigkeit eher
Vorteil)


• Extra Tools notwendig
Nachteile Frank Müller
Zusammenfassung
• RESTful APIs sind etabliert und flexibel


• Sie können in vielen Sprachen leicht implementiert werden


• Unabhängigkeit von Sprachen und Plattformen


• gRPC ist ein leistungsstarker Nachfolger


• Definition in Proto-Dateien erlaubt leichteren Austausch


• Generator nicht für alle Sprachen verfügbar
Zusammenfassung Frank Müller
Bildquellen


123RF


Pexels


iStockphoto


Eigene Fotos

Weitere ähnliche Inhalte

PDF
Ein Gopher im Netz
PPT
Übersicht Skriptsprachen
PDF
Spaß an der Nebenläufigkeit
PDF
Go - Googles Sprache für skalierbare Systeme
PPT
Interprozesskommunikation mit PHP
PPT
Web 2.0 Mit Der Yahoo User Interface Library
PDF
Devs@Home - Einführung in Go
PDF
entwickler.de 05/2023: Go über den Wolken
Ein Gopher im Netz
Übersicht Skriptsprachen
Spaß an der Nebenläufigkeit
Go - Googles Sprache für skalierbare Systeme
Interprozesskommunikation mit PHP
Web 2.0 Mit Der Yahoo User Interface Library
Devs@Home - Einführung in Go
entwickler.de 05/2023: Go über den Wolken

Ähnlich wie DevOpsCon - Verteilte Entwicklung in Go (13)

PDF
Web-Services mit Go
PDF
Skalierbare Anwendungen mit Google Go
PPTX
Backend-Services mit Rust
PDF
JAX 2024: Go-über-den-Wolken und in der Cloud
PDF
Ist GraphQL das bessere REST
PPTX
Microservices mit Rust
PDF
digitalSTROM Developer Day 2011: digitalSTROM-Server-Apps
PPTX
REST: Versprechen, Wirklichkeit & Alternativen: GraphQL, GRPC, JSON RPC...
PDF
2021 OOP - Kubernetes Operatoren
PDF
Web-API Design in Java
PDF
RESTful WebServices
PDF
Entwicklung eines Konzepts und einer flexiblen Architektur für ein Framework ...
PDF
JAX 2023 - Cloud Provider APIs
Web-Services mit Go
Skalierbare Anwendungen mit Google Go
Backend-Services mit Rust
JAX 2024: Go-über-den-Wolken und in der Cloud
Ist GraphQL das bessere REST
Microservices mit Rust
digitalSTROM Developer Day 2011: digitalSTROM-Server-Apps
REST: Versprechen, Wirklichkeit & Alternativen: GraphQL, GRPC, JSON RPC...
2021 OOP - Kubernetes Operatoren
Web-API Design in Java
RESTful WebServices
Entwicklung eines Konzepts und einer flexiblen Architektur für ein Framework ...
JAX 2023 - Cloud Provider APIs
Anzeige

Mehr von Frank Müller (19)

PDF
IT-Tage 2024: Philosophie in der Software-Architektur
PDF
JAX 2023 - Generics in Go
PDF
Let The Computer Do It
PDF
Concurrency with Go
PDF
Fun with functions
PDF
Blockchains - Mehr als nur digitale Währungen
PDF
Cloud Provisioning mit Juju
PDF
Juju - Scalable Software with Google Go
PDF
RESTful Web Applications with Google Go
PDF
Clouds, leicht beherrschbar
PDF
WTC 2013 - Juju - Mit etwas Magie zur perfekten Cloud
PDF
Juju - Google Go in a scalable Environment
PDF
OOP 2013 - Weltweite Entwicklung von Open Source Software
KEY
Beauty and Power of Go
PDF
Pecha Kucha: Nebenläufigkeit als natürliches Paradigma
PDF
Go to the Cloud
KEY
On Event-Driven Architecture
KEY
Google Go - Good artists borrow, great artists steal.
KEY
Agility And The Way To SOA
IT-Tage 2024: Philosophie in der Software-Architektur
JAX 2023 - Generics in Go
Let The Computer Do It
Concurrency with Go
Fun with functions
Blockchains - Mehr als nur digitale Währungen
Cloud Provisioning mit Juju
Juju - Scalable Software with Google Go
RESTful Web Applications with Google Go
Clouds, leicht beherrschbar
WTC 2013 - Juju - Mit etwas Magie zur perfekten Cloud
Juju - Google Go in a scalable Environment
OOP 2013 - Weltweite Entwicklung von Open Source Software
Beauty and Power of Go
Pecha Kucha: Nebenläufigkeit als natürliches Paradigma
Go to the Cloud
On Event-Driven Architecture
Google Go - Good artists borrow, great artists steal.
Agility And The Way To SOA
Anzeige

DevOpsCon - Verteilte Entwicklung in Go

  • 1. Verteilte Entwicklung in Go RESTful APIs und gRPC Frank Müller
  • 2. •Oldenburg, Deutschland •1965 geboren •Kubermatic •Team Lead Development •@themue Einführung Frank Müller
  • 4. • Laufzeitumgebung sind Clouds und Cluster • Komponenten sind Microservices • Aufteilung bringt Flexibilität und Skalierbarkeit • APIs auf Basis von REST oder gRPC bringen Sprachunabhängigkeit Runtime im Netz Frank Müller
  • 6. • HTTP Bibliothek ist genug • URL bezeichnet Typ und gegebenenfalls Ressource • HTTP Methode bestimmt Aktion • Create / Read / Update / Delete ist POST / GET / PUT / DELETE • Body enthält die Daten • Content Type ist in der Regel JSON Einfache Struktur Frank Müller
  • 7. POST /api/cards/12345/items Content-Type: application/json { "articleNo": "9876", "spec": { "size": "XL", "color": "red" }, "number": 2 } Beispiel Frank Müller
  • 8. • Package net/http enthält Server, Request und Response • Es definiert den Handler und implementiert ihn als ServeMux • Nicht hinreichend, aber leicht zu ergänzen • Ein eigenes Package httpx hilft ‣ Pfade abbilden ‣ Methoden abbilden Standardbibliothek genügt beinahe Frank Müller
  • 9. // NestedHandler allows to nest handler following the // pattern /handlerA/{entityID-A}/handlerB/{entityID-B}. type NestedHandler struct { resource string handler http.Handler nested map[string]http.Handler } func NewNestedHandler(resource string, handler http.Handler) *NestedHandler { return &NestedHandler{ resource: resource, handler: handler, nested: make(map[string]http.Handler), } } Pfade abbilden (1) Frank Müller
  • 10. // Nest nests another nested handler. func (h *NestedHandler) Nest(handler *NestedHandler) *NestedHandler { h.nested[handler.resource] = handler return handler } // NestHandler nests any handler as new nested handler. func (h *NestedHandler) NestHandler( resource string, handler http.Handler) *NestedHandler { nh := NewNestedHandler(resource, handler) h.nested[resource] = nh return nh } Pfade abbilden (2) Frank Müller
  • 11. func (h *NestedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if own or one of the nested handler is addressed. eh, ok := h.analyzePath(r.URL.Path) if ok { eh.ServeHTTP(w, r) return } // Handler not found. http.NotFound(w, r) } Pfade abbilden (3) Frank Müller
  • 12. // CreateHandler care for create requests, aka POST. type CreateHandler interface { Create(w http.ResponseWriter, r *http.Request) } // ReadHandler care for read requests, aka GET. type ReadHandler interface { Read(w http.ResponseWriter, r *http.Request) } . . . Methoden abbilden (1) Frank Müller
  • 13. // MethodHandler maps requests to according methods. type MethodHandler struct { handler http.Handler } // NewMethodHandler wraps a handler for automatic method mapping. func NewMethodHandler(h http.Handler) *MethodHandler { return &MethodHandler{ handler: h, } } Methoden abbilden (2) Frank Müller
  • 14. // ServeHTTP implements http.Handler and cares for the mapping. func (h *MethodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: if ch, ok := h.handler.(CreateHandler); ok { ch.Create(w, r) return } case http.MethodGet: ... } // Fallback. h.handler.ServeHTTP(w, r) } Methoden abbilden (3) Frank Müller
  • 16. • Handler jeweils für Logik von Shopping Cards und Items • Wrapping durch MethodHandler • Kombination durch NestedHandler Beispiel Shopping API (1) Frank Müller
  • 17. type CardsHandler struct { ... } // Create to handle POST requests. func (h *CardsHandler) Create(w http.ResponseWriter, r *http.Request) { ... } // Read to handle GET requests. func (h *CardsHandler) Read(w http.ResponseWriter, r *http.Request) { ... } type ItemsHandler struct { ... } // Create to handle POST requests. func (h *ItemsHandler) Create(w http.ResponseWriter, r *http.Request) { ... } // Read to handle GET requests. func (h *ItemsHandler) Read(w http.ResponseWriter, r *http.Request) { ... } Beispiel Shopping API (2) Frank Müller
  • 18. // Little helper for convenience, wrapping with logging, // authentication/authorization, and method handling. wrap := func(h http.Handler) http.Handler { return httpx.NewLogHandler(httpx.NewAuthHandler(httpx.NewMethodHandler(h))) } // Wrap the handlers for business logic. ch := wrap(NewCardsHandler()) ih := wrap(NewItemsHandler()) // Nest the handlers for business logic of cards and their items. ncih := httpx.NewNestedHandler("cards", ch).Nest("items", ih) Beispiel Shopping API (3) Frank Müller
  • 19. // Use standard multiplexer for all handlers. mux := http.NewServeMux() mux.Handle("/cards/", ncih) // Configure and start server using the multiplexer. s := &http.Server{ Addr: ":8080", Handler: mux, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } log.Fatal(s.ListenAndServe()) Beispiel Shopping API (4) Frank Müller
  • 20. • Analyse des Pfads an einer Position für Resource Identifier • Analyse des Content Type • Analyse weiterer Header • Marshal und Unmarshal direkt in oder aus dem Body Weitere Helfer für das Package Frank Müller
  • 21. • Wenig Dependencies, wenig Komplexität, leichte Erweiterbarkeit • Wrapping ist praktisch für modulare Erweiterung ‣ Authentisierung und Autorisierung ‣ Logging • Testing der Geschäftslogik ist losgelöst von diesen Aspekten Vorteile Frank Müller
  • 22. • Eigenes Package erfordert Vorarbeit und eigene Wartung • Marshalling und Unmarshalling der Transportobjekte bringt manuelle Aufwände mit sich Nachteile Frank Müller
  • 24. •Remote Procedure Calls •Bei Google entwickelt •Nutzen in der Regel Protocol Buffers •Server bietet Dienste an •Clients nutzen sie Eigentlich nichts besonderes Frank Müller Go Service JS Client Java Client gRPC Server gRPC Stub gRPC Stub Proto Request Proto Request Proto Response Proto Responses
  • 25. • Protocol Buffers beschreiben Datenstrukturen ‣ sprachunabhängig ‣ plattformunabhängig ‣ erweiterbar • Serialisiert Daten für den Transport •Stubs werden für Zielsprache und -plattform generiert Grundlagen Frank Müller
  • 26. • Definition in Proto-Datei • Übertragung der Daten als Messages • Verschiedene Felder stehen zur Verfügung ‣ Verschiedene Integers, String, Bool, Floats, Slice of Bytes ‣ Optionales, OneOf, Enumerations, Wiederholungen ‣ Default Value und Schachtelungen Transport der Daten Frank Müller
  • 27. // Message to add one item to an existing card. message AddItemRequest { required string card_id = 1; required string article_no = 2; optional group spec = 3 { optional string size = 4; optional string color = 5; } required uint64 number = 6; } // Message to reply to the adding of an article. message AddItemReply { ... } Nachrichten Frank Müller
  • 28. // Service for the management of a shopping card. service Cards { // Create a new card. rpc NewCard () returns (NewCardReply) {} // Add an item. rpc AddItem (AddItemRequest) returns (AddItemReply) {} ... } Services Frank Müller
  • 29. Vorbereitung Frank Müller ## Installation der Plugins $ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26 $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1 ## Pfad erweitern $ export PATH="$PATH:$(go env GOPATH)/bin" ## gRPC Code generieren $ protoc -I $SRCDIR --go_out $DSTDIR $SRCDIR/myshop.proto
  • 30. // AddItem handels the AddItem call. func (s *cardService) AddItem( ctx context.Context, in *pb.AddItemRequest) (*pb.AddItemReply, error) { // Use data of AddItemRequest in to add the item, e.g. card, err := s.loadCard(in.CardId) if err != nil { return nil, err } ... // Return the reply. return &pb.AddItemReply{ ... }, nil } Server (1) Frank Müller
  • 31. // Start listener and GRPC server. lis, err := net.Listen("tcp", ":10000") if err != nil { log.Fatalf("failed to listen: %v", err) } grpcServer := grpc.NewServer() // Register card service. myshop.RegisterCardServer(grpcServer, &cardService{}) // Start serving. grpcServer.Serve(lis) Server (2) Frank Müller
  • 32. // Connect the server. conn, err := grpc.Dial("localhost:10000") if err != nil { log.Fatalf("failed to connect the server: %v", err) } defer conn.Close() 
 // Start the client. client := calculator.NewCardClient(conn) // Perform requests using the created client. reply, err := client.AddItem(context.Background(), &pb.AddItemRequest{ ... }) ... Client Frank Müller
  • 33. • Statische Datenformate • Effiziente Serialisierung • Streaming möglich • Connections können beibehalten werden • Umfangreicher Stack für Security und Credentials • Problemloser Mix verschiedener Sprachen Vorteile Frank Müller
  • 34. • Nicht für alle Sprachen verfügbar • Proto-Dateien sind eigene Artefakte (bei Mehrsprachigkeit eher Vorteil) • Extra Tools notwendig Nachteile Frank Müller
  • 36. • RESTful APIs sind etabliert und flexibel • Sie können in vielen Sprachen leicht implementiert werden • Unabhängigkeit von Sprachen und Plattformen • gRPC ist ein leistungsstarker Nachfolger • Definition in Proto-Dateien erlaubt leichteren Austausch • Generator nicht für alle Sprachen verfügbar Zusammenfassung Frank Müller