gin实现event stream

event stream是属于http的一种通信方式,可以实现服务器主动推送。原理于客户端请求服务器之后一直保持链接,服务端持续返回结果给客户端。相比较于websocket有如下区别:

  1. 基于http的通信方式,在各类框架的加持下不需要开发人员自己维护链接状态,而websocket一般需要开发自己维护客户端链接(一般是一个map)
  2. 也是因为基于http,客户端请求之后便处于接收状态(发送信道关闭?),所以只能接收服务端推送,而不能客户端推送,比较适合用作通知等场景。

以gin框架为例实现:

func TestEventStream(c *gin.Context) {
	// 声明数据格式为event stream
	c.Writer.Header().Set("Content-Type", "text/event-stream")
	c.Writer.Header().Set("Cache-Control", "no-cache")
	c.Writer.Header().Set("Connection", "keep-alive")
	// 禁用nginx缓存,防止nginx会缓存数据导致数据流是一段一段的
	c.Writer.Header().Set("X-Accel-Buffering", "no")

	w := c.Writer
	flusher, _ := w.(http.Flusher)
	flusher.Flush()
	// 数据chan
	msgChan := make(chan string)
	// 错误chan
	errChan := make(chan error, 1)

	// 开启另一个协程处理业务,通过msgChan和errChan传递信息和错误
	go handle(msgChan, errChan)

	// 读取消息
	for {
		msg, ok := <-msgChan
		if !ok {
			break
		}
		fmt.Fprintf(w, "event: message\n")
		fmt.Fprintf(w, "data: %s\n\n", msg)
		flusher.Flush()
	}

	// 检查错误
	for {
		err, ok := <-errChan
		if !ok {
			return
		}
		fmt.Println(err)
		fmt.Fprintf(w, "event: error\n")
		fmt.Fprintf(w, "data: %s\n\n", err.Error())
		flusher.Flush()
	}
}

//逻辑处理,读取文件中每一行的内容返回给eventstream
func handle(msgChan chan string, errChan chan error) {
	defer func() {
		if r := recover(); r != nil {
			errChan <- errors.New("system panic")
		}
		close(msgChan)
		close(errChan)
	}()
	file, err := os.Open("temp.txt")
	if err != nil {
		errChan <- err
		return
	}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		msgChan <- scanner.Text()
	}
}
以下是一个使用 Gin 和 WebSocket 实现简单聊天功能的示例代码: ```go package main import ( "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" "net/http" ) var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } type Message struct { Username string `json:"username"` Content string `json:"content"` } var clients = make(map[*websocket.Conn]bool) var broadcast = make(chan Message) func main() { r := gin.Default() // 打开 WebSocket 连接 r.GET("/ws", func(c *gin.Context) { conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { fmt.Println("Failed to open WebSocket connection:", err) return } clients[conn] = true // 循环接收客户端发送的消息 for { var msg Message err := conn.ReadJSON(&msg) if err != nil { fmt.Println("Failed to read message from client:", err) delete(clients, conn) conn.Close() break } // 处理客户端发送的消息 fmt.Printf("Received message from %s: %s\n", msg.Username, msg.Content) // 广播消息给所有客户端 broadcast <- msg } }) // 广播消息给所有客户端 go func() { for { msg := <-broadcast for conn := range clients { err := conn.WriteJSON(msg) if err != nil { fmt.Println("Failed to send message to client:", err) delete(clients, conn) conn.Close() } } } }() // 启动服务 err := http.ListenAndServe(":8080", r) if err != nil { fmt.Println("Failed to start server:", err) } } ``` 这个示例中,我们定义了一个 `Message` 结构体,包含用户名和内容两个字段。客户端通过 WebSocket 连接发送消息时,服务端会将消息解析为 `Message` 结构体,并将其广播给所有客户端。同时,服务端也会监听客户端连接的断开,并在客户端断开连接时从 `clients` 中删除该客户端的连接信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值