2. Baujahr 1965
Wohnhaft in Oldenburg
Software-Entwicklung seit
mehr als 25 Jahren
Fachautor seit 1999 http://www.dpunkt.de/googlego
Frank Müller Frank Müller - Go to the Cloud 2
Frank Müller — Go to the Cloud — 2/49
3. Jobs
Message Queues
Caching
Datenbank
Web Server
Mail
Betriebssystem
Redundanz
Hardware Load Balancer
Typische Systeme heute
Frank Müller — Go to the Cloud — 3/49
5. Platform as a Service
Basis für eigene Web-Anwendungen
Bereitstellung von Diensten
Transparente Skalierung
Einfaches Deployment und Monitoring
Google App Engine • Eigenschaften
Frank Müller — Go to the Cloud — 5/49
6. 2008 Vorstellung für Python
2009 Erweiterung um Java
2011 Integration von Go
Aktuell Version 1.6.x
Go noch mit Status „experimentell“
Google App Engine • Historie
Frank Müller — Go to the Cloud — 6/49
7. Verarbeitung von HTTP Requests
Asynchrone Datenverarbeitung
Langlaufende Jobs
Chronologische Jobs
Google App Engine • Services für Go I
Frank Müller — Go to the Cloud — 7/49
8. Schemalose Datenbank
Zugriff über Schlüssel und Abfragen
Keine Relationen, dafür Schlüsseltypen
Speicher für BLOBs
Memcache
Google App Engine • Services für Go II
Frank Müller — Go to the Cloud — 8/49
9. Nutzung von Google-Konten möglich
Mail
Persistente Verbindungen zum Client
Skalierbare Zugriffe auf URLs
Google App Engine • Services für Go III
Frank Müller — Go to the Cloud — 9/49
12. Ken Thompson
●
Multics, Unix, B, Plan 9, ed, UTF-8
●
Turing Award
Robe Pike
●
Unix, Plan 9, Inferno, acme, Limbo,
UTF-8
Google Go • Bekannte Väter
Frank Müller — Go to the Cloud — 12/49
14. package main
import (
"fmt"
)
func greeting(greeting, name string) {
fmt.Printf("%s, %s!n", greeting, name)
}
func main() {
hello := "Hello"
world := "World"
greeting(hello, world)
}
Google Go • Einfache Struktur
Frank Müller — Go to the Cloud — 14/49
16. type Door struct { ... }
func NewDoor(width, height float64) *Door { ... }
func (d *Door) Open() { ... }
func (d *Door) Close() { ... }
func (d Door) IsOpen() bool { ... }
func (d Door) Size() (float64, float64) { ... }
func (d Door) String() string { ... }
Google Go • Kein OO, aber Methoden
Frank Müller — Go to the Cloud — 16/49
17. type TypeStringer interface {
TypeString() string
}
type Foo int64
type Bar struct { id string }
func (f Foo) TypeString() string {
return "I'm a Foo (int64)."
}
func (b Bar) TypeString() string {
return "I'm a Bar (struct)."
}
func TypePrint(ts TypeStringer) { ... }
Google Go • Polymorphie über Schnittstellen
Frank Müller — Go to the Cloud — 17/49
18. Goroutinen leichtgewichtiger als
Threads
Threadpools für Goroutinen
Kommunikation über typisierte
Channels
Channels nicht an Goroutinen
gebunden
Google Go • Nebenläufigkeit
Frank Müller — Go to the Cloud — 18/49
19. type Resource interface {
...
}
type job struct {
jobFunc JobFunc
resultChan ResultChan
}
type JobFunc func(r Resource) *Result
type ResultChan chan *Result
type Result struct {
Payload interface{}
Error os.Error
}
Google Go • Einfache Nebenläufigkeit I
Frank Müller — Go to the Cloud — 19/49
20. type Manager struct {
resource Resource
jobChan chan *job
}
func NewManager(r Resource) *Manager {
m := &Manager{r, make(chan *job)}
go m.backend()
return m
}
func (m *Manager) backend() {
for j := range m.jobChan {
j.resultChan <- j.jobFunc(m.resource)
}
}
Google Go • Einfache Nebenläufigkeit II
Frank Müller — Go to the Cloud — 20/49
21. func (m Manager) Perform(jf JobFunc) ResultChan {
j := &job{jf, make(ResultChan)}
go func() {
m.jobChan <- j
}()
return j.resultChan
}
func (m Manager) Stop() {
close(m.jobChan)
}
Google Go • Einfache Nebenläufigkeit III
Frank Müller — Go to the Cloud — 21/49
22. func AnyFunc(m Manager) (*AnyResult, os.Error) {
rc := m.Perform(func(r Resource) *Result {
r.DoThis()
r.DoThat()
return &Result{..., nil}
})
... // Do something else.
result := <-rc
if result.Error != nil {
return nil, result.Error
}
return result.Payload.(*AnyResult), nil
}
Google Go • Einfache Nebenläufigkeit IV
Frank Müller — Go to the Cloud — 22/49
23. Umfangreiche Standardbibliothek
●
Netzwerk
●
Crypto
●
Zeichenketten
●
Marshalling und Encoding
●
I/O
●
Kompression
●
Bildverarbeitung
●
Reflection
Google Go • Organisation in Packages
Frank Müller — Go to the Cloud — 23/49
26. Come Together • Go und die App Engine
Frank Müller — Go to the Cloud — 26/49
27. package oop
import (
"fmt"
"http"
. "appengine"
)
func init() {
http.HandleFunc("/", greeting)
}
func greeting(rw http.ResponseWriter, r *http.Request) {
fmt.Fprintf(rw, "Hello, OOP 2012!")
}
Come Together • HTTP Requests
Frank Müller — Go to the Cloud — 27/49
28. type Address struct {
...
}
func address(rw http.ResponseWriter, r *http.Request) {
ctx := NewContext(r)
r.ParseForm()
id := r.FormValue("id")
addr := ReadAddressById(ctx, id)
if b, err := json.Marshal(addr); err == nil {
rw.Header().Set("Content-Type", "application/json")
rw.Write(b)
}
}
Come Together • Rückgabe von JSON
Frank Müller — Go to the Cloud — 28/49
29. Structs (Public / Annotation)
int(*), bool, string, float(*), []byte
Typen auf Basis dieser Typen
Eigene Typen für Zeit, *Key, BlobKey
Slices dieser Typen
Come Together • Nutzung des Datastores
Frank Müller — Go to the Cloud — 29/49
30. type Address struct {
Id string
Street string
City string
Country string
PhotoKey appengine.BlobKey
}
type Customer struct {
Id string `datastore:"CustomerId"`
Name string
AddressKey *datastore.Key
OrderKeys []*datastore.Key
LastOrder datastore.Time
Turnover Money
AvgTurnover Money `datastore:"-"`
}
Come Together • Beispielstrukturen
Frank Müller — Go to the Cloud — 30/49
31. func StoreCustomerAddress(ctx Context, c *Customer,
a *Address) os.Error {
c.AddressKey = datastore.NewKey(ctx, "Address", a.Id,
0, nil)
if _, err := datastore.Put(ctx, c.AddressKey, a);
err != nil {
return err
}
kc := datastore.NewKey(ctx, "Customer", c.Id, 0, nil)
_, err := datastore.Put(ctx, kc, c)
return err
}
Come Together • Daten speichern
Frank Müller — Go to the Cloud — 31/49
32. func ReadCustomerById(ctx Context, id string)
(*Customer, *Address, os.Error) {
k := datastore.NewKey(ctx, "Customer", id, 0, nil)
c := new(Customer)
a := new(Address)
if err := datastore.Get(ctx, k, c); err != nil {
return nil, nil, err
}
if err := datastore.Get(ctx, c.AddressKey, a);
err != nil {
return nil, nil, err
}
return c, a, nil
}
Come Together • Daten lesen
Frank Müller — Go to the Cloud — 32/49
33. func Book(ctx Context, fromAcc, toAcc string,
amount Money) os.Error {
b := func(c Context) os.Error {
if err := subtractAmount(c, fromAcc, amount);
err != nil {
return err
}
return addAmount(c, toAcc, amount)
}
return datastore.RunInTransaction(ctx, b, nil)
}
Come Together • Transaktionen
Frank Müller — Go to the Cloud — 33/49
34. Abfragetyp mit Filtern und Sortierung
Begrenzt auf eine Entität
Rückgabe der Daten oder Schlüssel
x
Abfrage der Anzahl
Alle Daten, Limitierung oder Iterator
Come Together • Abfragemöglichkeiten
Frank Müller — Go to the Cloud — 34/49
35. func QueryByTurnover(ctx Context,
turnover Money, limit int) ([]*Customer, os.Error) {
q := datastore.NewQuery("Customer").
Filter("Turnover >=", turnover).
Order("-Turnover").
Limit(limit)
cs := make([]*Customer, 0)
if _, err := q.GetAll(ctx, &cs); err != nil {
return nil, err
}
return ts, nil
}
Come Together • Daten abfragen
Frank Müller — Go to the Cloud — 35/49
36. Verteilter Puffer im Hauptspeicher
Byte Slices und serialisierte Objekte
Ablauf kann festgelegt werden
Statistik zur Analyse
Come Together • Caching
Frank Müller — Go to the Cloud — 36/49
37. func AllTagsCached(ctx Context) ([]*Tags, os.Error) {
ts := make([]*Tags, 0)
_, err := memcache.Gob.Get(ctx, "tags", &ts)
switch err {
case memcache.ErrCacheMiss:
if ts, err = cacheTags(ctx); err != nil {
return nil, err
}
return ts, nil
case nil:
return ts, nil
}
return nil, err
}
Come Together • Cache lesen
Frank Müller — Go to the Cloud — 37/49
38. func cacheTags(ctx Context) ([]*Tags, os.Error) {
if ts, err := ReadAllTags(ctx); err != nil {
return nil, err
}
ci := &memcache.Item{
Key: "tags",
Object: ts,
Expiration: 60,
}
if err = memcache.Gob.Set(ctx, ci); err != nil {
return nil, err
}
return ts, nil
}
Come Together • Cache schreiben
Frank Müller — Go to the Cloud — 38/49
39. HTTP Requests ohne Antwort
Längere Verarbeitung möglich
Mehrere Warteschlangen
Versionierung der Tasks
Come Together • Task Queues
Frank Müller — Go to the Cloud — 39/49
40. func AsyncStoreDoc(ctx Context, d *Document) os.Error {
if b, err := json.Marshall(d); err != nil {
return err
}
task := &taskqueue.Task{
Path: "/queues/store-doc",
Payload: b,
Header: make(http.Header),
}
task.Header.Set("Content-Type", "application/json")
_, err = taskqueue.Add(ctx, task, "store-doc")
return err
}
Come Together • Task einstellen
Frank Müller — Go to the Cloud — 40/49
41. func storeDoc(rw http.ResponseWriter, r *http.Request) {
var d Document
ctx := NewContext(r)
b, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
ctx.Errorf("Can't read request body: %v", err)
} else if err = json.Unmarshal(b, &d); err != nil {
ctx.Errorf("Can't unmarshal document: %v", err)
} else if err = StoreDoc(ctx, d); err != nil {
ctx.Errorf("Can't store document: %v", err)
}
}
Come Together • Task verarbeiten
Frank Müller — Go to the Cloud — 41/49
42. var delayedFunc = delay.Func("keyForFunc", myDelayedFunc)
func myDelayedFunc(ctx Context, ...) {
...
}
func doSomething(rw http.ResponseWriter, r *http.Request) {
ctx := NewContext(r)
...
delayedFunc.Call(ctx, arg1, arg2, ...)
}
Come Together • Tasks ganz einfach
Frank Müller — Go to the Cloud — 42/49
43. func hello(rw http.ResponseWriter, r *http.Request) {
ctx := NewContext(r)
u := user.Current(ctx)
if u == nil {
url := user.LoginURL(ctx, "/")
fmt.Fprintf(rw, `<a href="%s">Login</a>`, url)
return
}
url := user.LogoutURL(ctx, "/")
fmt.Fprintf(rw, `<a href="%s">Logout</a>`, url)
}
Come Together • Google Accounts nutzen
Frank Müller — Go to the Cloud — 43/49
44. func fetcher(rw http.ResponseWriter, r *http.Request) {
ctx := NewContext(r)
client := urlfetch.Client(ctx)
resp, err := client.Get("http://...")
if err != nil {
http.Error(rw, err.String(),
http.StatusInternalServerError)
return
}
...
}
Come Together • Ressourcen via HTTP laden
Frank Müller — Go to the Cloud — 44/49
45. func mailer(rw http.ResponseWriter, r *http.Request) {
ctx := NewContext(r)
msg := &mail.Message{
Sender: "My App <my.app@mydomain.de>",
To: "receiver@anydomain.de",
Subject: "Thank you for your feedback",
Body: feedbackText,
}
if err := mail.Send(ctx, msg); err != nil {
http.Error(rw, err.String(),
http.StatusInternalServerError)
return
}
}
Come Together • Mails senden
Frank Müller — Go to the Cloud — 45/49
47. Schneller Einstieg
Einfache und leistungsfähige API
Flexible Services
Produktive Programmiersprache
Transparente Skalierung
Fazit • Vorteile
Frank Müller — Go to the Cloud — 47/49
48. Weniger flexibel als IaaS, Rackspace
oder Hosting
Einschränkungen in den Services und
der API
Bindung an die Plattform im Code
Fazit • Nachteile
Frank Müller — Go to the Cloud — 48/49