MeBlogProjects
September 10, 2024

Implementing HTTP SSE Streaming golang a simpler Approach to Real-Time Updates

In modern web development, implementing real-time features is crucial for enhancing user experience. Whether it's real-time notifications, live data updates, or continuous status tracking, there are multiple ways to achieve streaming in the web, each with its pros and cons. WebSockets, for example, is a common choice for full-duplex communication between the client and server. However, in scenarios where only one-way communication is needed, implementing WebSockets can be overly complex and resource-intensive.

In the development of JustDeploy a tool designed to simplify Docker deployments I encountered a need to stream real-time deployment status updates to the client. Initially, I considered using WebSockets, but it became apparent that the complexity and overhead associated with bidirectional communication were unnecessary for this use case. Instead, I opted for Server-Sent Events (SSE), a simpler and more efficient method for unidirectional streaming over HTTP.

Why Choose Server-Sent Events (SSE)?

Server-Sent Events (SSE) is a technology that allows the server to push updates to the client over a single HTTP connection. Unlike WebSockets, SSE is inherently a unidirectional protocol, making it ideal for scenarios where the server needs to continuously send data to the client, but the client does not need to send data back.

Here are a few reasons why SSE was the right choice for JustDeploy:

  1. Simplicity: SSE uses a standard HTTP connection, which means it's easier to implement and doesn't require a complex protocol handshake as with WebSockets.
  2. Unidirectional Communication: Since my requirement was only to stream data from the server to the client (without the need for client-to-server communication), SSE perfectly suited the need.
  3. Automatic Reconnection: SSE handles automatic reconnection, which is a significant advantage for maintaining a persistent connection without extra client-side logic.
  4. Wide Browser Support: SSE is supported by most modern browsers, making it a reliable choice for web applications.

Implementing SSE in JustDeploy

Let's dive into the code implementation of SSE in JustDeploy. The goal was to stream deployment status updates from the server to the client as they occur.

SSE Endpoint Setup

func SubscriptionCreateDeployLoadingState(deployService \*service.DeployService) echo.HandlerFunc {
    return func(c echo.Context) error {
        w := c.Response()

    	// Set necessary headers for SSE
    	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", "*")

    	for {
    		select {
    		case <-c.Request().Context().Done():
    			// If the client closes the connection, exit the loop
    			return nil
    		case event := <-deployService.EventAdapter.EventDeployWrapper:
    			// Send the event to the client
    			fmt.Println("Sending event", event)
    			event.MarshalToSseEvent(w)
    			w.Flush() // Ensure the event is immediately sent to the client
    		}
    	}
    }
}

Explanation:

  • Headers Configuration: We start by setting the necessary HTTP headers. Content-Type is set to text/event-stream to indicate that the response will be streamed as SSE. Other headers like Cache-Control, Connection, and Access-Control-Allow-Origin are set to ensure the connection remains open and accessible across different domains.

  • Event Loop: The function enters an infinite loop where it waits for events from the deployService.EventAdapter.EventDeployWrapper channel. This channel is where deployment events are sent as they happen.

  • Context Handling: The loop checks if the client's request context is done, which happens when the client closes the connection. If so, the function returns, ending the SSE stream.

  • Sending Events: When a new event is received from the channel, it's marshaled into an SSE format and sent to the client. The w.Flush() method is called to push the event to the client immediately, ensuring that the client receives real-time updates without delay.

Defining the Event Structure

To structure the deployment events, I used the following EventDeployWrapper type:

type EventDeployWrapper struct {
	DeployName       string        `json:"deployName"`
    DeployId         string        `json:"deployId"`
    EventsDeployList []EventServer `json:"eventsDeployList"`
    CurrentStep      int           `json:"currentStep"`
}
  • DeployName and DeployId: These fields identify the deployment.
  • EventsDeployList: This is a list of events associated with the deployment process, allowing the client to understand each step of the deployment.
  • CurrentStep: This indicates the current step in the deployment process.

For example, here's how an EventDeployWrapper is initialized with the events and current step:

eventsList := []adapter.EventServer{
	{
		Title:     "Build your application",
		EventType: "create_deploy",
	},
	{
		Title:     "Pull the traefik image",
		EventType: "create_deploy",
	},
	{
		Title:     "Run the traefik router",
		EventType: "create_deploy",
	},
	{
		Title:     "Start your application",
		EventType: "create_deploy",
	},
}
eventWrapper := adapter.EventDeployWrapper{
	DeployName:       deploy.Name,
	DeployId:         deploy.Id,
	EventsDeployList: eventsList,
	CurrentStep:      0,
}

This setup allows JustDeploy to send structured and meaningful updates to the client as the deployment progresses, giving users real-time feedback on the status of their deployments.

Handling Errors

In case of errors during deployment, the SetStepError method is used to update the event wrapper with error information:

eventWrapper.SetStepError(err.Error())
deployService.EventAdapter.SendNewDeployEvent(eventWrapper)

This ensures that the client is informed immediately if something goes wrong, helping users to diagnose issues quickly.

Conclusion

Implementing real-time streaming in web applications can be achieved in various ways, but it's essential to choose the right tool for the job. In JustDeploy, Server-Sent Events (SSE) proved to be an ideal solution for streaming deployment status updates to the client. By leveraging the simplicity and efficiency of SSE, I was able to implement a robust and maintainable real-time feature with minimal overhead, ensuring that users receive up-to-date information on their deployments without the complexity of WebSockets.

Thanks you for reading ! 😄
You can checkout my other posts here, And if you want to be update for a new post you can enter your email below