使用go+gin实现服务器推送(Server Push)

287 阅读2分钟

前言:单向的流式传输,也被称为服务器推送(Server Push),在当前业务中运用是实时消息推送,比如社交网络的实时消息、邮件通知、实时新闻更新等。服务器可以在有新的消息时立即推送到客户端,而不需要客户端定期轮询服务器。

本案例采用了gin的官方示例实现,实际运用上可能还需要一定完善

定义了一个Event结构体,它包含了以下几个字段:

  • Message: 一个字符串类型的通道,用于接收主程序发送的事件消息。
  • NewClients: 一个ClientChan类型的通道,用于接收新连接的客户端通道。
  • ClosedClients: 一个ClientChan类型的通道,用于接收关闭连接的客户端通道。
  • TotalClients: 一个以ClientChan为键,布尔值为值的映射,用于存储所有当前连接的客户端通道。
type Event struct {
	// Events are pushed to this channel by the main events-gathering routine
	Message chan string

	// New client connections
	NewClients chan chan string

	// Closed client connections
	ClosedClients chan chan string

	// Total client connections
	TotalClients map[chan string]bool
}

ClientChan是一个字符串类型的别名,表示客户端通道。

// New event messages are broadcast to all registered client connection channels
type ClientChan chan string

定义了一个NewServer函数,它返回一个Event指针,主要功能是:

  • 使用make函数初始化Event结构体中的各个字段。
  • 启动一个协程,调用Event结构体的listen方法监听各个通道上的消息,并进行相应的处理。
  • 返回Event结构体的指针。
// Initialize event and Start procnteessing requests
func NewServer() (event *Event) {
	//使用make函数初始化Event结构体中的各个字段。
	event = &Event{
		Message:       make(chan string),
		NewClients:    make(chan chan string),
		ClosedClients: make(chan chan string),
		TotalClients:  make(map[chan string]bool),
	}
	//启动一个协程,调用Event结构体的listen方法监听各个通道上的消息,并进行相应的处理。
	go event.listen()

	return
}

定义了一个listen方法,它属于Event结构体。

  • 使用for循环和select语句不断地等待各个通道上的消息。
  • 当收到NewClients通道上的消息时,表示有新客户端连接到服务器。将该客户端通道添加到TotalClients映射中,并打印日志信息。
  • 当收到ClosedClients通道上的消息时,表示有客户端断开连接。将该客户端通道从TotalClients映射中删除,并关闭该通道,并打印日志信息。
  • 当收到Message通道上的消息时,表示有新事件消息需要广播。遍历TotalClients映射中的所有客户端通道,并将事件消息发送给它们。
func (stream *Event) listen() {
	for {
		select {
		// 添加新的客户端
		case client := <-stream.NewClients:
			stream.TotalClients[client] = true
			log.Printf("Client added. %d registered clients", len(stream.TotalClients))

		// 删除已经关闭的客户端
		case client := <-stream.ClosedClients:
			delete(stream.TotalClients, client)
			close(client)
			log.Printf("Removed client. %d registered clients", len(stream.TotalClients))

		// Broadcast message to client
		case eventMsg := <-stream.Message:
			for clientMessageChan := range stream.TotalClients {
				clientMessageChan <- eventMsg
			}
		}
	}
}