Go语言实现SSE中转demo
文章概要:本文主要通过一个demo来介绍如何使用Go语言实现SSE中转。
本文内容来自:谷流仓AI - ai.guliucang.com
前提
- 已安装Go语言环境(参考这篇文章:Macbook安装Go以及镜像设置)
创建项目
- 创建项目目录
bash
mkdir go-app && cd go-app
- 初始化项目
bash
# 后面的模块名自己定义
go mod init example/user/go-app
创建文件
先看一下完成之后的目录结构:
go-app/
├─ go.mod
├─ http/
│ ├─ requests.go
│ └─ responses.go
└─ main.go
然后每个文件的代码如下:
- main.go
go
package main
import (
"example/user/go-app/http"
"github.com/gin-gonic/gin"
"log"
)
// 主函数:初始化并启动 Gin 框架的 HTTP 服务器,支持 /event-stream 的 POST 和 GET 请求。
func main() {
// 创建一个用于传递事件的通道
ch := make(chan string)
// 初始化并配置默认的 Gin路由器
router := gin.Default()
// 设置 POST /event-stream 的处理函数,用于处理 POST 请求
router.POST("/event-stream", func(c *gin.Context) {
http.HandleEventStreamPost(c, ch) // 处理 POST 请求的逻辑
})
// 设置 GET /event-stream 的处理函数,用于处理 GET 请求
router.GET("/event-stream", func(c *gin.Context) {
http.HandleEventStreamGet(c, ch) // 处理 GET 请求的逻辑
})
// 启动 HTTP 服务器并监听端口 9990,记录启动失败的错误日志
log.Fatalf("error running HTTP server: %s\n", router.Run(":9990"))
}
- 创建http目录,并创建文件
- requests.go
go
package http
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"io"
)
// EventStreamRequest 结构体定义了事件流请求的数据模型
type EventStreamRequest struct {
Message string `form:"message" json:"message" binding:"required,max=100"` // 请求中必须提供的消息内容,最大长度为100
}
// HandleEventStreamPost 处理POST方法的事件流请求
// c: Gin框架的上下文对象,用于处理HTTP请求和响应
// ch: 用于事件流通信的通道,将请求消息发送到此通道
func HandleEventStreamPost(c *gin.Context, ch chan string) {
var request EventStreamRequest
// 尝试绑定请求数据到EventStreamRequest结构体
if err := c.ShouldBind(&request); err != nil {
// 如果绑定失败,生成错误响应并返回
errorMessage := fmt.Sprintf("request validation error: %s", err.Error())
BadRequestResponse(c, errors.New(errorMessage))
return
}
// 将请求消息发送到通道
ch <- request.Message
// 创建成功响应并返回
CreatedResponse(c, &request.Message)
return
}
// HandleEventStreamGet 处理获取事件流的请求。
// c: Gin框架的上下文对象,用于处理HTTP请求和响应。
// ch: 一个字符串类型的通道,用于向客户端发送事件消息。
func HandleEventStreamGet(c *gin.Context, ch chan string) {
// 使用Stream方法来建立一个服务器端事件流,不断检查通道中是否有新消息。
c.Stream(func(w io.Writer) bool {
// 如果通道中有消息,通过SSEvent方法以"message"事件类型发送到客户端。
if msg, ok := <-ch; ok {
c.SSEvent("message", msg)
return true // 表示继续发送下一个事件
}
return false // 表示没有更多事件,结束流
})
return
}
- reponses.go
go
package http
import (
"github.com/gin-gonic/gin"
"net/http"
)
// JSendFailResponse 定义了一个失败响应的结构体,包含状态和数据字段
type JSendFailResponse[T any] struct {
Status string `json:"status"` // 响应状态
Data T `json:"data"` // 响应数据,这里泛型T可以是任意类型
}
// JSendSuccessResponse 定义了一个成功响应的结构体,包含状态和可选的数据字段
type JSendSuccessResponse[T any] struct {
Status string `json:"status"` // 响应状态
Data T `json:"data,omitempty"` // 响应数据,成功时可选
}
// BadRequestResponse 用于处理Bad Request错误,返回400状态码和错误信息
func BadRequestResponse(c *gin.Context, error error) {
c.JSON(
http.StatusBadRequest,
JSendFailResponse[string]{
Status: "fail", // 设置响应状态为失败
Data: error.Error(), // 将错误信息填入数据字段
},
)
return
}
// CreatedResponse 用于处理创建资源的成功响应,返回201状态码和创建的资源信息
func CreatedResponse[T interface{}](c *gin.Context, i *T) {
c.JSON(
http.StatusCreated,
JSendSuccessResponse[T]{
Status: "success", // 设置响应状态为成功
Data: *i, // 填入创建的资源信息
},
)
return
}
运行程序
- 启动项目
bash
go run main.go
- 开一个终端,运行以下命令, 监听数据:
bash
curl http://localhost:9990/event-stream
- 打开另一个终端,运行以下命令,发送数据:
bash
curl -d '{"message":"Hello, Event Stream!"}' -H "Content-Type: application/json" -X POST http://localhost:9990/event-stream
- 观察第一个终端,可以看到数据已经发送过来了:
bash
event:message
data:Hello, Event Stream!