作者:周慧婷
写在前面
系统开发的过程中,我们经常需要实现消息推送的需求。单端单实例的情况下很好处理(网上有许多教程这里不做展开),但在分布式系统及多端需要推送的情况下该如何处理呢?
在分布式系统中,消息推送服务端是多实例的。某系统中一个服务生成一条消息,这条消息需要实时推送到多个终端,此时该如何进行有效的 WebSocket 推送呢?首先一起看看如下场景:
假设推送消息由消息实例 2 产生,但是终端真正连接的消息实例是实例 1 和实例 3,并没有连接到生产消息的实例 2,系统是如何将实例 2 的消息同步推送到终端 1 和终端 2 的呢?下文将详细描述。
基本原理
为了满足需求我们采用 redis 做协同中间件,用于存储用户信息、生成用户连接的唯一性标识以及 pod address,消息的生产者实例通过订阅 redis 获取终端连接的唯一性标识和 pod address,并通知到对应的消息实例,最终由相应连接终端的消息实例通过 WebSocket 将消息发推送到用户终端。具体流程如下图:
服务端实现
Client
Client 组件的作用,是当用户与消息服务中某个实例建立连接后,管理这个连接的信息,这里通过一个 Golang 结构体来定义:
type Client struct {
UUID string
UserID string
Socket *websocket.Conn
Send chan []byte
}
结构体中的数据类型说明如下:
- UUID:对连接进行唯一性的标识,通过此标识可以查找到连接信息。
- UserID:用户 ID。
- Socket:连接对象。
- Send:消息数据 channel。
我们为 Client 结构体实现了两个方法:Read、Write 来处理消息的接受和发送。
Read 方法
Read 方法比较简单,从终端接收请求消息后,消息实例通过 WebSocket 回应接收消息状态,并不返回请求结果。结果通过 Write 方法返回。
func (c *Client) Read(close, renewal chan *Client) {
defer func() {
close <- c
}()
for {
_, message, err := c.Socket.ReadMessage(