Ceci est un PPT partagé par Sameer Ajmani, responsable technique de l'équipe Go de Google, préparé pour les programmeurs Java afin de démarrer rapidement avec Go.

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

Vidéo

Ce PPT a été utilisé le 23 avril 2015 àNYJavaSIGutilisé dans.

Allez sur YouTube pour regarder la vidéo

Contenu principal

1. Qu'est-ce que Go, et qui utilise Go ?
2. Comparaison entre Go et Java
3. Exemples de code
4. Concurrence
5. Outils

Qu'est-ce que Go ?

"Go est un langage de programmation open source qui facilite la création de logiciels simples, fiables et efficaces."

golang.org

Histoire de Go

La conception a commencé dans la seconde moitié de 2007

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

Open source depuis 2009, avec une communauté très active.

La version stable Go 1 a été publiée au début de 2012.

Pourquoi Go ?

Go est une solution à l'échelle de Google.

Échelle du système

  • Échelle prévue pour 10⁶⁺ machines
  • Des tâches s'exécutent sur des milliers de machines chaque jour
  • Collabore et interagit avec d'autres tâches dans le système
  • Beaucoup de travail se déroule en même temps

Solution : un support très solide de la concurrence

Deuxième problème : l’échelle de l’ingénierie

En 2011

  • 5000+ développeurs répartis sur 40+ bureaux
  • 20+ modifications par minute
  • 50% de la base de code est modifiée chaque mois
  • 50 millions de cas de test exécutés par jour
  • Une arborescence de code unique

Solution : un langage conçu pour les grandes bases de code

Qui utilise Go chez Google ?

De nombreux projets, des milliers de programmeurs Go et des millions de lignes de code Go.

Exemples publics :

  • Proxy SPDY Chrome pour appareils mobiles
  • Serveurs de téléchargement pour Chrome, ChromeOS, Android SDK, Earth, etc.
  • Répartiteur de charge MySQL Vitess de YouTube

Ses tâches principales sont les serveurs web, mais c'est un langage polyvalent.

Qui d'autre utilise Go en dehors de Google ?

golang.org/wiki/GoUsers

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

Comparaison de Go et Java

Go et Java ont beaucoup en commun

  • Famille C (typage fort, accolades)
  • Typage statique
  • Récupération de mémoire
  • Sécurité mémoire (références nil, vérifications des bornes à l'exécution)
  • Les variables sont toujours initialisées (zéro/nil/false)
  • Méthodes
  • Interfaces
  • Assertions de type (instances)
  • Réflexion

Différences entre Go et Java

  • Les programmes sont compilés directement en code machine, sans VM
  • Binaires liés statiquement
  • Contrôle de la disposition mémoire
  • Valeurs de fonction et fermetures lexicales
  • Chaînes de caractères intégrées (UTF-8)
  • Maps et tableaux/slices génériques intégrés
  • Concurrence intégrée

Go omet délibérément un certain nombre de fonctionnalités

  • Pas de classes
  • Pas de constructeurs
  • Pas d'héritage
  • Pas de final
  • Pas d'exceptions
  • Pas d'annotations
  • Pas de génériques définis par l'utilisateur

Pourquoi Go omet-il ces fonctionnalités ?

La clarté du code est primordiale

Quand on lit le code, on peut clairement savoir ce que le programme va faire

Quand on écrit du code, on peut aussi clairement faire en sorte que le programme fasse ce que l'on veut

Parfois, cela signifie écrire une boucle au lieu d'appeler une fonction obscure.

(Ne devenez pas trop ennuyeux)

Pour un contexte de conception détaillé, voir :

Exemples

Les programmeurs Java devraient trouver Go familier

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, 世界!")
}

Bonjour, serveur web

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, 世界!")
}

(Accès) le type est déclaré en fonction du nom de la variable.
Les noms de variables publiques commencent par une majuscule, les variables privées commencent par une minuscule.

Exemple : 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) {

Validation des requêtes

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 est une méthode sur *http.Request :

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

`query := req.FormValue("q")` initialise la variable query, dont le type est le résultat de l'expression de droite, ici le type string.

Récupérer les résultats de recherche

// 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
    }

La méthode Search renvoie deux valeurs : results et error.

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

Lorsque la valeur de error est nil, les résultats sont valides.

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

Le type Error peut contenir des informations supplémentaires, accessibles via une assertion de type.

Afficher les résultats de recherche

// 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
    }

Les résultats utilisent Template.Execute pour générer du HTML et écrire dans un io.Writer :

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

http.ResponseWriter implémente l'interface io.Writer.

Les variables Go manipulent des modèles HTML

// 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>
`))

Interroger l'API Google Search

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()

L'instruction defer entraîne l'exécution de resp.Body.Close lorsque la méthode Search renvoie.

Analyser les données JSON renvoyées en types struct Go

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
}

C'est le frontend correspondant

Tous les packages référencés proviennent de la bibliothèque standard :

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

Scalabilité du serveur Go : chaque requête s'exécute dans sa propre goroutine.

Parlons de la concurrence.

Processus séquentiels communicants (Hoare, 1978)

Programmes concurrents en tant que processus indépendants, s'exécutant séquentiellement par communication de messages.

L'exécution séquentielle est facile à comprendre ; l'asynchrone ne l'est pas.

"Ne communiquez pas en partageant la mémoire ; partagez la mémoire en communiquant."

Principes de Go :goroutines, canaux et instructions select.

Goroutines

Les goroutines sont comme des threads légers.

Elles utilisent de petitespiles (piles minuscules) et s'exécutent avec un ajustement à la demande.

Les programmes Go peuvent avoir des milliers et des milliers de(goroutines)instances.

Utilisez l'instruction go pour démarrer une goroutine :

go f(args)

Le runtime Go place les goroutines dans des threads du système d'exploitation.

N'utilisez pas de threads pour bloquer les goroutines.

Canaux

Les canaux sont conçus pour la communication entre goroutines.

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

Sélection

L'instruction select déclare un bloc pour déterminer l'exécution.

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

Seul le bloc case dont la condition est vraie sera exécuté.

Exemple : Google Search (backend)

Q : Que peut faire Google Search ?

R : Posez une question, et il renvoie une page de résultats de recherche (et quelques annonces).

Q : Comment obtenons-nous ces résultats de recherche ?

R : Envoyez une requête à la recherche web, à la recherche d’images, à YouTube (vidéo), à Maps, aux actualités, attendez, puis récupérez les résultats.

Comment devrions-nous l’implémenter ?

Recherche Google : un framework factice

Nous allons simuler une fonction de recherche qui expire aléatoirement entre 0 et 100 millisecondes.

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))
    }
}

Recherche Google : framework de test

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

Recherche Google (en série)

La fonction Google prend une requête et renvoie un ensemble de résultats (pas nécessairement des chaînes de caractères).

Google appelle Web, Image et Video en séquence et ajoute les résultats à l’ensemble de résultats.

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

Recherche Google (en parallèle)

Lancez les recherches Web, Image et Video en parallèle, et attendez tous les résultats.

La méthode func est une fermeture sur query et 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 Search (délai d'attente)

Attendre les serveurs lents.

Aucun verrou, aucune variable de condition, aucune valeur de retour.

    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

Éviter le délai d'attente

Q : Comment éviter de perdre les résultats des services lents ?

R : Répliquer le service, envoyer des requêtes à plusieurs services répliqués et utiliser le résultat de la première réponse.

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
}

Utilisation de la fonction First

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 Search (réplication)

Utiliser des services répliqués pour réduire la latence excessive.

    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

Autre

Aucun verrou, aucune variable de condition, aucun appel.

Résumé

Avec quelques transformations simples, nous utilisons les primitives de concurrence de Go pour transformer un

  • Lent
  • séquentiel
  • sensible aux défaillances

programme en un

  • Rapide
  • concurrent
  • réutilisable
  • robuste

Outils

Go dispose de nombreux outils puissants

  • gofmt et goimports
  • L'outil go
  • godoc
  • Prise en charge des IDE et des éditeurs

Le langage est conçu pour la chaîne d'outils.

gofmt et goimports

Gofmt peut formater automatiquement le code, sans aucune option.

Goimports met à jour les déclarations d'import en fonction de votre espace de travail.

La plupart des gens peuvent utiliser ces outils en toute sécurité.

play.golang.org/p/GPqra77cBK

L'outil go

L'outil go peut compiler des programmes Go à partir de code source dans une arborescence de répertoires conventionnelle. Aucun Makefile ni autre configuration nécessaire.

Récupérez ces outils et leurs dépendances, puis compilez et installez :

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

Exécutez :

% present

godoc

Générez de la documentation pour tout le code Go open source dans le monde :

godoc.org

Prise en charge des IDE et des éditeurs

Eclipse, IntelliJ, emacs, vim, etc. :

  • gofmt
  • goimports
  • godoclookups
  • complétion de code
  • navigation dans le code

Mais il n'existe pas d'« IDE Go ».

Les outils Go sont partout.

Les prochaines étapes de Go

Consultez la feuille de route de Go en ligne.

tour.golang.org

Une multitude de ressources d'apprentissage.

golang.org/wiki/Learn

Une formidable communauté.

golang.org/project

Merci

Sameer Ajmani

Responsable technique, équipe Go

Google

@Sajma

sameer@golang.org