Hoje vou mostrar em poucas linhas de código como criar um Websocket usando um pacote padrão no Go (Golang) chamado net/http e a implementação de Websocket da Gorilla Web toolkit.
Este post parte do principio que você já tem o setup mínimo Go configurado (instalando o compilador, criado a estrutura de diretórios e declarado a variável GOPATH).
O que são Websockets?
WebSockets é um protocolo de comunicação bidirecional que permite comunicação real-time entre um navegador web e um servidor. Ele foi criado para fornecer uma alternativa mais eficiente às conexões HTTP long polling e HTTP Streaming, que são utilizadas para criar aplicativos com comunicação em tempo real.
Ao contrário das conexões HTTP tradicionais, onde o cliente precisa fazer uma solicitação e o servidor precisa enviar uma resposta, as conexões WebSockets são bidirecionais e permitem que o cliente e o servidor envie mensagens uns aos outros sem precisar de uma solicitação explícita.
Portanto as aplicações se tornam mais interativas e dinâmicas, como jogos online, chats, aplicações de monitoramento em tempo real, etc.
Para estabelecer uma conexão WebSocket, um navegador web envia uma solicitação HTTP de Upgrade para o servidor, indicando que deseja atualizar para uma conexão WebSocket. Assim, se o servidor suportar WebSockets e concordar com a atualização, ele responderá com uma resposta de Upgrade e a conexão será estabelecida.
Após isso, ambos cliente e servidor podem então enviar mensagens uns aos outros através da conexão WebSocket aberta.
WebSockets é suportado por quase todos os navegadores modernos e também tem suporte a várias linguagens de programação no lado do servidor, incluindo Go, Java, C#, Python, e JavaScript.
Implementando Websockets
Passo um: Construindo um servidor Web em Go
Assumindo que você já esteja dentro do diretório da sua aplicação e tenha criando um arquivo main.go, vamos partir da seguinte estrutura:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello!")
}
Vamos começar a implementar nosso servidor Web usando o net/http. Para isso, precisamos declarar um path para a nossa aplicação responder as requisições em um handler para recebê-las e tratá-las:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", handler) // Aqui declaramos o path e o Handler
http.ListenAndServe(":3000", nil) // Aqui ficará a porta em que nosso Web server vai responder
}
// Aqui vamos tratar nossa requisição: o writer irá permitir
// definir o que desejamos escrever de volta para o client que enviou a requisiçao.
func handler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Hello")
}
Neste ponto do código, já possuímos tudo que precisamos para rodar nossa aplicação Web. Rodando um go run main.go, já podemos ver:
Passo dois: Lidando com chamadas via Websocket
Como citei na introdução, vamos usar o pacote Websocket da Gorilla Web Toolkit. Vale dar uma olhada na documentação atualizada para instalar a dependência, mas por hora só precisamos fazer um go get:
go get github.com/gorilla/websocket
A próxima coisa que precisamos é adicionar um websocket.Upgrader ao nosso código. É através dele que vamos receber o socket para lidarmos com as chamadas.
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
Uma das propriedades do upgrader é checarmos a origem do tráfego. Por ora, vamos sempre retornar true e assim seguirmos com o post.
Agora vamos alterar nossa função handler para finalmente lermos a mensagem via Websocket, logarmos no console do Web server e, para propósito de demonstração, vamos retornar a mesma mensagem de volta para o cliente:
package main
import (
"fmt"
"github.com/gorilla/websocket"
"net/http"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":3000", nil)
}
func handler(writer http.ResponseWriter, request *http.Request) {
socket, err := upgrader.Upgrade(writer, request, nil)
if err != nil {
fmt.Println(err)
}
for {
// Vamos ler a mensagem recebida via Websocket
msgType, msg, err := socket.ReadMessage()
if err != nil {
fmt.Println(err)
return
}
// Logando no console do Webserver
fmt.Println("Mensagem recebida: ", string(msg))
// Devolvendo a mensagem recebida de volta para o cliente
err = socket.WriteMessage(msgType, msg)
if err != nil {
fmt.Println(err)
return
}
}
}
Para testarmos se tudo funcionou, primeiro vamos levantar o web server via:
go run main.go
Enfim, agora podemos usar Echo Test da websocket.org. Só precisamos alterar a Location apontando para o localhost:3000, como abaixo:
Feito isso, podemos conectar no nosso Websocket e enviar mensagens, recebendo as mesmas como resposta. E no console do Web server podemos ver os logs chegando:
Mensagem recebida: Rock it with HTML5 WebSocket
Cuidados ao usar Websockets
Existem alguns cuidados ao usar WebSockets em sua aplicação:
- Segurança: Como as conexões WebSockets são bidirecionais e permitem comunicação direta entre cliente e servidor, certamente é importante garantir que a comunicação esteja criptografada para evitar ataques de interceptação de dados. Aliás, isso pode ser feito usando o protocolo HTTPS (HTTP Secure) sobre o WebSocket.
- Firewall: Muitos firewalls são configurados para bloquear conexões WebSockets, portanto, é importante garantir que as portas necessárias estejam abertas para sua aplicação.
- Estado da conexão: Uma vez que as conexões WebSockets são longas duras, é importante garantir que as conexões não fiquem presas em um estado inválido por muito tempo. Isso pode ser feito implementando uma lógica de tempo limite para as conexões e garantindo que elas sejam fechadas corretamente quando não são mais necessárias.
- Escalabilidade: Como as conexões WebSockets são mais intensivas em recursos do que as conexões HTTP tradicionais, é importante garantir que sua aplicação possa escalar para lidar com um grande número de conexões simultaneamente.
- Compatibilidade do navegador: WebSockets é suportado por quase todos os navegadores modernos, mas alguns navegadores mais antigos ainda não oferecem suporte completo a ele. Certifique-se de que sua aplicação ofereça uma alternativa para navegadores que não suportam WebSockets.
- Fallback: Seu aplicação deve ser preparada para lidar com a situação quando o websocket é desconectado por algum motivo, como por exemplo, mudanças no firewall, ou o usuário sair da página. A solução recomendada é ter um fallback para outra forma de comunicação como o polling ou long polling.
Além disso, é importante monitorar e testar continuamente sua aplicação para garantir que ela esteja funcionando corretamente e para detectar e resolver problemas rapidamente.