Build Real-Time Dashboards with Server-Sent Events (SSE) in Go (Golang) – Step-by-Step Guide

Build Real-Time Dashboards with Server-Sent Events (SSE) in Go (Golang) – Step-by-Step Guide

Tuesday, October 29, 2024
~ 8 min read
Learn how to implement Server-Sent Events (SSE) in Go (Golang) with a real-time example that streams CPU and memory data to a browser. Build live dashboards effortlessly with this step-by-step tutorial.

How to Implement Server-Sent Events (SSE) in Go: A Beginner’s Guide

Server-Sent Events (SSE) is a simple way to push updates from the server to the client in real time. In this article, we’ll walk through how to build a Server-Sent Events (SSE) example using Golang. This guide will show how to set up the Go environment, write the code, and run the server to stream CPU and memory data live to a webpage.

Let’s dive into it step-by-step.


What You’ll Learn

  • How to set up a Go project from scratch
  • Implement SSE in Golang using net/http package
  • Monitor system resources (CPU and Memory)
  • Display real-time updates in a browser with SSE

Prerequisites

  • Basic knowledge of Go (Golang) and HTML
  • Go installed on your machine (you can download it from Go’s official site)
  • Git installed (optional, but helpful for Go module management)

Step 1: Initialize Your Go Project

First, set up a Go project to manage dependencies.

mkdir go-sse-example
cd go-sse-example
go mod init example.com/go-sse

This command creates a new Go project and initializes a module named example.com/go-sse.


Step 2: Install Dependencies

We will use the gopsutil library to monitor CPU and memory usage. Install it using the following command:

go get github.com/shirou/gopsutil/v4

This package provides utilities to fetch system statistics like CPU and memory usage.


Step 3: Write the SSE Server Code

Create a main.go file in your project folder and paste the following code:

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/shirou/gopsutil/v4/cpu"
    "github.com/shirou/gopsutil/v4/mem"
)

// ConvertBytesToGBDecimal converts bytes to GB (decimal system).
func ConvertBytesToGBDecimal(bytes uint64) float64 {
    return float64(bytes) / (1000 * 1000 * 1000)
}

func main() {
    http.HandleFunc("/events", sseHandler)

    // Start the server on port 8080
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatalf("unable to start server: %s", err.Error())
    }
}

func sseHandler(w http.ResponseWriter, r *http.Request) {
    // Setup SSE headers
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    w.Header().Set("Access-Control-Allow-Origin", "*")

    // Create timers for memory and CPU updates
    memTicker := time.NewTicker(time.Second)
    defer memTicker.Stop()

    cpuTicker := time.NewTicker(time.Second)
    defer cpuTicker.Stop()

    clientGone := r.Context().Done()

    // Flush the response to ensure the client receives data continuously
    rc := http.NewResponseController(w)

    for {
        select {
        case <-clientGone:
            fmt.Println("Client disconnected")
            return
        case <-memTicker.C:
            memoryStats, err := mem.VirtualMemory()
            if err != nil {
                log.Printf("Failed to get memory stats: %s", err.Error())
                return
            }
            data := fmt.Sprintf(
                "Total: %.2f GB, Used: %.2f GB, Percentage: %.2f%%",
                ConvertBytesToGBDecimal(memoryStats.Total),
                ConvertBytesToGBDecimal(memoryStats.Used),
                memoryStats.UsedPercent,
            )
            if _, err := fmt.Fprintf(w, "event:mem\ndata:%s\n\n", data); err != nil {
                log.Printf("Failed to write memory event: %s", err.Error())
                return
            }
            rc.Flush()
        case <-cpuTicker.C:
            cpuStats, err := cpu.Times(false)
            if err != nil {
                log.Printf("Failed to get CPU stats: %s", err.Error())
                return
            }
            data := fmt.Sprintf(
                "User: %.2f, System: %.2f, Idle: %.2f",
                cpuStats[0].User, cpuStats[0].System, cpuStats[0].Idle,
            )
            if _, err := fmt.Fprintf(w, "event:cpu\ndata:%s\n\n", data); err != nil {
                log.Printf("Failed to write CPU event: %s", err.Error())
                return
            }
            rc.Flush()
        }
    }
}

Explanation

  1. SSE Handler: Sends memory and CPU stats to the client every second.
  2. NewTicker: Used to trigger events every second for both CPU and memory.
  3. Flush: Ensures the server pushes events without delay.
  4. Client Disconnect Handling: Listens for the client's disconnection using r.Context().Done().

Step 4: Create the HTML Frontend

Now, let’s create a simple webpage to display the data. Create a file named index.html in the project folder and add the following code:

<!doctype html>
<html>
  <body>
    <h2>Real-time System Monitoring</h2>
    <p>Memory Usage: <span id="mem"></span></p>
    <p>CPU Usage: <span id="cpu"></span></p>

    <script type="text/javascript">
      const eventSource = new EventSource("http://127.0.0.1:8080/events");

      const memSpan = document.getElementById("mem");
      const cpuSpan = document.getElementById("cpu");

      eventSource.addEventListener("mem", (event) => {
        memSpan.textContent = event.data;
      });

      eventSource.addEventListener("cpu", (event) => {
        cpuSpan.textContent = event.data;
      });

      eventSource.onerror = (err) => {
        console.error("SSE error", err);
      };
    </script>
  </body>
</html>

Explanation

  • EventSource: Connects to the SSE endpoint /events to receive updates.
  • Event Handlers: Listen for mem and cpu events and update the respective HTML elements.
  • Error Handling: Logs errors if the SSE connection fails.

Step 5: Run the Go Server

Make sure both main.go and index.html are in the same directory. Now, run the Go server using:

go run main.go

The server will start on http://127.0.0.1:8080.


Step 6: Open the Frontend

Open the index.html file in your browser. You should see real-time memory and CPU usage being displayed.


Common Issues and Troubleshooting

  1. CORS Error: Ensure the Access-Control-Allow-Origin header is set to "*".
  2. SSE Not Working: Make sure your browser supports SSE (most modern browsers do).
  3. Firewall Issues: If accessing the server remotely, ensure port 8080 is open.

Conclusion

In this tutorial, we implemented a Server-Sent Events (SSE) example in Golang that streams real-time CPU and memory statistics to a web page. SSE is a powerful and simple way to push updates from the server without needing complex WebSocket setups.

This project serves as a foundation for building real-time dashboards or monitoring tools. You can further expand this example by adding disk usage, network stats, or even integrating with cloud monitoring tools.


Keywords

  • Server-Sent Events (SSE) in Golang
  • Real-time monitoring with Go
  • SSE example in Go
  • Build live dashboards with Golang
  • Push updates from server to browser using SSE

Try it out and let me know in comment if you encounter any issues!

Post a comment

Comments

Join the conversation and share your thoughts! Leave the first comment.

Get your FREE PDF on "100 Ways to Try ChatGPT Today"

Generating link, please wait for: 60 seconds

Checkout all hot deals now 🔥

Search blogs

No blog posts found