这里是通过stomp的本地消息模式搭建集群,不依赖外部MQ,通过前端连接时在gateway层进行路由,并且后端服务向websocket发送消息到前端时,通过feign调用时也走同样的ribbon逻辑路由到对应的websocket服务,feign调用和gateway创建websocket连接时的路由规则相同即可。这里通过租户id一致性哈希来选择服务实例。
image.png image.png前端创建连接时 指向gateway,在路径上带上租户id,
image.png
在WebFilter 中拿到uri中的租户id放入threadlocal中,
image.png
ribbon的路由规则和配置提到公用jar中,以便所有后端服务的feign调用时共用,
image.png
feign的配置类,自定义ribbon规则指定对应的被访问的服务,这里是共用的,而我这里gateway单独的配置类如下
image.png
一样的,这是由于项目依赖问题。能共用的尽量共用,根据自己项目调整。
image.png通过指定的配置类实例化路由规则bean
image.png
image.png
image.png
image.png
所有的路由逻辑,大概就是取租户id,如果取不到则返回第一个服务实例,
第一次路由时,会缓存租户id和被选实例的映射,如果后续哈希值和缓存的映射关系相同,继续选择相同的服务实例,如果不同,说明有服务重启了服务实例顺序改变,需要负载再均衡,重新选择实例并缓存。如果增加服务实例或挂断则也需要负载再均衡,重新选择实例(实际取决于一致性哈希算出的index)。
image.png
feign调用提供一个接口
image.png
feign负载方式的实现类,通过spring 事件方式调用feign这样可以提供一个重试机制
image.png
image.png
调用feign
image.png image.png
重试机制。
这里调用feign需要考虑的是,前端请求触发和消息消费者触发context不同。
原来的实现是通过前端用户随意连接到后端的websocket某个服务实例,而后端通过kafka不同的groupId(启动时生成随机的)来确保每个websocket服务实例冗余后端发送的消息,这样每个websocket服务都会收到所有消息,不管当前实例连接的session需不需要。
因为stomp协议的后端实现 每次发送给前端消息时,通过遍历一个Map
Map<订阅路径 ,List< SessionInfo List<用户的订阅ids >>> 因为我们目前的业务和前端不通过 订阅id来转发消息,都是通过不同的订阅路径来区分,所以是需要 O(m * n ) m为订阅路径数量,n为连接上的session数量,如果做feign来路由指定服务的话,m的数量会除以 集群服务数量,假设x个服务
就是 m/x * n 至少减少x倍的 循环压力。并且订阅路径的size是有限制的,
image.png
默认是1024 上图可以设置。这决定了 Map的容量,如果超过会进行清理
image.png通过 LinkedMultiValueMap 维护了一个 LRU缓存来清理
image.png
发送websocket消息到前端的主要逻辑,
通过订阅路径遍历获取要发送的 session信息时是加锁的。。。所以减轻遍历压力刻不容缓!!
这里既然说到了另一个 kafka的实现方式,我们是可以通过配置,启动时来加载对应的实现
image.png通过接口调用,启动时加载配置里选择的实现类
image.png
路由也需要通过配置选择。
然后再让你的应用各个服务启动时能加载到的配置信息配置好就可以了,而前端连接时带上查询参数不会影响噢。
网友评论