Go - Googles Sprache für skalierbare Systeme

73
Go - Googles Sprache für skalierbare Systeme

Transcript of Go - Googles Sprache für skalierbare Systeme

Go - Googles Sprachefür skalierbare Systeme

Frank Müller

OldenburgBaujahr 1965

EntwicklerFachautor

[email protected]@themue

Parallelität oder Nebenläufigkeit?

❝ Parallelism Programming as the simultaneous

execution of (possibly related) computations.

Concurrency Programming as the composition

of independently executing processes.

Rob Pike

Sieh, wie wir dies nutzen ...

Bereitgestellte Infrastruktur ...

... ist nichts ohne bereitgestellte Dienste

mysql

wordpress

Bekanntes Szenario

auf individuellen

Servern

Höhere Komplexität in Clouds

wordpress

mediawikihaproxy-w mediawiki memcached

haproxy-b wordpress mysql

wordpressvarnish

Ein mühsamer Weg

Juju - Open Source Tool zur Automatisierung von Clouds

http://jujucharms.com

Plattformen

Plattformen

mediawiki

wordpress

AWS Azure OpenStack Joyent ...

haproxy-w mediawiki memcached

haproxy-b wordpress mysql

wordpressvarnish

juju generate-config juju bootstrap

Bootstrap

Bootstrap

API

State

Provisioner

...

Zentrale Funktionen

Provisionierung

juju deploy juju-gui juju deploy wordpress juju deploy mysql juju expose juju-gui

Bootstrap

juju-gui wordpress mysql

Relationen

juju add-relation wordpress mysql juju expose wordpress

Bootstrap

juju-gui wordpress mysql

Konfiguration

juju set mysql dataset-size=50% juju set wordpress tuning=optimized

Bootstrap

juju-gui wordpress* mysql*

Skalieren

juju deploy memcached juju add-relation wordpress memcached juju add-unit wordpress

wordpress*

Bootstrap

juju-gui wordpress* mysql*

memcached

Varianten

Unit

Unit

Unter- geordnete

Unit

Unit

Unit

Unit

ContainerDirekte AbhängigkeitStandard

Einige Charms

ceph

mediawiki mongodb

cassandra

rails

mysql

wordpress

rabbitmq

haproxy

apache2

hadoopsquid

hbase

couchdb

postgresqlnfsntp

Viel Spaß in den Wolken

Wie hilft uns Google Go hier?

Moderne Mehrkernsysteme sind wie die Vereinigung von Flüssen

Ihre Energie gilt eseffizient zu nutzen

Google Go als Multi-Paradigmen-Sprache

imperativ

bedingt funktional

bedingt objektorientiert

nebenläufig

❝It’s better to have apermanent income

than to befascinating.

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

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 }

Beispiel: Goroutines als Server

Client

Client

Client

Server

Beispiel: Lastverteilung

Client

Client

Worker

Worker

Worker

Master

Netze von Goroutines

A C D

GFEB

E1 E2 E3

Goroutines können eigene Wegegehen …

… oder gemeinsame

Ein Wimmeln wie im Bienenstock

Einblicke in die Juju Architektur

State - Watcher - Worker

State

API Networker

Provisioner

Uniter

Deployer

...

!

Collections Individuelle Objekte

Lebenszyklen

Client

Lauter nebenläufige Arbeit

Agent

State

Worker

Goroutine

Goroutine

Worker

Goroutine

Goroutine

Worker

Goroutine

Goroutine

Beispiel Firewaller

unitd Loop

unitd Loop

machined Loop

State Main Loop

machined Loop

unitd Loop

serviced Loop

Environment Configuration

Machines

Machine Units

Exposes

Ports

Herausforderungen nebenläufiger Anwendungen

Rechenleistung nutzen

Rennen kontrollieren

Konflikte vermeiden

Probleme abfedern

Sehr naives Parallelisieren (1)

func process(in []int) []int { resultChan := make(chan int) for _, value := range in { // One goroutine per value. go processValue(value, resultChan) }

// Collecting the results. out := make([]int, len(in)) for i := 0; i < len(in); i++ { out[i] = <-resultChan } return out }

Sehr naives Parallelisieren (2)

func processValue( inValue int, resultChan chan int) { // Time of result calculation may vary. outValue := inValue …

resultChan <- outValue }

Problem und Verbesserung

• Unterschiedliche Laufzeiten führen zu falscher Indizierung im Ergebnis

• Index in der Verarbeitung mitführen

• 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 may vary. 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

Arbeitsteilung (1)

// Process all data passed by reference. func process(ds []*Data) {

var wg sync.WaitGroup for i := 0; i < len(ds); i += chunkSize {

// Process one chunk in the background. go processChunk(ds[i:i+chunkSize], wg)

} // Wait for all processing to complete. wg.Wait()

}

Arbeitsteilung (2)

// Processing a chunk of data passed by reference. func processChunk(ds []*Data, wg sync.WaitGroup) {

// Increment WaitGroup counter and signal when done. wg.Add(1) defer wg.Done() // Process each data. for _, d := range ds {

if d != nil { // Direct manipulation. d.Foo = ... ...

} }

}

Kontrolle von Goroutines

• Keine direkte Beziehung zwischen Goroutines

• Kontrolle nur über Channels

• Externe Packages helfen

• gopkg.in/tomb.v2

• github.com/tideland/goas/v2/loop

Operation in einer Schleifefunc (t *T) loop() {

defer t.tomb.Done() for {

select { case <-t.tomb.Dying():

// Cleanup ... return

case f := <-t.fooChan: if err := t.foo(f); err != nil {

t.tomb.Kill(err) }

case b := <-t.barChan: // ...

} }

}

Steuerung von außen

// Stop ends the main loop. func (t *T) Stop() error {

// Kill(nil) means no error as reason. t.tomb.Kill(nil) return t.tomb.Wait()

}

// Err retrieves the error in case the backend loop died. func (t *T) Err() error {

return t.tomb.Err() }

Prüfung des Backends

func (t *T) Foo(foo *Foo) (*Bar, error) { env := &fooEnv{foo, make(chan *Bar)} select { case t.fooChan <- env: case <-t.tomb.Dead():

return nil, errors.New("backend dead") } select { case bar := <- env.barChan:

return bar, nil case <-t.tomb.Dead():

return nil, errors.New("backend dead") }

}

Schnittstellen

Flexible Nutzung

• Nur Deklaration

• Einbettung in umfassendere Interfaces

• Implizites Duck Typing

• Anwendbar auf jede Typdefination

Verhalten definieren und kombinieren

type StorageReader interface { Get(name string) (io.ReadCloser, error) List(prefix string) ([]string, error) URL(name string) (string, error)

}

type StorageWriter interface { Put(name string, r io.Reader, length int64) error Remove(name string) error

}

type Storage interface { StorageReader StorageWriter

}

Teilnutzung möglich

// Only the writing is needed. func Write(sw StorageWriter) error {

... return sw.Put(myName, myReader, myLen)

}

// Full storage access here. func RemoveFirst(s Storage, prefix string) error {

l, err := s.List(prefix) if err != nil { return err } return s.Remove(l[0])

}

Implementierung pro Provider

Environ

Storage Azure

AWS

MAAS

OpenStack

...

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 golang.org/x/net

• Weitere Crypto Packages (u.a. OpenPGP) in golang.org/x/crypto

Serialisierung via JSON

type Parameters map[string]interface{}

type Action struct { Tag string `json:"tag"` Receiver string `json:"receiver"` Name string `json:"name"` Parameters Parameters `json:"parameters,omitempty"` }

Marshalling

action := Action{ Tag: "FooBar", … Paramaters: Parameters{"foo": 4711, "bar": true}, }

marshalledAction, err := json.Marshal(action) if err != nil { … }

n, err := anyWriter.Write(marshalledAction)

Unmarshalling

var action Action

err := json.Unmarshal(marshalledAction, &action) if err != nil { … }

fmt.Printf("Tag: %s\n", action.Tag)

API in Juju

Netz und Crypto

Reflection für Dispatching

WebSockets

Serialisierung via JSON

Bildquellen 123RFiStockphotoeigene Quellen