当前位置: 首页 > article >正文

[Go实战]:SSE消息推送

前言

在现代Web开发中,前后端分离已成为主流趋势。为了实现实时数据推送,Server-Sent Events (SSE) 是一种高效且易于实现的技术。本文将介绍如何在Go语言中实现SSE服务端,并在前端使用JavaScript进行集成,实现一个完整的实时数据推送系统

1. SSE简介

Server-Sent Events (SSE) 是HTML5的一项技术,用于服务器向浏览器自动发送更新信息。SSE的主要特点包括:

  • 单向通信:数据流是从服务器到客户端的单向流动。
  • 自动重连:如果连接断开,客户端会自动尝试重新建立连接。
  • 简单易用:相比WebSocket,SSE的API更简单,更容易上手。
  • 文本格式:SSE的数据是以文本形式发送的,通常为JSON或纯文本。

2. 后端实现

2.1 设置静态文件目录

首先,我们需要设置静态文件目录,以便前端页面和其他静态资源可以被正确加载。

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

func main() {
	// 设置静态文件目录
	fs := http.FileServer(http.Dir("./static"))
	http.Handle("/", fs)

	// 设置 SSE 处理器
	http.HandleFunc("/events", handleEvents)

	// 启动服务器
	http.ListenAndServe(":8080", nil)
}

2.2 设置SSE处理器

接下来,我们创建一个SSE处理器来处理客户端的SSE请求,并设置正确的响应头。

func handleEvents(w http.ResponseWriter, r *http.Request) {
	// 设置 CORS 头部
	//w.Header().Set("Access-Control-Allow-Origin", "*")
	//w.Header().Set("Access-Control-Allow-Methods", "GET")
	//w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

	// 设置响应头
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")

	// 模拟数据流
	for {
		// 生成推送消息
		data, _ := json.Marshal(map[string]string{"timestamp": time.Now().Format(time.RFC3339)})
		_, err := fmt.Fprintf(w, "data: %s\n\n", data)
		if err != nil {
			// 客户端断开连接,输出日志
			fmt.Println("Client disconnected:", err)
			return
		}

		// 刷新缓冲区
		if flusher, ok := w.(http.Flusher); ok {
			flusher.Flush()
		}

		// 检查是否应该关闭连接
		select {
		case <-r.Context().Done():
			return
		default:
			time.Sleep(2 * time.Second) // 每2秒发送一次消息
		}
	}
}

2.3 启动HTTP服务器

在上面的代码中,我们定义了两个路由:

  • /:处理静态文件请求。
  • /events:处理SSE请求。

3. 前端实现

3.1 创建前端页面

创建一个名为 index.html 的文件,用于展示接收到的消息。将该文件放在 main.go 同级地 static 目录下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE 案例</title>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            height: 100%;
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #f0f0f0;
        }

        .container {
            text-align: center;
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            max-width: 600px;
            width: 100%;
            box-sizing: border-box;
        }

        h1 {
            color: #333;
            margin-bottom: 20px;
        }

        #messages {
            height: 300px; /* 固定高度 */
            overflow-y: auto;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 10px;
            background-color: #f9f9f9;
            color: #555;
            margin-top: 20px;
            box-sizing: border-box;
        }

        p {
            margin: 5px 0;
            font-size: 16px;
        }

        #clock {
            font-size: 18px;
            color: #777;
            margin-top: 20px;
        }
    </style>
</head>
<body>
<div class="container">
    <h1>服务器发送事件示例</h1>
    <div id="messages"></div>
    <div id="clock"></div>
</div>

<script>
    // 创建一个新的 EventSource 实例
    const eventSource = new EventSource('/events');

    // 监听消息事件
    eventSource.onmessage = function (event) {
        const newMessage = document.createElement('p');
        newMessage.textContent = 'New message: ' + event.data;
        document.getElementById('messages').appendChild(newMessage);

        // 滚动到底部
        const messagesDiv = document.getElementById('messages');
        messagesDiv.scrollTop = messagesDiv.scrollHeight;
    };

    // 监听错误事件
    eventSource.onerror = function (error) {
        console.error('EventSource failed:', error);
        eventSource.close();
    };
</script>
</body>
</html>
  • 创建EventSource对象:const eventSource = new EventSource('/events'); 创建一个EventSource对象,连接到服务器的 /events 路由。
  • 处理消息:eventSource.onmessage 事件处理器用于处理从服务器接收到的消息,并将其显示在页面上。
  • 处理错误:eventSource.onerror 事件处理器用于处理连接错误,并在发生错误时关闭连接。
  • 自动滚动:每次接收到新消息时,自动滚动到消息列表的底部,确保用户始终能看到最新的消息。

4. 运行项目

4.1 启动服务:

go run main.go

打开浏览器,访问 http://localhost:8080,你应该能看到每两秒钟从服务器推送的一条新消息,且页面内容全屏自适应,样式更加美观。

5 注意事项

5.1 客户端连接限制

  • 浏览器对每个域名下的SSE连接数有限制。大多数现代浏览器允许每个域名最多6个并发连接。如果超过这个限制,新的连接将会被阻塞,直到有连接关闭。
  • 如果你的应用需要支持更多的并发连接,可以考虑使用子域名或负载均衡来分散连接。

5.2 服务器资源管理

  • 每个SSE连接都会占用服务器的一个goroutine,因此需要合理管理服务器资源。如果预期会有大量并发连接,建议使用连接池或其他资源管理机制。
  • 可以通过设置超时、心跳检测等方式来管理长时间未活动的连接,避免资源浪费。

5.3 错误处理和重连

  • 客户端可以通过 onerror 事件处理器来处理连接错误,并实现自动重连逻辑。
  • 服务器端可以在连接关闭时发送适当的错误信息,帮助客户端更好地处理异常情况。

5.4 安全性

  • 确保SSE接口的安全性,避免暴露敏感数据。可以使用HTTPS来加密传输数据。
  • 对于需要认证的场景,可以在SSE请求中携带认证信息,例如使用HTTP头部或Cookie。

总结

本文详细介绍了如何在Go语言中实现SSE(Server-Sent Events)服务端,并在前端使用JavaScript进行集成,实现一个完整的实时数据推送系统。SSE作为一种轻量级的实时通信技术,非常适合那些只需要从服务器向客户端发送数据的应用场景。希望本文能对你有所帮助!


http://www.kler.cn/a/401966.html

相关文章:

  • 保存数据到Oracle时报错ORA-17004: 列类型无效: 1111
  • vscode remote-ssh直连docker容器
  • 2024年低压电工证考试题库及低压电工试题解析
  • 环形缓冲区 之 STM32 串口接收的实现
  • C++AVL平衡树
  • Inpaint-Web:纯浏览器端实现的开源图像处理工具
  • 聚类分析 | MSADBO优化Spectral谱聚类优化算法
  • 群控系统服务端开发模式-应用开发-前端级别功能开发
  • 小红书内容推荐算法开发:利用API打造个性化用户体验
  • Android全局异常捕获
  • Android中常见内存泄漏的场景和解决方案
  • 代码辅助工具 GPT / Cursor
  • AWS EC2 ubuntu 使用密码登陆
  • 24.11.14 朴素贝叶斯分类 决策树-分类
  • 【算法】动态规划中01背包问题解析
  • uniapp 自定义加载组件,全屏加载,局部加载 (微信小程序)
  • git 基础之 merge 和 rebase 的比较
  • 运维面试题.云计算面试题之三ELK
  • VGG16-Pytorch实现人脸识别
  • C/C++实现tcp客户端和服务端的实现(从零开始写自己的高性能服务器)
  • AI 驱动低代码平台:开创智能化用户体验新纪元
  • vue功能基础元素使用
  • Java中日志采集框架-JUL、Slf4j、Log4j、Logstash
  • 在 macOS 和 Linux 中,波浪号 `~`的区别
  • 使用C++编写一个程序,模拟掷骰子的过程,输出1到6之间的随机数。
  • 【企业级分布式系统】ELK优化