Merge pull request #775 from contraslash/master

Translated chapter 8 to spanish
This commit is contained in:
astaxie
2017-01-10 12:13:33 +08:00
committed by GitHub
6 changed files with 1124 additions and 0 deletions

20
es/08.0.md Normal file
View File

@@ -0,0 +1,20 @@
# 8 Servicios web
Los servicios web permiten usar formatos como XML o JSON para intercambiar información a través de HTTP. Por ejemplo, si quieres saber el clima de Shanghai mañana, el precio de las acciones de Apple, o la información de un producto de Amazon, puedes escribir un segmento de ceodigo que obtenga la información de las plataformas abiertas. En Go, este proceso puede ser comparado a llamar una función local y obtener su valor de retorno.
La clave es que los servicios web son independientes de plataforma, esto permite desplegar tus aplicaciones en Linux e interactuar con aplicaciones ASP.NET en windows, por ejemplo, y tampoco tendrás problema interactuando con JSP o FreeBSD.
La arquitectura REST y el protocolo SOAP son los dos estilos mas populares en los servicios web y pueden ser implementados estos días.
- Las peticiónes REST son muy claras, porque están basadas en HTTP. Cada petición REST es en escencia una petición HTTP, y los servidores manejan las peticiones usando diferentes métodos. Como muchos desarrolladores ya están familiarizados con HTTP, REST puede sentirse como si ya estuviera en sus bolsillos. Vamos a ver como se implementa un REST en la sección 8.3.
- SOAP es un estándar para transmisión de información a través de la red y llamado a funciones remotas, lanzado por w3c. El problema con SOAP es que la especificación es muy grande y complicada, y sigue haciéndose mas larga. Go cree que las cosas deberían ser simples, entonces no vamos a hablar sobre SOAP.
Afortunadamente Go soporta RPC (Llamados a procedimientos remotos) que tiene un buen desempeño y es facil de desarrollar, entonces introduciremos como implementar un RPC en la sección 8.4.
Go es el lenguaje C del siglo XXI, queriendo ser simple y de alto desempeño. Con estas cualidades en mente, vamos a introducir la programación de sockets en la sección 8.1. Hoy en día muchos servidores en tiempo real utilizan sockets para superar el desempeño lento de HTTP. Junto con el rápido desarrollo de HTML, los websockets son usados por muchas compañias de juegos de red, y hablaremos mas de esto en la sección 8.2.
## Enlaces
- [Índice](preface.md)
- Capítulo anterior: [Capítulo 7 Resumen](07.7.md)
- Siguiente sección: [Sockets](08.1.md)

410
es/08.1.md Normal file
View File

@@ -0,0 +1,410 @@
# 8.1 Sockets
Algunos desarrolladores de aplicaciones red dicen que las capas inferiores de progración son basadas en sockets. Esto puede no ser cierto para todos los casos, pero muchas aplicaciones modernas en realidad usan sockets para obtener ventaja. Te has preguntado ¿cuántos navegadores se conectan con servidores web cuando navegas por internet? O ¿cómo MSN te conecta a ti y a tus amigos cuando están en el mismo chat enviando un mensaje en tiempo real? Muchos servicios como estos utilizan sockets para transferir datos. Como puedes ver, los sockets ocupan una posición muy importante en la programación de red hoy, y vamos a aprender a usar sockets en Go en esta sección.
## ¿Qué es un socket?
Los sockets se originaron de Unix, y dada la filosofía básica de "todo es un archivo", todo puede ser operado con "abrir -> escribir/leer -> cerrar". Los sockets implementan esta filosofía. Los sockets tienen una función para abrir un socket justo como lo haces con un archivo. Esto retorna un descriptor del socket que puede ser usado para operaciones como crear conexiones, transferir información, etc.
Hay dos tipos de sockets que son comúnmente usados que son los sockets de flujo (SOCK_STREAM) y sockets de datagramas (SOCK_DGRAM). Los sockets de flujo son orientadas a conexion, como TCP, mientras que los sockets de datagramas no establecen conexiones, como UDP.
## Comunicación entre sockets
Antes que entendamos como los sockets se comunican uno con otro, necesitamos asegurarnos que cada socket es único, de otra manera, establecer comunicaciones confiables está fuera del alcance. Podemos darle a cada proceso un único identificador (PID) que sirve a nuestro ambiente local, sin embargo, esto no funciona sobre la red. Afortunadamente TCP/IP nos ayuda con este problema. La dirección IP de la capa de red es única en una red de hosts. y el "protocolo + puerto" es único entre una red de aplicaciones. Entonecs podemos usar estor principios para hacer sockets que sean únicos.
![](images/8.1.socket.png?raw=true)
Figura 8.1 capas del protocolo de red.
Las aplicaciones que están basadas en TCP/IP todas usan sockets en su código de una manera u otra. Dado que las aplicaciones web se están volviendo cada vez mas prevalentes en los días modernos, no es de extrañarse que los programadores digan que "todo es sobre sockets".
## Conocimiento básico de Sockets
Sabemos que existen dos tipos de sockets, los TCP y los UDP. TCP y UDP son protocolos, y como mencioné, también necesitamos dirección IP y número de puerto para tener sockets únicos.
### IPv4
El internet globarl usa TCP/IP como su protocolo, donde IP es la capa de red y una parte principal de TCP/IP. IPv4 significa que es la versión 4; el dsarrollo de infraestructura lleva adelantado 30 años.
El número de bits en una dirección IPv4 es 32, lo que significa que solamente 2^32 dispositivos son capaces de conectarse a internet. A razón del rápido desarrollo del internet, las direcciones IP sobrepasaron este límite en los años recientes.
Formato de las direcciones IP: `127.0.0.1`, `172.122.121.111`.
### IPv6
IPv6 es la siguiente versión o la siguiente generación de internet. Ha sido desarrollada para solucionar muchos de los problemas inherentes a IPv4. Dispositivos usando IPv6 tienen una dirección de 128 bits de largo, entonces no tenemos que preocuparnos por la unicidad de la dirección. Para colocar esto en perspectiva, significa que puedes tener mas de 1000 direcciones IP por cada metro cuadrado en la tierra con IPv6. Otroso problema como conexión peer to peer, calida de servicio (QoS), seguridad, broadcast múltiple, etc también han sido mejorados.
Formato de direcciones: `2002:c0e8:82e7:0:0:0:c0e8:82e7`.
### Tipos IP en Go
el paquete `net` de Go provee muchos tipos, funciones y métodos para programación de red. La definición de IP es como sigue:
```
type IP []byte
```
LA función `ParseIP(s string) IP` es para convertir la versión IPv4 o IPv6 a una IP.
```
package main
import (
"net"
"os"
"fmt"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s ip-addr\n", os.Args[0])
os.Exit(1)
}
name := os.Args[1]
addr := net.ParseIP(name)
if addr == nil {
fmt.Println("Invalid address")
} else {
fmt.Println("The address is ", addr.String())
}
os.Exit(0)
}
```
Este retorna la correspondiente formato IP para una correspondiente dirección IP.
## Socket TCP
¿Qué podemos hacer cuando sabemos como visitar un servicio web a través de un puerto de red? Como un cliente, podemos enviar una petición apuntada al puerto de red y obtener la respuesta; como un servidor necesitamos enlazar un servicio a un puerto de red, esperar por por las peticiones de los clientes y darles una respuesta.
En Go, el paquete `net` tiene un tipo llamado `TCPConn` que facilita este tipo de interacción de cliente/servidor. Este tipo tiene dos funciones principales:
```
func (c *TCPConn) Write(b []byte) (n int, err os.Error)
func (c *TCPConn) Read(b []byte) (n int, err os.Error)
```
`TCPConn` puede ser usado como cliente o servidor para leer y escribir información:
También necesitamos una `TCPAddr` para representar la información de la dirección TCP:
```
type TCPAddr struct {
IP IP
Port int
}
```
Usamos la función `ResolveTCPAddr` para obtener una `TCPAddr` en Go:
```
func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error)
```
- Los argumentos de `net` pueden ser "tcp4", "tcp6" o "tcp", donde cada uno significa IPV4 Solamente, IPv6 solamente o IPv4 o IPv6.
- `addr` puede ser un nombre de dominio o una dirección ip, como "www.google.com:80" o "127.0.0.1:22"
### Cliente TCP
Los clientes de Go usan la función `DialTCP` en el paquete `net` para crear una conexión TCP, donde el resultado es un objeto `TCPConn`; después que una conexión es establecida, el servidor tiene el mismo tipo de objeto de conexión para la conexión actual, y cliente y servidor pueden comenzar a intercambiar información. En general, los clientes envían una petición a través de una `TCPConn` y reciben información del servidor; los servidores leen y analizan las peticiones, y devuelven una respuesta. Esta conexión permanecerá válida hasta que el cliente o el servidor la cierre. La función para crear una conexión es como sigue:
```
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error)
```
- Los argumentos de `net` pueden ser "tcp4", "tcp6" o "tcp", donde cada uno significa IPV4 Solamente, IPv6 solamente o IPv4 o IPv6.
- `laddr` representa la conexión local, definida a `nil` la mayoría de las veces.
- `raddr` representa la dirección remota.
Vamos a escribir un ejemplo para simular la petición de un cliente solicitando conexión a un servidor basado en una petición HTTP. Necesitamos un encabezado HTTP simple:
```
"HEAD / HTTP/1.0\r\n\r\n"
```
La respuesta del servidor puede verse de la siguiente manera:
```
HTTP/1.0 200 OK
ETag: "-9985996"
Last-Modified: Thu, 25 Mar 2010 17:51:10 GMT
Content-Length: 18074
Connection: close
Date: Sat, 28 Aug 2010 00:43:48 GMT
Server: lighttpd/1.4.23
```
Código del cliente:
```
package main
import (
"fmt"
"io/ioutil"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
os.Exit(1)
}
service := os.Args[1]
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
conn, err := net.DialTCP("tcp", nil, tcpAddr)
checkError(err)
_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
checkError(err)
result, err := ioutil.ReadAll(conn)
checkError(err)
fmt.Println(string(result))
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
```
En el ejemplo superior, usamos la entrada de un usuario como el argumento de `service` para `net.ResolveTCPAddr` para obtener un `tcpAddr`. Pasando `tcpAddr` a la función `DialTCP`, creamos una conexión TCP `conn`. Entonces podemos usar `conn` para enviar la petición al servidor. Finalmente, usamos `ioutil.ReadAll` para leer todo el contenido de `conn`, que contiene la respuesta del servidor.
### Servidor TCP
Ya tenemos un cliente TCP, También podemos usar el paquete `net` para escribir servidores TCP. En el lado del servidor necesitamos enlazar nuestro servicio a un puerto específico y esperar por una conexión de cliente.
```
func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error)
func (l *TCPListener) Accept() (c Conn, err os.Error)
```
Los argumentos requeridos aquí sin idénticos para por el la función `DialTCP` que usamos antes.
Implementemos una sincronización usando el puerto 7777:
```
package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
service := ":7777"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
daytime := time.Now().String()
conn.Write([]byte(daytime)) // No nos preocupamos por el valor de respuesta
conn.Close() // terminamos la conexión con el cliente
}
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
```
Después que el servicio es iniciado, esperamos por las peticiones de los clientes. Cuando se recibe una petición de un cliente, este la acepta (`Accept`) y retorna una respuesta que contiene la información del tiempo actual. Vale la pena preocuparse por los errores que pueden ocurrir en el ciclo `for`, el servicio continúa corriendo en vez de salir. En vez de terminar, el servidor almacenará el error en un log de errores del servidor.
El código de arriba todavía no es suficientemente bueno. Nosotros no usamos rutinas, lo que nos permitirá aceptar peticiones simultaneas. Hagamos esto ahora:
```
package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
service := ":1200"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
daytime := time.Now().String()
conn.Write([]byte(daytime)) // No nos preocupamos por el valor de retorno
// Terminamos con este cliente
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
```
Al separar nuestra proceso de negocio del la función `handleClient` y usando la palabra reservada `go`, ya hemos implementado concurrencia para nuestro servicio. Esto es una buena demostración del poder y la simplicidad de las rutinas.
Algunos deben de estar pensando lo siguiente: este servidor no hace nada útil. ¿Qué si necesitamos enviar múltiples peticiones para diferentes formatos de tiempo en una sola conexión? ¿Cómo hacemos eso?
```
package main
import (
"fmt"
"net"
"os"
"time"
"strconv"
)
func main() {
service := ":1200"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
conn.SetReadDeadline(time.Now().Add(2 * time.Minute)) // Definimos un tiempo de cancelación de dos minutos
request := make([]byte, 128) // Definimos la longitud máxima de la petición para prevenir ataques de desbordamiento.
defer conn.Close() // Cerramos la conexión antes de salir
for {
read_len, err := conn.Read(request)
if err != nil {
fmt.Println(err)
break
}
if read_len == 0 {
break // Conexión cerrada por el cliente
} else if string(request[:read_len]) == "timestamp" {
daytime := strconv.FormatInt(time.Now().Unix(), 10)
conn.Write([]byte(daytime))
} else {
daytime := time.Now().String()
conn.Write([]byte(daytime))
}
}
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
```
En este ejemplo usamos `conn.Read()` para constantemente leer las peticiones de un cliente. No podemos cerrar la conexión porque los clientes pueden enviar mas de una petición. Usando `conn.SetReadDeadline()` la conexión se cerrará automáticamente después que pase este periodo de tiempo. Cuando el tiempo de caducidad ha pasado, nuestro programa termina desde el ciclo `for`. Nota que las peticiones (`request`) necesitan ser creadas con la limitante de tamaño en para prevenir ataques de desbordamiento.
### Controlando las conexiones TCP
Funciones de control para conexiones TCP:
```
func DialTimeout(net, addr string, timeout time.Duration) (Conn, error)
```
Para definir el tiempo límite de las conexiones, estos son los métodos para ser usados por clientes y servidores:
```
func (c *TCPConn) SetReadDeadline(t time.Time) error
func (c *TCPConn) SetWriteDeadline(t time.Time) error
```
Definir el tiempo límite de una conexión:
```
func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error
```
Vale la pena tomar algún tiempo para pensar en cuanto quieres que sea el tiempo límite para la conexión. Conexiones largas pueden reducir el la carga extra producida por la creación de conexiones y es ua buena opción para aplicaciones que necesita intercambiar información frecuentemente.
Para información mas detallada, busca la documentación oficial del paquete `net` de Go.
## Sockets UDP
La única diferencia entre un socket UDP y uno TCP es el método de procesamiento para múltiples conexiones en el lado del servidor. Esto radica en que UDP no tinene una función como `Accept`. Todas las otras funciones tienen una contraparte `UDP`; slo reemplaza `TCP` por `UDP` en las funciones mencionadas arriba:
```
func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error)
func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error)
func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error)
```
Ejemplo de un client UDP:
```
package main
import (
"fmt"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])
os.Exit(1)
}
service := os.Args[1]
udpAddr, err := net.ResolveUDPAddr("udp4", service)
checkError(err)
conn, err := net.DialUDP("udp", nil, udpAddr)
checkError(err)
_, err = conn.Write([]byte("anything"))
checkError(err)
var buf [512]byte
n, err := conn.Read(buf[0:])
checkError(err)
fmt.Println(string(buf[0:n]))
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error ", err.Error())
os.Exit(1)
}
}
```
Ejemplo de servidor UDP:
```
package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
service := ":1200"
udpAddr, err := net.ResolveUDPAddr("udp4", service)
checkError(err)
conn, err := net.ListenUDP("udp", udpAddr)
checkError(err)
for {
handleClient(conn)
}
}
func handleClient(conn *net.UDPConn) {
var buf [512]byte
_, addr, err := conn.ReadFromUDP(buf[0:])
if err != nil {
return
}
daytime := time.Now().String()
conn.WriteToUDP([]byte(daytime), addr)
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error ", err.Error())
os.Exit(1)
}
}
```
## Resumen
A través de la descripción y algunos códigos de ejemplos usando sockets TCP y UDP, podemos ver que Go provee un excelente soporte para la programación por sockets, y que son divertidos y fáciles de usar. Go también provee muchas funciones para construir aplicaciones de alto rendimiento con sockets.
## Links
- [Índice](preface.md)
- Sección previa: [Servicios web](08.0.md)
- Siguiente secición: [WebSocket](08.2.md)

160
es/08.2.md Normal file
View File

@@ -0,0 +1,160 @@
# 8.2 WebSockets
Los WebSockets son una característica importante de HTML5. Esto implementa sockets basados en navegadores, lo que permite a los navegadores tener comunicación en dos vías con los servidores. La mayoría de navegadores populares como Firefox, Chrome y Safari proveen soporte para WebSockets.
Las personas utilizan "roll polling" para los servicios de mensajes instantaneos antes que los WebSockets nacieran, lo que permitía alos clientes enviar peticiones HTTP periódicamente. El servidor entonces retornaba la información mas reciente a los clientes. La desventaja de este método es que requiere que los clientes mantengan enviando muchas conexiones al servidor, lo que consume mucho ancho de banda.
Los WebSockets una un tipo especial de encabezado que reduce el número de apretones de manos requeridos entre el servidor y el cliente, a solo uno, para establecer la conexión. Esta conexión permanecerá activa por todo su tiempo de vida, y puedes usar JavaScript para enviar o recibir información de esta conexión, como en el caso de un socket TCP convencional. Esto resuelve muchos de los dolores de cabeza de las aplicaciones web en tiempo real y le trae las siguientes ventajas al protocolo HTTP:
- Solo una conexión TCP para un solo cliente web.
- Los servidores de WebSockets pueden enviar información a los clientes.
- Los encabezados mas livianos reducen la carga de transmisión de red.
Las URLs de los WebSockets comienzan con un ws:// o un wss://. La siguiente figura muestra el proceso de comunicación de un websocket. Un encabezado particular es enviado al servidor como parte del apretón de manos y la conexión es establecida. Entonces, servidores y cliente son capaces de recibir y enviar información a través del WebSocket
![](images/8.2.websocket.png?raw=true)
Figura 8.2 Principio de los websockets
## Principios de los websockets
El protocolo de websockets es muy fácil. Después de completar el apretón de manos inicial, una conexión es establecida. La comunicación subsecuenta comenzará con un "\x00" y terminará con un "\xFF". Este prefijo y sufijo será visible a los clientes, porque el websocket romperá los datos entre ambos finales, produciendo los datos puros.
Las conexiones por websockets son pedidas por los navegadores y respondidas por los servidores. Después la conexión es establecida. Este proceso se llama "apretón de manos".
Considera las siguientes peticiones y respuestas
Consider the following requests and responses:
![](images/8.2.websocket2.png?raw=true)
Figura 8.3 Peticiones y respuestas de un websocket
"Sec-WebSocket-key" es generada aleatoreamente, como puedes haberlo adivinado, y es codificada a base 64. Los servidores encesitan concatenar esta llave a una cadena después de aceptar una petición:
```
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
```
Supón que tenemos `f7cb4ezEAl6C3wRaU6JORA==`, Entonces enviamos:
```
f7cb4ezEAl6C3wRaU6JORA==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
```
Usa sha1 para computar el valor binario y luego base 64 para codificarlo. Entonces tendremos:
```
rE91AJhfC+6JdVcVXOGJEADEJdQ=
```
Usa esto como valor en el encabezado de respuesta `Sec-WebSocket-Accept`.
## WebSocket en Go
La librería estándar de Go no provee soporte para WebSockets. Sin embargo el paquete `websocket` que es un subpaquete de `go.net` lo es, y es oficialmente mantenido mantenido y soportado.
Usa `go get` para instalar este paquete:
```
go get golang.org/x/net/websocket
```
Los WebSockets tienen lados de cliente y servidor. Veamos un ejemplo simple de un usuario ingresa alguna información en el lado del cliente y la envía al lado del servidor a través de un WebSocket, seguido por el servidor reenviando la información al cliente.
Lado del cliente:
```
<html>
<head></head>
<body>
<script type="text/javascript">
var sock = null;
var wsuri = "ws://127.0.0.1:1234";
window.onload = function() {
console.log("onload");
sock = new WebSocket(wsuri);
sock.onopen = function() {
console.log("connected to " + wsuri);
}
sock.onclose = function(e) {
console.log("connection closed (" + e.code + ")");
}
sock.onmessage = function(e) {
console.log("message received: " + e.data);
}
};
function send() {
var msg = document.getElementById('message').value;
sock.send(msg);
};
</script>
<h1>WebSocket Echo Test</h1>
<form>
<p>
Message: <input id="message" type="text" value="Hello, world!">
</p>
</form>
<button onclick="send();">Send Message</button>
</body>
</html>
```
Como puedes ver, es muy facil utilizar las funciones Javascript del lado del cliente para establecer la conección. El evento `onopen` es activado después de terminar exitosamente el proceso del apretón de manos. Esto le dice al cliente que la conexión ha sido creada exitosamente. El cliente trata de abrir una conexions usualmente ata estos cuatro eventos:
- 1onopen: activado cuando la conexión es establecida.
- 2onmessage: activado después de recibir un mensaje.
- 3onerror: activado cuando un error ha ocurrido.
- 4onclose: activuado cuando la conexión ha sido cerrada.
Código del servidor:
```
package main
import (
"golang.org/x/net/websocket"
"fmt"
"log"
"net/http"
)
func Echo(ws *websocket.Conn) {
var err error
for {
var reply string
if err = websocket.Message.Receive(ws, &reply); err != nil {
fmt.Println("Can't receive")
break
}
fmt.Println("Received back from client: " + reply)
msg := "Received: " + reply
fmt.Println("Sending to client: " + msg)
if err = websocket.Message.Send(ws, msg); err != nil {
fmt.Println("Can't send")
break
}
}
}
func main() {
http.Handle("/", websocket.Handler(Echo))
if err := http.ListenAndServe(":1234", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
```
Cuando un cliente envía la información, el cliente la recive (`Receive`) y usa un `Send` para enviar la respuesta.
![](images/8.2.websocket3.png?raw=true)
Figura 8.4 Servidor WebSocket recibiendo información.
A través de el ejemplo de arriba, podemos ver que la implemenación de un cliente y servior WebSocket es muy conveniente. Podemos usar el paquete `net` directamente en Go. Con el rápido desarrollo de HTML5, pienso que los WebSockets tendrán un rol mucho mas importante en las aplicaciones web modernas; deberíamos ser capaces al menos de estar familiarizados con ellos.
## Enlaces
- [Índice](preface.md)
- Sección previa: [Sockets](08.1.md)
- Siguiente sección: [REST](08.3.md)

123
es/08.3.md Normal file
View File

@@ -0,0 +1,123 @@
# 8.3 REST
REST es una arquitectura muy popular en internet hoy, porque esta en estándares bien definidos y estrictos y es facil de entender y expandir. Más y más sitios web están basando sus diseños encima de REST. En esta sección, vamos a echarle un vistaso de cerca a la implementación de una arquitectura rEST en Go y (esperando) aprender como usarla para nuestro beneficio.
## ¿Qué es REST?
La primera declaración del concepto de REST fue (REpresentational State Transfer) en el año 2000 en la disertación de doctorado de Roy Thomas Fielding, que también es el cofundador del protocolo HTTP. Este especifica las restricciones de arquitectura y los principios, y cualquier cosa implementada en esta arquitectura puede se llamada sistema RESTful.
Antes que entendamos lo que es REST, necesitamos entender los siguientes conceptos:
- Recursos
REST es la Capa de Presentación de la Transferencia de Estado, donde la capa de presentación actualmente es la capa de presentación del recurso.
Entonces ¿Qué son los recursos? Imagenes, documentos, videos, etc. Todos son ejemplos de recursos que pueden ser localizados por una URI.
- Representación
Los recursos son entidades de información específica que pueden ser mostrados de una variedad de maneras en la capa de presentación. Por ejemplo, un documento TXT puede ser representado como un HTML, JSON, XML, etc; una imagen puede representarse como jpg, png, etc.
Las URIs son utilizadas para indentificar recursos, pero ¿cómo determinamos la manifestación específica? Está referido a los encabezados HTTP Accept y Content-Type; estos dos campos describen la capa de presentación.
- Estado de transferencia
Un proceso interactivo es iniciado entre el cliente y el servidor cada vez que visitas una página de un sitio web. Durante este proceso, ciertos datos relacionados con la página actual necesitan ser guardados. Sin embargo, ¡HTTP es un protocolo sin estado! Es obvio que necesitamos guardar este estado del cliente del lado del servidor. Esto prosigue en que si el cliente modifica alguna información y quiere que los cambios persistan, debe exister una manera de informar al servidor sobre el nuevo estado.
La mayor parte del tiempo, los clientes le informan al servidor los cambios de estado usadno HTTP. Existen cuatro operaciones con las que se hace esto:
- GET es usado para obteer recursos
- POST es usado para crear o actualizar recursos
- PUT es usado para actualizar recursos
- DELETE elimina recursos
Para resumir lo anterior:
- (1) Caca URI representa un recurso.
- (2) Existe una capa de presentación para transferir recursos entre clientes y servidores.
- (3) Los clientes usan 4 métodos HTTP para implementar la "Capa de Presentación de Transferencia de Estado", permitiéndoles a ellos operar con recursos remotos.
El principio mas importande de las aplicaciones web que implementan REST es que la interacción entre clientes y servidores es sin estado; cada petición debe encapsular toda la información requerida. Los servidores deberían ser capaces de reiniciarse en cualquier momento sin que los clientes sean notificados. Además, las peticiones pueden ser respondidas por cualquier servidor del mismo servicio, lo cual es ideal para la computación en la nube. Por último, como es sin estado, los clientes pueden cachear la data para mejorar el rendimiento.
Otro principio importante de un sistema REST es la delaminación, que significa que una capa no tiene una manera de interactuar directamente con componentes de otras capaz. Esto puede limitar la complejidad del sistema y motivar la independencia entre componentes inferiores.
![](images/8.3.rest2.png?raw=true)
Figura 8.5 Arquitectura REST
Cuando las restricciones RESTful son acatadas juiciosamente, las aplicaciones web pueden escalar para acomodarse a un número masivo de clientes. Usando una arquitectura rEST también podemos reducir las demoras entre clientes y servidores, simplificando la arquitectura y mejorando la visibilidad de los susbistemas.
![](images/8.3.rest.png?raw=true)
Figure 8.6 Expansibilidad de REST
## Implementación RESTful
Go no tiene soporte durecto para REST, pero desde que las aplicaciones RESTful están basadas en HTTP, podemos usar el paquete `net/http` para implementarla nosotros mismos. Por supuesto, primero necesitamos hacer algunas modificaciones antes de implementar completamente REST.
REST usa diferentes métodos para manejar recursos, dependiendo de la interacción que es requerida por ese recurso. Muchas aplicaciones existentes dicen ser RESTful pero no implementan REST. Voy a categorizar estas aplicaciones en varios niveles dependiendo de que métdos HTTP implementan.
![](images/8.3.rest3.png?raw=true)
Figura 8.7 Niveles de REST
La figura de arriba nos muestra tres niveles actualmente implementados en REST. puedes escoger no seguir todas las reglas y restricciones de REST cuando estás desarrollando tus aplicaciones propias, porque a veces no encajan en todas las situaciones. Las aplicaciones RESTful usan todos los métodos HTTP incluyendo `DELETE` y `PUT`, pero en muchos casos, los clientes solo envían peticiones `GET` y `POST`.
- El estándar de HTML permite a los clientes enviar `GET` y `POST` a través de enlaces y formularios. No es posible enviar peticiones `PUT` o `DELETE` sin soporte de AJAX.
- Algunos firewalls interceptan peticiones `PUT` y `DELETE` y los clientes tienen `POST` para implementarlos. Los servicios completamente RESTful usan los métodos originales HTTP y los reestablecen.
Podemos simular las peticiones `PUT` y `DELETE` agregando un campo oculto llamado `_method` en nuestras peticiones `POST`, sin embargo estas peticiones tienen que ser convertidas en el servidor antes de que puedan ser procesadas. En mis aplicaciones personales, uso este flujo de trabajo para implmentar iterfaces REST. Las interfaces RESTful son fáciles de implementar en Go, como se puede ver en el siguiente ejemplo:
```
package main
import (
"fmt"
"github.com/drone/routes"
"net/http"
)
func getuser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprintf(w, "you are get user %s", uid)
}
func modifyuser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprintf(w, "you are modify user %s", uid)
}
func deleteuser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprintf(w, "you are delete user %s", uid)
}
func adduser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprint(w, "you are add user %s", uid)
}
func main() {
mux := routes.New()
mux.Get("/user/:uid", getuser)
mux.Post("/user/:uid", modifyuser)
mux.Del("/user/:uid", deleteuser)
mux.Put("/user/", adduser)
http.Handle("/", mux)
http.ListenAndServe(":8088", nil)
}
```
Este ejemplo muestra como puedes escribir aplicaciones REST muy básicas. Nuestros recursos son usuarios y usamos diferentes funciones para diferentes métods. Aquí, importamos un paquete de terceros llamado `github.com/drone/routes`. Ya hemos cubierto como implementar un manejador de rutas personalizado en capítulos anteriores. El paquete `drone/routes` implementa un mapeo de rutas muy conveniente de seguir y muy fácil para implementar arquitecturas REST. Como puedes ver, REST requiere que implementes diferente lógica para cada método HTTP de el mismo recurso.
## Resumen
REST es un estilo de arquitectura web, construido en las experiencias pasadas con WWW: sin estado, centrado en los recursos, uso completo de protocolos HTTP y URI y la provisión de interfaces unificadas. Estas consideraciones de diseño superior le han permitido a REST convertirse en estándar mas popular para servicios web. En cierta manera, al enfatizar la carga en las URIs y el uso de estándares tempranos de Internet como HTTP, REST a pavimentado el camino para aplicaciones grandes y escalables. Actualmente el soporte que Go da para REST es muy básico, sin embargo, al implementar rutas personalizads y diferentes manejadores de petición para cada tipo de petición HTTP, podemos alcanzar una arquitectura RESTful en nuestras aplicaciones web de Go.
## Enlaces
- [Índice](preface.md)
- Sección Previa: [WebSocket](08.2.md)
- Siguiente sección: [RPC](08.4.md)

400
es/08.4.md Normal file
View File

@@ -0,0 +1,400 @@
# 8.4 RPC
En secciones anteriores hablamos sobre como escribir aplicaciones de red basadas en Sockets y HTTP. Aprendimos que ambos usan un modelo de "intercambio de información", en donde los clientes envían peticiones y los servidores las responden. Este tipo de intercambio está basado en un formato específico que ambos lados conocen y saben como comunicarse con el. Sin embargo, existen muchas aplicaciones independientes que no usan este modelo, pero en vez de eso llaman servicios como si llamaran funciones normales.
RPC fue creado para ser el modo de llamado de funciones para sistemas de red. Los clientes ejecutan RPC como si fueran funciones nativas. excepto que empaquetan la función y los parámetros y los envían a través de la red a un servidor. El servidor puede desempaquetar los parámetros y procesar la petición, ejecutando los resultados de vuelta hacia el cliente.
En ciencias de la computación, un llamado de procedimiento remoto (Remote Call Procedure RPC) es un tipo de comunicación inter proceso que permite a un programa de computador ejecutar una subrutina o precedimiento en otro espacio de direcciones (usualmente otro computador en un red compartida) sin que el programador específicamente codifique los detalles para esta interacción remota. Esto es, el programador escribe el mismo código ya sea que la subrutina se ejecute local o remotamente. Cuando el software en cuestión usa principios orientados a objetos, el RPC es llamado invocación remota o invocación remota de métodos.
## Principio de trabajo de RPC
![](images/8.4.rpc.png?raw=true)
Figura 8.8 Principio de trabajo de RPC
Normalmente, un llamado RPC de un cliente a un servidor tiene diez pasos:
- 1. Llamar al manejador del cliente que ejecute la transmisieon de los argumentos.
- 2. Llamar al kernel del sistema para enviar mensajes de red.
- 3. Enviar mensajes a los hosts remotos.
- 4. El servidor recibe y maneja los argumentos.
- 5. Se ejecuta el proceso remoto.
- 6. Se retorna el resultado de la ejecución al manejador.
- 7. El servidor maneja la llamada a un kernel remoto.
- 8. El mensaje es enviado al kernel local.
- 9. El cliente maneja el mensaje del kernel.
- 10. El cliente obtiene los resultados del manejador correspondiente.
## RPC en Go
Go tiene soporte oficial para RPC en su librería estándar en tres niveles, que son TCP, HTTP y JSON. Nota que Go RPC no es como otro sistema tradicional RPC. Requiere que uses aplicaciones Go en cliente y servidor porque codifica el contenido usando Gob.
Funciones que Go RPC deben seguir las siguientes reglara para tener acceso remoto, de otra manera, las llamadas serán ignoradas.
- Las funciones deben ser exportadas (Primera letra en mayúscula)
- Las funciones deben tener dos argumentos con tipos exportados.
- El primer argumento es para recibir del cliente y el segundo debe ser un puntero y debe ser para responderle al cliente.
- Las funciones deben tener valor de reporte o un tipo de error.
Por ejemplo:
```
func (t *T) MethodName(argType T1, replyType *T2) error
```
Donde T, T1 y T2 deben estar codificados en el paquete `package/gob`.
Cualquier tipo de RPC tiene que pasar por la red para transferir datos. Go RPC puede usar HTTP o TCO. Los beneficios de usar HTTP es que puedes reusar algunas funciones del paquete `net/http`.
### HTTP RPC
Lado del servidor HTTP:
```
package main
import (
"errors"
"fmt"
"net/http"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func main() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
err := http.ListenAndServe(":1234", nil)
if err != nil {
fmt.Println(err.Error())
}
}
```
Registramos un servicio RPC de Arith, luego registramos este servicio a través de `rpc.HandleHTTP`. Después de eso, somos capaces de transferir datos a través de HTTP.
Código del lado del cliente:
```
package main
import (
"fmt"
"log"
"net/rpc"
"os"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ", os.Args[0], "server")
os.Exit(1)
}
serverAddress := os.Args[1]
client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
if err != nil {
log.Fatal("dialing:", err)
}
// Sincronizamos la llamada
args := Args{17, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
var quot Quotient
err = client.Call("Arith.Divide", args, &quot)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
}
```
Compilamos el cliente y el servidor separadamente y luego iniciamos el servidor y el cliente. Entonces puedes tener algo similar después que ingreses algunos datos.
```
$ ./http_c localhost
Arith: 17*8=136
Arith: 17/8=2 remainder 1
```
Como puedes ver, definimos una estructura para el tipo de retorno. La usamos como argumento de función en el lado del servidor, y como el tipo del segundo y tercer argumento del cliente `client.Call`. Esta llamada es muy importante. Tiene tres argumentos, donde el primero es el nombre de la función que va a ser llamado, el segundo es el argumento que quieres pasar y el último es el valor de retorno (de tipo puntero). Hasta aquí podemos ver que es fácil implementar RPC en Go.
### TCP RPC
Vamos a probar el RPC que está basado en TCP, aquí esta el código del servidor:
```
package main
import (
"errors"
"fmt"
"net"
"net/rpc"
"os"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func main() {
arith := new(Arith)
rpc.Register(arith)
tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
rpc.ServeConn(conn)
}
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
```
La diferencia entre HTTP RPC y TCP RPC es que tenemos que controlar las conexiones por nuestra propia cuenta si usamos TCP RPC, entonces pasamos las conexiones para el procesamiento RPC.
Como puedes adivinar, este puede ser un patrón que se bloquea. Eres libre de usar rutinas para esta aplicación en un experimento mas avanzado.
Código del lado del cliente:
```
package main
import (
"fmt"
"log"
"net/rpc"
"os"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ", os.Args[0], "server:port")
os.Exit(1)
}
service := os.Args[1]
client, err := rpc.Dial("tcp", service)
if err != nil {
log.Fatal("dialing:", err)
}
// Sincronizamos las llamadas
args := Args{17, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
var quot Quotient
err = client.Call("Arith.Divide", args, &quot)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
}
```
La única diferencia en el lado del cliente es que los clientes HTTP usan DialHTTP donde los clientes TCP usan Dial(TCP).
### JSON RPC
JSON RPC codifica la información en JSON en vez de Gob. Veamos un ejemplo de Go usando JSON RPC en el servidor.
```
package main
import (
"errors"
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"os"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
func main() {
arith := new(Arith)
rpc.Register(arith)
tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
jsonrpc.ServeConn(conn)
}
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
```
JSON RPC está basado en TCP y no soporta HTTP todavía.
El código del cliente:
```
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
"os"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ", os.Args[0], "server:port")
log.Fatal(1)
}
service := os.Args[1]
client, err := jsonrpc.Dial("tcp", service)
if err != nil {
log.Fatal("dialing:", err)
}
// Sincronizamos llamadas
args := Args{17, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)
var quot Quotient
err = client.Call("Arith.Divide", args, &quot)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
}
```
## Resumen
Go tiene buen soporte para implementaciones de RPC sobre HTTP, TCP y JSON, que nos permite fácilmente desarrollar aplicaciones we distrubidas: sinembargo, es lamentable que Go no tenga soporte para SOAP RPC incluido, sin embargo algunas paquetes de terceros de código abierto ofrecen esto.
## Enlaces
- [Índice](preface.md)
- Sección Previa: [REST](08.3.md)
- Siguiente Sección: [Summary](08.5.md)

11
es/08.5.md Normal file
View File

@@ -0,0 +1,11 @@
# 8.5 Resumen
En este capítulo, he introducido muchos conceptos conocidos de modelos de desarrollo de aplicaciones web. En la seccieon 8.1 describimos el funcionamiento básico de programación por sockets. Por la revolución acelerada de las tecnologías de red e infraestructura, y dado que los Sockets son una piedra angular de estos cambios, debes especializarte en estos conceptos en orden de ser un desarrollador web competente. En la sección 8.2 hablamos los WebSockets de HTML5 que soporta comunicación en ambas vías entre el cliente y el servidor elimitando la necesidad del polling con AJAX. En la sección 8.3 implementamos una aplicación usando la arquitectura REST, que particularmente es muy apropiada para el desarrollo de APIs sobre la red; gracias al rápido crecimiento de las aplicaciones móviles, creo que las APIs RESTful pueden ser una tendencia que siga. En la sección 8.4, hablamos sobre RPC en Go.
Go provee un excelente soporte para estos cuatro tipos de métodos de desarrollo. Nota que el paquete `net` y sus subpaquetes es el lugar donde Go coloca las herramientas de red. Si quieres un conocimiento mas profundo de los detalles de implementación, deberías leer el código fuente de esos paquetes.
## Enlaces
- [Índice](preface.md)
- Sección previa: [RPC](08.4.md)
- Siguiente Capítulo: [Seguridad y Encripción](09.0.md)