WebSocket em Go: criação de aplicações real-time de nível avançado

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

WebSocket em Go: técnicas avançadas para aplicações real-time

\n\n

O desenvolvimento de aplicações real-time em Go é um dos pontos fortes da linguagem. O suporte embutido para concorrência através de goroutines e canais torna Go a escolha ideal para servidores WebSocket. No entanto, quando se trata de sistemas de produção com milhares de conexões simultâneas, o simples exemplo "Hello, WebSocket" deixa de funcionar. Neste artigo, abordaremos técnicas avançadas: desde a otimização do pool de conexões até a escalabilidade através de brokers de mensagens.

\n\n

1. Arquitetura de pool de conexões: gerenciando milhares de clientes

\n\n

No exemplo básico, criamos uma goroutine separada para cada conexão. Isso é aceitável para 100-200 clientes, mas com 10.000+ conexões, o consumo de memória e a sobrecarga de troca de contexto tornam-se críticos. A abordagem avançada é usar um pool de conexões com um número fixo de workers e canais.

\n\n
// Pool — pool de conexões com limitação de goroutines\ntype Pool struct {\n    connections map[*websocket.Conn]bool\n    broadcast   chan []byte\n    register    chan *websocket.Conn\n    unregister  chan *websocket.Conn\n}\n\nfunc NewPool(maxGoroutines int) *Pool {\n    return &Pool{\n        connections: make(map[*websocket.Conn]bool),\n        broadcast:   make(chan []byte, 256),\n        register:    make(chan *websocket.Conn),\n        unregister:  make(chan *websocket.Conn),\n    }\n}\n\nfunc (pool *Pool) Start() {\n    for {\n        select {\n        case conn := <-pool.register:\n            pool.connections[conn] = true\n        case conn := <-pool.unregister:\n            if _, ok := pool.connections[conn]; ok {\n                delete(pool.connections, conn)\n                conn.Close()\n            }\n        case message := <-pool.broadcast:\n            for conn := range pool.connections {\n                go func(c *websocket.Conn) {\n                    if err := c.WriteMessage(websocket.TextMessage, message); err != nil {\n                        pool.unregister <- c\n                    }\n                }(conn)\n            }\n        }\n    }\n}
\n\n

Por que isso funciona? Os canais atuam como buffers, prevenindo bloqueios. Um único select gerencia todos os eventos, e as goroutines para escrita são criadas apenas quando necessário. Isso reduz a carga de pico no GC (coletor de lixo).

\n\n

2. Desligamento gracioso e gerenciamento de estado das conexões

\n\n

Ao reiniciar o servidor, é importante fechar corretamente todas as conexões WebSocket, enviando aos clientes uma mensagem de encerramento. Ignorar esta etapa leva a conexões "penduradas" e vazamentos de memória.

\n\n
// Server — servidor WebSocket com desligamento gracioso\ntype Server struct {\n    pool    *Pool\n    httpSrv *http.Server\n}\n\nfunc (s *Server) Shutdown(ctx context.Context) error {\n    // 1. Fecha o servidor HTTP (novas conexões não são aceitas)\n    if err := s.httpSrv.Shutdown(ctx); err != nil {\n        return err\n    }\n\n    // 2. Envia a todos os clientes uma mensagem de encerramento\n    shutdownMsg := []byte(`{"type":"shutdown","message":"server is going down"}`)\n    s.pool.broadcast <- shutdownMsg\n\n    // 3. Fecha todas as conexões no pool\n    for conn := range s.pool.connections {\n        conn.WriteMessage(websocket.CloseMessage, \n            websocket.FormatCloseMessage(websocket.CloseNormalClosure, "server shutdown"))\n        conn.Close()\n    }\n\n    return nil\n}\n\n// Uso no main\nfunc main() {\n    pool := NewPool(1000)\n    go pool.Start()\n\n    srv := &Server{pool: pool}\n    srv.httpSrv = &http.Server{Addr: ":8080"}\n\n    // Tratamento de sinais\n    go func() {\n        sigint := make(chan os.Signal, 1)\n        signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)\n        <-sigint\n        ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n        defer cancel()\n        srv.Shutdown(ctx)\n    }()\n\n    log.Fatal(srv.httpSrv.ListenAndServ                

Blogs

Book Recommendations