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.

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."
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 ?
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 :
- Moins, c'est exponentiellement plus (Pike, 2012)
- Go chez Google : conception du langage au service de l'ingénierie logicielle (Pike, 2012)
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é.
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 :
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.
Une multitude de ressources d'apprentissage.
Une formidable communauté.
Merci
Sameer Ajmani
Responsable technique, équipe Go
sameer@golang.org