Upload
frank-mueller
View
361
Download
0
Embed Size (px)
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
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