Dies ist eine PPT, die Sameer Ajmani, Technical Lead Manager des Go-Teams von Google, geteilt hat und die Java-Programmierern einen schnellen Einstieg in Go ermöglichen soll.

为 Java 程序员准备的 Go 语言入门 PPT

Video

Diese PPT wurde am 23. April 2015 beiNYJavaSIGeingesetzt.

Gehen Sie zu YouTube, um das Video anzusehen

Hauptinhalte

1. Was ist Go, und wer verwendet Go?
2. Go und Java im Vergleich
3. Codebeispiele
4. Nebenläufigkeit
5. Werkzeuge

Was ist Go?

"Go ist eine Open-Source-Programmiersprache, mit der sich einfache, zuverlässige und effiziente Software leicht erstellen lässt."

golang.org

Geschichte von Go

Design begann in der zweiten Hälfte des Jahres 2007

  • Robert Griesemer, Rob Pike und Ken Thompson.
  • Ian Lance Taylor und Russ Cox

Seit 2009 als Open Source verfügbar, mit einer sehr aktiven Community.

Die stabile Version Go 1 wurde Anfang 2012 veröffentlicht.

Warum Go?

Go ist eine Lösung für die Skalierung bei Google.

Systemskalierung

  • Skalierung für 10⁶⁺ Maschinen geplant
  • Jobs laufen jeden Tag auf Tausenden von Maschinen
  • Kooperiert und interagiert mit anderen Jobs im System
  • Eine Menge Arbeit findet gleichzeitig statt

Lösung: sehr starke Unterstützung für Nebenläufigkeit

Zweites Problem: Skalierung im Engineering

Im Jahr 2011

  • 5000+ Entwickler in 40+ Büros
  • 20+ Änderungen pro Minute
  • 50% der Codebasis wird jeden Monat geändert
  • 50 Millionen Testfälle werden pro Tag ausgeführt
  • Ein einzelner Codebaum

Lösung: eine Sprache, die für große Codebasen entwickelt wurde

Wer verwendet Go bei Google?

Viele Projekte, Tausende Go-Programmierer und Millionen Zeilen Go-Code.

Öffentliche Beispiele:

  • Chrome-SPDY-Proxy für mobile Geräte
  • Download-Server für Chrome, ChromeOS, Android SDK, Earth usw.
  • YouTube-Vitess-MySQL-Balancer

Seine Hauptaufgaben sind Webserver, aber es ist eine Allzweck-Programmiersprache.

Wer außer Google verwendet Go noch?

golang.org/wiki/GoUsers

Apcera, Bitbucket, bitly, Canonical, CloudFlare, Core OS, Digital Ocean, Docker, Dropbox, Facebook, Getty Images, GitHub, Heroku, Iron.io, Kubernetes, Medium, MongoDB-Dienste, Mozilla-Dienste, New York Times, pool.ntp.org, Secret, SmugMug, SoundCloud, Stripe, Square, Thomson Reuters, Tumblr, ...

Vergleich von Go und Java

Go und Java haben viel gemeinsam

  • C-Familie (stark typisiert, geschweifte Klammern)
  • Statische Typisierung
  • Speicherbereinigung
  • Speichersicherheit (nil-Referenzen, Laufzeit-Grenzprüfungen)
  • Variablen werden immer initialisiert (zero/nil/false)
  • Methoden
  • Interfaces
  • Typzusicherungen (Instanzen)
  • Reflexion

Unterschiede zwischen Go und Java

  • Programme werden direkt in Maschinencode kompiliert, keine VM
  • Statisch gelinkte Binärdateien
  • Kontrolle über das Speicherlayout
  • Funktionswerte und lexikalische Closures
  • Integrierte Strings (UTF-8)
  • Integrierte generische Maps und Arrays/Slices
  • Integrierte Nebenläufigkeit

Go lässt bewusst eine Reihe von Features weg

  • Keine Klassen
  • Keine Konstruktoren
  • Keine Vererbung
  • Kein final
  • Keine Ausnahmen
  • Keine Annotationen
  • Keine benutzerdefinierten Generics

Warum verzichtet Go auf diese Funktionen?

Die Klarheit des Codes ist oberstes Gebot

Beim Lesen von Code erkennt man klar, was das Programm tun wird

Beim Schreiben von Code kann man das Programm auch klar dazu bringen, das zu tun, was man möchte

Manchmal bedeutet das, eine Schleife zu schreiben, anstatt eine obskure Funktion aufzurufen.

(Nicht zu langweilig werden)

Für detaillierten Designhintergrund siehe:

Beispiele

Java-Programmierer sollten Go vertraut finden

Main.java

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

hello.go

package main
import "fmt"
func main() {
    fmt.Println("Hello, 世界!")
}

Hallo, Webserver

package main

import (
    "fmt"
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/hello", handleHello)
    fmt.Println("serving on http://localhost:7777/hello")
    log.Fatal(http.ListenAndServe("localhost:7777", nil))
}
func handleHello(w http.ResponseWriter, req *http.Request) {
    log.Println("serving", req.URL)
    fmt.Fprintln(w, "Hello, 世界!")
}

(Zugriff) Typ wird anhand des Variablennamens deklariert.
Öffentliche Variablennamen beginnen mit einem Großbuchstaben, private Variablen beginnen mit einem Kleinbuchstaben.

Beispiel: GoogleFrontend


func main() {
    http.HandleFunc("/search", handleSearch)
    fmt.Println("serving on http://localhost:8080/search")
    log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
// handleSearch handles URLs like "/search?q=golang" by running a
// Google search for "golang" and writing the results as HTML to w.
func handleSearch(w http.ResponseWriter, req *http.Request) {

Anfragevalidierung

func handleSearch(w http.ResponseWriter, req *http.Request) {
    log.Println("serving", req.URL)
    // Check the search query.
    query := req.FormValue("q")
    if query == "" {
        http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest)
        return
    }

FormValue ist eine Methode auf *http.Request:

package http
type Request struct {...}
func (r *Request) FormValue(key string) string {...}

`query := req.FormValue("q")` initialisiert die Variable query, deren Typ das Ergebnis des Ausdrucks auf der rechten Seite ist, hier der Typ string.

Suchergebnisse abrufen

// Run the Google search.
    start := time.Now()
    results, err := Search(query)
    elapsed := time.Since(start)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

Die Methode Search gibt zwei Werte zurück: results und error.

func Search(query string) ([]Result, error) {...}

Wenn der Wert von error nil ist, sind results gültig.

type error interface {
    Error() string // a useful human-readable error message
}

Der Typ Error kann zusätzliche Informationen enthalten, auf die über eine Typassertion zugegriffen werden kann.

Suchergebnisse rendern

// Render the results.
    type templateData struct {
        Results []Result
        Elapsed time.Duration
    }
    if err := resultsTemplate.Execute(w, templateData{
        Results: results,
        Elapsed: elapsed,
    }); err != nil {
        log.Print(err)
        return
    }

results verwenden Template.Execute, um HTML zu generieren und in einen io.Writer zu schreiben:

type Writer interface {
        Write(p []byte) (n int, err error)
}

http.ResponseWriter implementiert das Interface io.Writer.

Go-Variablen manipulieren HTML-Vorlagen

// A Result contains the title and URL of a search result.
type Result struct {
    Title, URL string
}
var resultsTemplate = template.Must(template.New("results").Parse(`
<html>
<head/>
<body>
  <ol>
  {{range .Results}}
    <li>{{.Title}} - <a href="{{.URL}}.html">{{.URL}}</a></li>
  {{end}}
  </ol>
  <p>{{len .Results}} results in {{.Elapsed}}</p>
</body>
</html>
`))

Google Search API anfordern

func Search(query string) ([]Result, error) {
    // Prepare the Google Search API request.
    u, err := url.Parse("https://ajax.googleapis.com/ajax/services/search/web?v=1.0")
    if err != nil {
        return nil, err
    }
    q := u.Query()
    q.Set("q", query)
    u.RawQuery = q.Encode()
    // Issue the HTTP request and handle the response.
    resp, err := http.Get(u.String())
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

Die defer-Anweisung bewirkt, dass resp.Body.Close ausgeführt wird, wenn die Methode Search zurückkehrt.

Zurückgegebene JSON-Daten in Go-Struct-Typen parsen

developers.google.com/web-search/docs/#fonje

var jsonResponse struct {
        ResponseData struct {
            Results []struct {
                TitleNoFormatting, URL string
            }
        }
    }
    if err := json.NewDecoder(resp.Body).Decode(&jsonResponse); err != nil {
        return nil, err
    }
    // Extract the Results from jsonResponse and return them.
    var results []Result
    for _, r := range jsonResponse.ResponseData.Results {
        results = append(results, Result{Title: r.TitleNoFormatting, URL: r.URL})
    }
    return results, nil
}

Das ist das Frontend dafür

Alle referenzierten Pakete stammen aus der Standardbibliothek:

import (
    "encoding/json"
    "fmt"
    "html/template"
    "log"
    "net/http"
    "net/url"
    "time"
)

Go-Server-Skalierbarkeit: Jede Anfrage läuft in ihrer eigenen Goroutine.

Sprechen wir über Nebenläufigkeit.

Communicating Sequential Processes (Hoare, 1978)

Nebenläufige Programme als unabhängige Prozesse, die durch Nachrichtenkommunikation sequenziell ausgeführt werden.

Sequenzielle Ausführung ist leicht zu verstehen; asynchrone nicht.

"Kommuniziere nicht durch gemeinsamen Speicher; teile Speicher durch Kommunikation."

Go-Prinzipien:Goroutines, Channels und select-Anweisungen.

Goroutines

Goroutinen sind wie leichtgewichtige Threads.

Sie verwenden kleineStacks (winzige Stacks) und passen sich bei Bedarf an.

Go-Programme können Tausende und Abertausende von(Goroutinen)Instanzen haben.

Verwenden Sie die go-Anweisung, um eine Goroutine zu starten:

go f(args)

Die Go-Laufzeit platziert Goroutinen in OS-Threads.

Verwenden Sie keine Threads, um Goroutinen zu blockieren.

Kanäle

Kanäle sind für die Kommunikation zwischen Goroutinen konzipiert.

c := make(chan string)
 
// goroutine 1
c <- "hello!"
 
// goroutine 2
s := <-c
fmt.Println(s) // "hello!"

Auswählen

Die select-Anweisung deklariert einen Block, um die Ausführung zu bestimmen.

select {
case n := <-in:
  fmt.Println("received", n)
case out <- v:
  fmt.Println("sent", v)
}

Nur der case-Block, dessen Bedingung wahr ist, wird ausgeführt.

Beispiel: Google Search (Backend)

F: Was kann Google Search tun?

A: Stellen Sie eine Frage, und es liefert eine Seite mit Suchergebnissen (und einige Anzeigen) zurück.

F: Wie erhalten wir diese Suchergebnisse?

A: Sende eine Abfrage an Websuche, Bildsuche, YouTube (Video), Maps, News, warte und hole dann die Ergebnisse ab.

Wie sollten wir es implementieren?

Google Search: ein Fake-Framework

Wir simulieren eine Suchfunktion, die zufällig zwischen 0 und 100 Millisekunden ein Timeout hat.

var (
    Web   = fakeSearch("web")
    Image = fakeSearch("image")
    Video = fakeSearch("video")
)
type Search func(query string) Result
func fakeSearch(kind string) Search {
    return func(query string) Result {
        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
        return Result(fmt.Sprintf("%s result for %q\n", kind, query))
    }
}

Google Search: Test-Framework

func main() {
    start := time.Now()
    results := Google("golang")
    elapsed := time.Since(start)
    fmt.Println(results)
    fmt.Println(elapsed)
}

Google Search (seriell)

Die Google-Funktion nimmt eine Abfrage entgegen und gibt eine Ergebnismenge zurück (nicht unbedingt Zeichenketten).

Google ruft Web, Image und Video nacheinander auf und fügt die Ergebnisse zur Ergebnismenge hinzu.

func Google(query string) (results []Result) {
    results = append(results, Web(query))
    results = append(results, Image(query))
    results = append(results, Video(query))
    return
}

Google Search (parallel)

Führe Web-, Image- und Video-Suchen gleichzeitig aus und warte auf alle Ergebnisse.

Die func-Methode ist eine Closure über query und c.

func Google(query string) (results []Result) {
    c := make(chan Result)
    go func() { c <- Web(query) }()
    go func() { c <- Image(query) }()
    go func() { c <- Video(query) }()
    for i := 0; i < 3; i++ {
        result := <-c
        results = append(results, result)
    }
    return
}

Google-Suche (Timeout)

Auf langsame Server warten.

Keine Locks, keine Bedingungsvariablen, keine Rückgabewerte.

    c := make(chan Result, 3)
    go func() { c <- Web(query) }()
    go func() { c <- Image(query) }()
    go func() { c <- Video(query) }()
    timeout := time.After(80 * time.Millisecond)
    for i := 0; i < 3; i++ {
        select {
        case result := <-c:
            results = append(results, result)
        case <-timeout:
            fmt.Println("timed out")
            return
        }
    }
    return

Timeout vermeiden

F: Wie verhindern wir, dass Ergebnisse von langsamen Diensten verloren gehen?

A: Repliziere den Dienst, sende Anfragen an mehrere replizierte Dienste und verwende das Ergebnis der ersten Antwort.

func First(query string, replicas ...Search) Result {
    c := make(chan Result, len(replicas))
    searchReplica := func(i int) { c <- replicas[i](query) }
    for i := range replicas {
        go searchReplica(i)
    }
    return <-c
}

Verwendung der First-Funktion

func main() {
    start := time.Now()
    result := First("golang",
        fakeSearch("replica 1"),
        fakeSearch("replica 2"))
    elapsed := time.Since(start)
    fmt.Println(result)
    fmt.Println(elapsed)
}

Google-Suche (Replikation)

Verwende replizierte Dienste, um übermäßige Latenz zu reduzieren.

    c := make(chan Result, 3)
    go func() { c <- First(query, Web1, Web2) }()
    go func() { c <- First(query, Image1, Image2) }()
    go func() { c <- First(query, Video1, Video2) }()
    timeout := time.After(80 * time.Millisecond)
    for i := 0; i < 3; i++ {
        select {
        case result := <-c:
            results = append(results, result)
        case <-timeout:
            fmt.Println("timed out")
            return
        }
    }
    return

Sonstiges

Keine Locks, keine Bedingungsvariablen, keine Aufrufe.

Zusammenfassung

Mit ein paar einfachen Transformationen verwenden wir die Nebenläufigkeitsprimitive von Go, um eine

  • Langsam
  • sequenziell
  • ausfallempfindliche

Programm in ein

  • Schnell
  • nebenläufig
  • wiederverwendbares
  • robust

Werkzeuge

Go hat viele leistungsstarke Werkzeuge

  • gofmt und goimports
  • Das go-Tool
  • godoc
  • IDE- und Editor-Unterstützung

Die Sprache ist auf die Toolchain ausgelegt.

gofmt und goimports

Gofmt kann Code automatisch formatieren, ganz ohne Optionen.

Goimports aktualisiert Importdeklarationen basierend auf Ihrem Arbeitsbereich.

Die meisten können diese Werkzeuge bedenkenlos verwenden.

play.golang.org/p/GPqra77cBK

Das go-Tool

Das go-Tool kann Go-Programme aus Quellcode in einer herkömmlichen Verzeichnisstruktur erstellen. Keine Makefiles oder andere Konfiguration erforderlich.

Holen Sie sich diese Tools und ihre Abhängigkeiten und bauen und installieren Sie sie dann:

% go get golang.org/x/tools/cmd/present

Ausführen:

% present

godoc

Erzeugen Sie Dokumentation für den gesamten Open-Source-Go-Code der Welt:

godoc.org

IDE- und Editor-Unterstützung

Eclipse, IntelliJ, emacs, vim usw.:

  • gofmt
  • goimports
  • godoclookups
  • Codevervollständigung
  • Codenavigation

Aber es gibt keine "Go-IDE".

Go-Tools sind überall.

Die nächsten Schritte mit Go

Die Go-Roadmap online ansehen.

tour.golang.org

Eine Fülle an Lernressourcen.

golang.org/wiki/Learn

Eine großartige Community.

golang.org/project

Vielen Dank

Sameer Ajmani

Technischer Leiter, Go-Team

Google

@Sajma

sameer@golang.org