Este es el PPT compartido por Sameer Ajmani, gerente técnico líder del equipo de Go de Google, preparado para que los programadores de Java se inicien rápidamente en Go.

Vídeo
Este PPT fue el 23 de abril de 2015 enNYJavaSIGdonde se utilizó.
Ir a YouTube para ver el vídeo
Contenido principal
1. ¿Qué es Go y quién lo usa?
2. Comparación de Go y Java
3. Ejemplos de código
4. Concurrencia
5. Herramientas
¿Qué es Go?
Go es un lenguaje de programación de código abierto que hace fácil construir software simple, confiable y eficiente.
Historia de Go
El diseño comenzó en la segunda mitad de 2007
- Robert Griesemer, Rob Pike y Ken Thompson.
- Ian Lance Taylor y Russ Cox
Desde 2009 es de código abierto, con una comunidad muy activa.
La versión estable de Go, Go 1, se publicó a principios de 2012.
¿Por qué existe Go?
Go es una solución para la escala de Google.
Escala de sistemas
- La escala prevista es de 10⁶⁺ máquinas.
- Cada día se ejecutan trabajos en miles de máquinas.
- Colaboran e interactúan con otros trabajos en el sistema.
- Se realiza una gran cantidad de trabajo al mismo tiempo.
Solución: soporte muy potente para la concurrencia.
El segundo problema: la escala de ingeniería
En 2011
- Más de 5000 desarrolladores en más de 40 oficinas.
- Más de 20 cambios por minuto.
- Cada mes se modifica el 50% de la base de código.
- Se ejecutan 50 millones de casos de prueba cada día.
- Un único árbol de código.
Solución: un lenguaje diseñado para grandes bases de código.
¿Quién usa Go en Google?
Muchos proyectos, miles de programadores de Go, millones de líneas de código Go.
Ejemplos públicos:
- Proxy SPDY de Chrome para dispositivos móviles.
- Servidores de descarga para Chrome, ChromeOS, Android SDK, Earth, etc.
- Balanceador MySQL de YouTube, Vitess.
La tarea principal son los servidores web, pero es un lenguaje de propósito general.
¿Quién más usa Go además de Google?
Apcera, Bitbucket, bitly, Canonical, CloudFlare, Core OS, Digital Ocean, Docker, Dropbox, Facebook, Getty Images, GitHub, Heroku, Iron.io, Kubernetes, Medium, MongoDB services, Mozilla services, New York Times, pool.ntp.org, Secret, SmugMug, SoundCloud, Stripe, Square, Thomson Reuters, Tumblr, ...
Comparación de Go y Java
Go y Java tienen mucho en común
- Familia de C (tipado fuerte, llaves)
- Tipado estático
- Recolección de basura
- Seguridad de memoria (referencias nil, comprobación de límites en tiempo de ejecución)
- Las variables siempre se inicializan (zero/nil/false)
- Métodos
- Interfaces
- Aserción de tipos (instanceof)
- Reflexión
Diferencias entre Go y Java
- El código se compila directamente a código de máquina, sin VM.
- Binarios estáticamente enlazados.
- Control del diseño de memoria.
- Valores de función y clausuras léxicas.
- Cadenas integradas (UTF-8)
- Mapas genéricos integrados y arrays/slices
- Concurrencia integrada.
Go eliminó deliberadamente muchas características
- Sin clases.
- Sin constructores.
- Sin herencia.
- Sin final.
- Sin excepciones.
- Sin anotaciones.
- Sin genéricos personalizados.
¿Por qué Go prescinde de esas características?
La claridad del código es primordial.
Al ver el código, se puede saber claramente qué hará el programa.
Al escribir código, también se puede lograr claramente que el programa haga lo que deseas.
A veces esto significa escribir un bucle en lugar de llamar a una función poco clara.
(No te vuelvas demasiado aburrido).
Para conocer el contexto de diseño en detalle, consulta:
- Less is exponentially more (Pike, 2012)
- Go at Google: Language Design in the Service of Software Engineering (Pike, 2012)
Ejemplos
Los programadores de Java deberían encontrar Go muy familiar
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, 世界!")
}
Hello, web server (Hola, servidor 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, 世界!")
}
El tipo se declara según el nombre de la variable (permisos de acceso).
Los nombres de las variables públicas comienzan con mayúscula; las privadas, con minúscula.
Ejemplo: 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) {
Validación de solicitudes
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 es un método de *http.Request:
package http
type Request struct {...}
func (r *Request) FormValue(key string) string {...}
query := req.FormValue("q") inicializa la variable query, cuyo tipo es el resultado de la expresión de la derecha, aquí de tipo string.
Obtener resultados de búsqueda
// 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
}
El método Search devuelve dos valores: el resultado `results` y el error `error`.
func Search(query string) ([]Result, error) {...}
Cuando el valor de `error` es `nil`, `results` es válido.
type error interface {
Error() string // a useful human-readable error message
}
El tipo `error` puede contener información adicional, accesible mediante aserción.
Renderizar resultados de búsqueda
// 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
}
El resultado `results` utiliza Template.Execute para generar HTML y escribirlo en un `io.Writer`:
type Writer interface {
Write(p []byte) (n int, err error)
}
http.ResponseWriter implementa la interfaz io.Writer.
Variables de Go y plantillas 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>
`))
Solicitar la API de búsqueda de Google
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()
La declaración `defer` hace que `resp.Body.Close` se ejecute cuando el método Search retorna.
Analizar los datos JSON devueltos a tipos struct de 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
}
Este es su frontend
Todos los paquetes referenciados provienen de la biblioteca estándar:
import (
"encoding/json"
"fmt"
"html/template"
"log"
"net/http"
"net/url"
"time"
)
A escala de servidor Go: cada solicitud se ejecuta en su propia goroutine.
Hablemos de concurrencia.
Procesos secuenciales comunicantes (Hoare, 1978)
Los programas concurrentes actúan como procesos independientes que se ejecutan secuencialmente mediante el intercambio de mensajes.
La ejecución secuencial es fácil de entender; la asíncrona no.
No te comuniques compartiendo memoria; comparte memoria comunicándote.
Principios de Go:goroutines, canales y la declaración select.
Goroutines
Goroutines son como hilos ligeros.
Se ejecutan mediante pequeñaspilas (tiny stacks) y se ajustan según la demanda.
Los programas Go pueden tener miles y miles de(goroutines)instancias.
Usa la declaración `go` para iniciar una goroutine:
go f(args)
El runtime de Go coloca las goroutines en hilos del SO.
No bloquees las goroutines con hilos.
Channels
Los Channels se definen para la comunicación entre goroutines.
c := make(chan string) // goroutine 1 c <- "hello!" // goroutine 2 s := <-c fmt.Println(s) // "hello!"
Select
`select` declara un bloque de sentencias para decidir la ejecución.
select {
case n := <-in:
fmt.Println("received", n)
case out <- v:
fmt.Println("sent", v)
}
Solo se ejecuta el bloque `case` cuya condición se cumple.
Ejemplo: Búsqueda de Google (backend)
Pregunta: ¿Qué puede hacer la Búsqueda de Google?
Respuesta: Haz una pregunta, y puede devolver una página de resultados de búsqueda (y algunos anuncios).
Pregunta: ¿Cómo obtenemos estos resultados de búsqueda?
Respuesta: Envía una pregunta a búsqueda web, búsqueda de imágenes, YouTube (videos), mapas, noticias, espera y luego recupera los resultados.
¿Cómo lo implementamos?
Búsqueda de Google: un framework simulado
Vamos a simular una función de búsqueda con un tiempo de espera aleatorio de 0 a 100 milisegundos.
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))
}
}
Búsqueda de Google: framework de pruebas
func main() {
start := time.Now()
results := Google("golang")
elapsed := time.Since(start)
fmt.Println(results)
fmt.Println(elapsed)
}
Búsqueda de Google (serial)
La función Google toma una consulta y devuelve un conjunto de resultados (no necesariamente una cadena).
Google llama secuencialmente a Web, Image y Video y agrega las respuestas al conjunto de resultados.
func Google(query string) (results []Result) {
results = append(results, Web(query))
results = append(results, Image(query))
results = append(results, Video(query))
return
}
Búsqueda de Google (paralelo)
Ejecuta simultáneamente las búsquedas Web, Image y Video, y espera todos los resultados.
La función se cierra sobre `query` y `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
}
Búsqueda de Google (timeout)
Esperar a servidores lentos.
Sin bloqueos, sin variables de condición, sin valores de retorno.
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
Evitar el timeout
Pregunta: ¿Cómo evitar perder los resultados de servicios lentos?
Respuesta: Replica el servicio, envía la solicitud a múltiples réplicas y usa el resultado de la primera respuesta.
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
}
Usar la función 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)
}
Búsqueda de Google (replicación)
Usa servicios replicados para reducir la latencia innecesaria.
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
Otros
Sin bloqueos, sin variables de condición, sin llamadas.
Resumen
Después de algunas transformaciones simples, usamos las primitivas de concurrencia de Go para transformar un
- Lento
- secuencial
- sensible a fallos
programa en una
- Rápido
- concurrente
- reutilizable
- robusta
Herramientas
Go tiene muchas herramientas potentes
- gofmt y goimports
- The go tool
- godoc
- Soporte de IDEs y editores
El lenguaje está diseñado para la cadena de herramientas.
gofmt y goimports
Gofmt formatea el código automáticamente, sin opciones.
Goimports actualiza las declaraciones de importación según tu espacio de trabajo.
La mayoría de las personas pueden usar estas herramientas de forma segura.
The go tool
La herramienta `go` puede compilar programas Go a partir del código fuente en una distribución de directorios tradicional. No necesita Makefiles ni otra configuración.
Localiza estas herramientas y sus dependencias, luego compila e instala:
% go get golang.org/x/tools/cmd/present
Ejecuta:
% present
godoc
Genera documentación para todo el código Go de código abierto del mundo:
Soporte de IDE y editores
Eclipse, IntelliJ, emacs, vim, etc.:
- gofmt
- goimports
- godoclookups
- code completion
- code navigation
Pero no hay un "Go IDE".
Las herramientas de Go están en todas partes.
Próximos pasos de Go
Ver la hoja de ruta de Go en línea
Una gran cantidad de recursos de aprendizaje
Una comunidad excelente
Thank you
Sameer Ajmani
Tech Lead Manager, Go team
sameer@golang.org