Spring Boot系列21 Spring Websocket实现websocket集群方案讨论

概述

本文对websocket集群的方案进行讨论:前端

  1. 在websocket集群中,后端准确将指定的消息推送到指定的用户,前端实时接收服务推送的消息
  2. 对websocket集群的方案进行讨论,并肯定最佳方案

webscoket集群方案

集群方案分析

这里写图片描述

在上个博文Spring Boot系列20 Spring Websocket实现向指定的用户发送消息中实现向指定用户发送消息的功能,可是咱们将提供websocket服务的服务进行集群(如上图)则存在以下问题:web

上图中,用户A经过websocket注册到服务A,服务A经过STOMP协议订阅RabbitMQ上的消息,同理用户B。若是用户A链接到服务A上,那么在位于服务B上的MQ模块即便使用SimpMessagingTemplate实例向用户A发送消息,此消息也没法到达用户A,缘由是由于服务B上没有服务A的注册信息,没法准确的推送消息.只有在服务A上的MQ模块使用SimpMessagingTemplate实例向这个用户发送消息,消息才会到达用户Aredis

针对这个问题下文咱们经过3个方案解决这个问题,并详细分析每一个方案的有缺点。后端

webSocket集群方案一

概述 无论消息的接收者链接在哪一个服务上,每一个服务A/B都接收消息,对相同的消息都使用SimpMessagingTemplate实例进行推送,保证总有一个消息会被用户收到。 浏览器

这里写图片描述

详细流程以下缓存

  1. 用户A/B分别经过ws链接服务A/B, 而后服务A/B经过stomp协议接入RabbitMQ
  2. 消息发送者将消息发送到RabbitMQ的交换机上,使用扇形交换机。这样保证同一个消息能够同时被服务A/B接收
  3. 两个服务上的MQ模块接收对应消息后,无论对应的用户是不是经过本身链接到RabbitMQ,直接使用SimpMessagingTemplate实例向消息中指定的用户推送消息
  4. 用户A/B接收到对应的消息

优势服务器

  1. 实现比较简单

不足websocket

  1. 消息生产者发送消息的RabbitMQ交换机必须是广播功能,如扇形交换机
  2. 为了保证消息顺利到达用户,相同的消息必须在两个服务A/B上执行相同的操做。这样若是服务越多,则重复的发送消息越多
  3. 若是用户不在线,不管发送多少消息用户都不能收到

webSocket集群方案二

概述 使用redis缓存用户的websocket链接信息,记录用户登陆到哪一个服务上,当有消息过来时,将消息推送到用户登陆的服务,而后服务都使用SimpMessagingTemplate实例进行推送session

这里写图片描述

在方案一的基础上增长以下功能:架构

  1. 服务A/B上增长MQ模块,服务A/B上MQ模块会链接到RabbitMQ,分别订阅队列A/B
  2. 服务A/B增长WS模块,当websocket链接过来时,将此用户的链接信息存储到redis上,系统记住每一个用户登陆的到哪一个服务
  3. 消息生产者将消息推送到交换机,不直接推送到服务A/B
  4. 增长新的模块dispatch,此模块接收到消息,而后从redis中读取要消息要推送到用户链接到那个服务器上,而后将消息发送到用户链接服务对应的队列中。若是消息要发送给用户B,则dispatch模块会将消息发送到队列B
  5. 服务A/B的MQ模块接收到消息后,使用SimpMessagingTemplate实例向指定用户推送消息

优势

  1. 此方案克服上一个方案不足的地方

缺点

  1. 实现复杂
  2. 发送MQ消息的次数增长1倍

webSocket集群方案三

概述 不使用SimpMessagingTemplate,使用RabbitMQ的客户端API直接向用户在RabbitMQ上订阅的队列发送消息

发现用户经过浏览器登陆websocket并注册RabbitMQ时,此时这个链接会在RabbitMQ创建一个队列,队列的名称相似stomp-subscription-***,此队列绑定到默认交换机amq.topic,路由键为"web订阅队列名称+'-user'+websocket sessionId"(这里是demo-userpjplggbl,demo是stomp weboscket链接的队列名称,pjplggbl登陆websocket登陆时的websocket sessionId值),图片以下:

这里写图片描述

根据这个,设计以下架构:

这里写图片描述

在方案一的基础进行以下修改,新的架构图流程以下:

  1. 服务A增长WS模块,当websocket链接过来时,将此用户的链接信息(主要是websocket sesionId值)存储redis中
  2. 消息生产者发送消息到的交换机,这些服务不直接推送服务A/B
  3. 增长新的模块dispatch,此模块接收推送过来的信息,并从redis中读取消息接收用户对应的websocket sesionId值,而后根据上面的规则计算出用户对应的路由键,而后将消息发送到用户订阅的队列上
  4. 前端接收消息

优势:

  1. 即克服第一个方案不足的地方,又比第二个方案简单

结论

方案三是最好的方案,下一篇文章,咱们会介绍如何在代码中实现方案三

相关文章
相关标签/搜索