Go语言实现SSE中转demo

Go语言实现SSE中转demo

文章概要:本文主要通过一个demo来介绍如何使用Go语言实现SSE中转。
本文内容来自:谷流仓AI - ai.guliucang.com

前提

创建项目

  1. 创建项目目录
bash 复制代码
mkdir go-app && cd go-app
  1. 初始化项目
bash 复制代码
# 后面的模块名自己定义
go mod init example/user/go-app

创建文件

先看一下完成之后的目录结构:

复制代码
go-app/
├─ go.mod
├─ http/
│  ├─ requests.go
│  └─ responses.go
└─ main.go

然后每个文件的代码如下:

  1. 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"))
}
  1. 创建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
}

运行程序

  1. 启动项目
bash 复制代码
go run main.go
  1. 开一个终端,运行以下命令, 监听数据:
bash 复制代码
curl http://localhost:9990/event-stream
  1. 打开另一个终端,运行以下命令,发送数据:
bash 复制代码
curl -d '{"message":"Hello, Event Stream!"}' -H "Content-Type: application/json" -X POST http://localhost:9990/event-stream
  1. 观察第一个终端,可以看到数据已经发送过来了:
bash 复制代码
event:message
data:Hello, Event Stream!
相关推荐
梁梁梁梁较瘦1 天前
边界检查消除(BCE,Bound Check Elimination)
go
梁梁梁梁较瘦1 天前
指针
go
梁梁梁梁较瘦1 天前
内存申请
go
半枫荷1 天前
七、Go语法基础(数组和切片)
go
梁梁梁梁较瘦2 天前
Go工具链
go
半枫荷2 天前
六、Go语法基础(条件控制和循环控制)
go
半枫荷3 天前
五、Go语法基础(输入和输出)
go
小王在努力看博客3 天前
CMS配合闲时同步队列,这……
go
Anthony_49264 天前
逻辑清晰地梳理Golang Context
后端·go
Dobby_055 天前
【Go】C++ 转 Go 第(二)天:变量、常量、函数与init函数
vscode·golang·go