Sling Academy
Home/Golang/Designing WebSocket-Based Pub/Sub Systems in Go

Designing WebSocket-Based Pub/Sub Systems in Go

Last updated: November 26, 2024

WebSocket-based Publish/Subscribe (Pub/Sub) systems offer a robust solution for real-time communication scenarios. In this article, we will explore how to design and implement such systems using Go, a powerful language for backend development.

Understanding WebSocket and Pub/Sub

WebSockets enable two-way communication channels over a single TCP connection, which is ideal for real-time data transfer. The Pub/Sub model facilitates efficient data broadcasting wherein a publisher sends messages which are then received by subscribers.

Setting Up a Basic WebSocket Server in Go

Let's start by setting up a basic WebSocket server using the Gorilla WebSocket library which is popular in the Go community.


package main

import (
    "fmt"
    "net/http"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
       return true
    },
}

func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer ws.Close()

    for {
        _, msg, err := ws.ReadMessage()
        if err != nil {
            log.Println("read:", err)
            break
        }
        fmt.Printf("Received: %s\n", msg)
    }
}

func main() {
    http.HandleFunc("/ws", handleConnections)
    log.Println("Starting server on :8080")
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

This code creates a simple WebSocket server listening on port 8080, accepting connections, and reading messages sent by clients.

Implementing the Publish/Subscribe Pattern

To implement the Pub/Sub pattern, we need to extend our WebSocket server to handle multiple clients and broadcast messages. We can manage clients and their subscriptions using channels in Go.


var clients = make(map[*websocket.Conn]bool)
var broadcast = make(chan Message)

type Message struct {
    Email   string `json:"email"`
    Content string `json:"content"`
}

func handleMessages() { 
    for {
        msg := <-broadcast
        for client := range clients {
            err := client.WriteJSON(msg)
            if err != nil {
                log.Printf("error: %v", err)
                client.Close()
                delete(clients, client)
            }
        }
    }
}

func handleConnections(w http.ResponseWriter, r *http.Request) {
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer ws.Close()
    clients[ws] = true

    for {
        var msg Message
        err := ws.ReadJSON(&msg)
        if err != nil {
            log.Printf("error: %v", err)
            delete(clients, ws)
            break
        }
        broadcast <- msg
    }
}

In this improved version, the server listens to the broadcast channel and sends incoming messages to all connected clients. Each client connects via WebSocket and starts sending messages. Anytime a message is sent, it gets broadcasted to all subscribed clients.

Testing Your WebSocket Pub/Sub System

We can test our Pub/Sub system using a simple HTML page that connects to the WebSocket server and sends messages.





    WebSocket Test


    

    Send
    
        var ws = new WebSocket('ws://localhost:8080/ws');
        var output = document.getElementById('output');
        ws.onmessage = function(event) {
            output.value += event.data + '\n';
        };

        function sendMessage() {
            var msg = {
                email: "[email protected]",
                content: document.getElementById('message').value
            };
            ws.send(JSON.stringify(msg));
        }
    


This simple frontend connects to our Go WebSocket server, allowing users to send and receive messages in real-time, thereby testing the Pub/Sub mechanism.

Conclusion

WebSocket and the Pub/Sub model are effective for building real-time applications. Using Go, you can build efficient and scalable real-time applications leveraging this architecture. Experiment with the examples provided to deepen your understanding and explore further enhancements like message persistence, security, or more advanced subscription management.

Next Article: Exploring WebSocket Load Balancing and Clustering in Go

Previous Article: Testing WebSocket Applications in Go: Tools and Techniques

Series: Websocket & Chat Programs in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant