推送服务中有几个模块:消息,频道。
频道:几个用户,组成一个频道。(比如XX部门是一个频道,部门通知就发送给这个频道)
消息:指的是需要推送给频道的信息,此处使用队列。
一个经典的数据结构
map[ Group ] messageList 存储当前最新message
map[Group] userList 频道和用户
map[user] messageList 用户和对应接收的消息
频道可以大致分为这几类:
有些频道,时效性很强,,只接收最新消息,比如会议通知。(谁不想接收一大串通知自己去年xx时间开会的消息)
有些频道,需要把所有信息都接收,比如,系统设置通知。
有些频道,接收完了,还要存起来,比如用户操作日志。为此我们还要把他们连上数据库持久化起来。
一个websocket推送服务。
在一个推送服务中,各个用户组处在不同的频道中,我们通过接口来连接传递我们要推送的消息。推送系统验证用户权限,然后进行推送。
接入:
我们通过访问并且返回javascript 文件来接入系统,同时很好的解决了跨域问题。比如,引入:
<script src="xxxx.js"></script>
它会找到网站的通用UserID或者Token,发回推送服务,来确定用户状态。
而对于子系统的每个页面,我们可以在过滤器中加入这个返回脚本,有网关的,在网关系统中加入
存储:
在用户数量和消息数量达到一定的时候,上文数据结构就不能存放在推送服务上面了,我们可以放在redis缓存上面,使用hash结构进行存储。同时map[user] messageList 结构 messageList也分为未读和已读,这里我们只存储未读的 ,而已读的可以存储在关系数据库中。
而对于推送服务需要做到,把每个频道接收的信息都存储到数据库,更细致的,可以把每个用户存储已读未读的信息存储起来。
性能:
很明显,每个页面至少要开一个线程,负责监听与发送消息。我使用go协程池来做的,每当发送或者接收的时候才分配go协程,操作完成立马回收,包括心跳包(60秒一次),每个协程2k,常规服务器十几万个连接没有压力。