event stream是属于http的一种通信方式,可以实现服务器主动推送。原理于客户端请求服务器之后一直保持链接,服务端持续返回结果给客户端。相比较于websocket有如下区别:
- 基于http的通信方式,在各类框架的加持下不需要开发人员自己维护链接状态,而websocket一般需要开发自己维护客户端链接(一般是一个map)
- 也是因为基于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()
}
}