Skalierbare Anwendungen mit Google Go

Preview:

DESCRIPTION

Vortrag der OOP 2014 Überblick über die Vorteile der Programmiersprache Go für skalierbare Anwendungen sowie ein Einblick in hierbei zu beachtende Probleme und ihre Lösung.

Citation preview

Skalierbare Anwendungen mit Google Go

Frank Müller

Oldenburg Baujahr 1965

Entwickler Fachautor

!

frank@mweb.de @themue

Moderne Mehrkernsysteme sind wie dieVereinigung von Flüssen

Ihre Energie gilt eseffizient zu nutzen

Hier bietet sich Google Go an

Google Go als multikulturelle Sprache

imperativ

bedingt funktional

bedingt objektorientiert

nebenläufig

❝It’s better to have a permanent income than to be fascinating.

–Oscar Wilde

Nebenläufigkeit in Go

• Leichtgewichtige Goroutines im Thread Pool

• Sehr große Anzahl gleichzeitig möglich

• Kommunikation und Synchronisation über Channels

• Vielfältige Kontrolle durch select Statement

Beispiel: Goroutines als Server

Client

Client

Client

Server

Beispiel: Lastverteilung

Worker

Worker

Master

Client

Client Worker

Netze von Goroutines

B

A

F

E 2E 1

E

E 3

C D

G

Ein Wimmeln wieim Bienenstock

Kapselung in Typen

package service !type Service struct { thisChan chan *This thatChan chan *That foo bool bar int baz string }

Konstruktoren sind Funktionen

func NewService(...) *Service { s := &Service{ thisChan: make(chan *This), thatChan: make(chan *That, 10), …, } ! // Start of the backend loop as goroutine. go s.loop() ! return s }

Endlosschleifen und select

func (s *Service) loop() { ticker := time.NewTicker(time.Second) for { select { case this := <-s.thisChan: s.doThis(this) case that := <-s.thatChan: s.doThat(that) case <-ticker.C: s.doTick() } } }

Methoden als externe Schnittstellen

func (s *Service) DoThis(data string) int { respChan := make(chan int) this := &This{data, respChan} ! s.thisChan <- this return <-respChan }

Goroutines können eigene Wege gehen …

… oder gemeinsame

Herausforderungen nebenläufiger Anwendungen

Rechenleistung nutzen

Rennen kontrollieren

Konflikte vermeiden

Probleme abfedern

Sehr naive Parallelisierung (1)

func process(in []int) []int { resultChan := make(chan int) for _, value := range in { // One goroutine per value. go processValue(value, resultChan) } out := make([]int, 0) for i := 0; i < len(in); i++ { out = append(out, <-resultChan) } return out }

Sehr naive Parallelisierung (2)

func processValue( inValue int, resultChan chan int) { // Time of result calculation depends // on value. outValue := inValue … ! resultChan <- outValue }

Verbesserung

• Index in der Verarbeitung mitführen

• Output mit richtiger Größe initialisieren

• Ergebnis gemäß Index setzen

Verbesserung (1)

func process(in []int) []int { resultChan := make(chan *result) for index, value := range in { // One goroutine per value. go processValue(index, value, resultChan) } out := make([]int, len(in)) for i := 0; i < len(in); i++ { result <- resultChan out[result.index] = result.value } return out }

Verbesserung (2)type result struct { index int value int } !func processValue( index, inValue int, resultChan chan *result) { // Time of result calculation depends // on value. outValue := inValue … ! // Send index with result. resultChan <- &result{index, outValue} }

Isolierung von Zugriffen

• Loops serialisieren Zugriffe

• Synchronisation über Channels oder Package sync

• Referenzen nur an eine Goroutine senden

• Aufteilung von Arrays und Slices

Unabhängigkeit von Goroutines

• Keine Beziehung zwischen Goroutines

• Kontrolle nur über Channels

• Externe Packages helfen

• git.tideland.biz/goas/loop

• launchpad.net/tomb

Schleifenfunktion

func (t *MyType) backendLoop(l loop.Loop) error { for { select { case <-l.ShallStop(): return nil case foo := <-t.fooChan: if err := t.processFoo(foo); err != nil { return err } case …: … } } }

Kontrolle der Schleifenfunktion

// Start of the goroutine in constructor. t.loop = loop.Go(t.backendLoop) !// Tell loop to stop. err := t.loop.Stop() !// Kill loop with passed error and wait. t.loop.Kill(myError) err := t.loop.Wait() !// Retrieve status and possible error. status, err := t.loop.Error()

Deadlocks vermeiden (Timeout)

func (t *MyType) Foo(foo *Foo) error { select { case t.fooChan <- foo: case <-time.After(timeout): return errors.New("timed out") } return nil }

Deadlocks vermeiden (Loop)

func (t *MyType) Foo(foo *Foo) error { select { case t.fooChan <- foo: case <-t.loop.IsStopping(): return errors.New("not running anymore") } return nil }

Vernetzung

Hilfreiche Packages

• net, …/http für IP und Web

• html/template, mime/… für Inhalte

• encoding/gob, …/json, …/xml für die Serialisierung

• compress/… zur Komprimierung

• crypto/… zur Verschlüsselung

Externe Packages von Google

• WebSocket, SPDY, Dict und mehr in code.google.com/p/go.net

• Weitere Crypto Packages (u.a. OpenPGP) in code.google.com/p/go.crypto

Serialisierung via JSON

type OrderItem struct { Id int `json:"ArticleNo"` Quantity int } !type Order struct { Name string Street string City string Remarks string `json:",omitempty"` Items []OrderItem }

Marshalling

order := Order{ Name: "Foo Bar", … Items: []OrderItem{{4711, 1}, {4712, 5}, …}, } !marshalledOrder, err := json.Marshal(order) if err != nil { … } !n, err := anyWriter.Write(marshalledOrder)

Unmarshalling

var order Order !err := json.Unmarshal(marshalledOrder, &order) if err != nil { … } !fmt.Printf("Name: %s\n", order.Name)

Fallbeispiel einer API

Netz und Crypto

Reflection für Dispatching

WebSockets

Serialisierung via JSON

❝Zitat hier eingeben.

–Christian BauerBildquellen 123RFiStockphotoeigene Quellen

Recommended