一、背景
作为公司的基础服务,多个业务方连接我们的通道服务,平板应用在进入课堂的时候,和通道服务建立长连接,做到消息通达的及时性。
二、目标
- 1、不同的业务进行隔离,尽量做到互不影响。
- 2、不同的课堂尽可能地进行隔离。
- 3、现状是,ws集群依赖MQ进行消息转发,后期尽可能减少对中间件mq的依赖。
三、Mq广播模式(现状方案)
四、Mq扩展(方案一)
我们这里的通道服务有两个节点,平板作为consumer的角色和通道服务建立长连接,进行消息的上行和下行。
因为通道是一个基础服务,必然首要考虑的是多租户的问题。所以不同的业务交由不同的vhost。
为了进一步地隔离不同的课堂中间的数据干扰,我们采用多个队列Queue,将他们隔离开来。(补充说明,因为有些是小课堂,比如课堂人数只有几十人,大的课堂则可能多大几百几千人。随着人数的剧增,会带来mq消息数量的暴增。)
发送消息的代码:
- 变更前
rabbitTemplate.convertAndSend(channelExchange, "", message);
- 变更后,这里的routingKey是根据课堂ID进行hash算法计算得出的值。
rabbitTemplate.convertAndSend(channelExchange, routingKey, message);
其实就是将fanout模式变更为topic模式,具体它们的区别见下:
五、去Mq方案(ws互为客户端)
这里简单梳理下他们的顺序:
1、注册到zk,在程序启动的过程中,把本通道的Ip和port、tags等信息注册到zk。
2、监听zk目录下的其他节点,如果发现有新注册上来的节点,则把当前节点当做客户端去连接它。如果发现有移除的节点,则把上一步与之建立的长连接断开。
3、任意一个通道节点要发送消息给consumer(平板用户),寻找那些已建立的ws客户端们,依次转发消息至其他通道节点。
六、总结
本文开始是讨论如何扩展mq,以及多租户的隔离。但是mq自身也存在qps的上限,所以我们又设计并实现了后者方案。
好处是不依赖于第三方中间件,存储方面只依赖redis数据库,大大地提升了ws集群的并发性能。(redis是必须的,因为它需要保存用户的session信息,包括userId、业务类别、课堂ID、设备ID等。正因为我们保存了这些信息,才可能做登录或退出的动作,以及限制同一个用户在同一个房间–即课堂,不同的设备只允许登录一个。-- 有点像java的可重入锁!!)