使用rabbitmq广播模式来处理集群下的websocket消息推送

websocket属于长连接,当客户端连接上服务端后,将保持于服务端的连接。
而当websocket服务端存在集群的情况,如果需要将某个消息发送到客户端时,通过接口调用发送,这种情况只能将消息发送到与这台服务端连接的客户端,会存在部分客户无法接收消息的情况。

后续通过搜集资料选择采用了用rabbitmq来做websocket的集群,即通过使用mq的广播交换机,然后结合服务端启动创建动态队列来绑定同一个交换机,这样便能使集群中的每一个服务端都能收到消息,然后再去往连在当前服务端的websocket推送消息,不在的则跳过不发。

交换机以及队列设置



/**
 * @author peng
 * @program 
 * @description 交换机以及队列设置
 * @create 2022/03/30 22:08
 **/
@Configuration
public class BroadcastMqConfig {


    private final static String BROADCAST_EXCHANGE = "broadcast_exchange";

    @Value("${server.port}")
    private String serverPort;


    public static String getQueueName() {
        return "broadcast:" + getLocalIP() + ":";
    }

    /**
     * websocket 交换机
     *
     * @return
     */
    @Bean
    public FanoutExchange fanoutDirectExchange() {
        return new FanoutExchange(BROADCAST_EXCHANGE);
    }

    /**
     * websocket 队列
     *
     * @return
     */
    @Bean
    public Queue queueFanoutQueue() {
        return QueueBuilder.durable(BROADCAST_EXCHANGE + ":" + getLocalIP() + ":" + serverPort).build();
    }


    /**
     * websocket 队列交换机绑定
     *
     * @param callQueueFanoutQueue
     * @param broadcastmsgFanoutDirectExchange
     * @return
     */
    @Bean
    public Binding broadcastFanoutBinding(Queue callQueueFanoutQueue, FanoutExchange broadcastmsgFanoutDirectExchange) {
        return BindingBuilder
                .bind(callQueueFanoutQueue)
                .to(broadcastmsgFanoutDirectExchange);
    }


    /**
     * 取当前系统站点本地地址 linux下 和 window下可用
     *
     * @return
     */
    public static String getLocalIP() {
        String ipStr = "";
        InetAddress ip = null;
        try {
            // 如果是Windows操作系统
            if (isWindowsOS()) {
                ip = InetAddress.getLocalHost();
            } else {
                boolean findIp = false;
                Enumeration<NetworkInterface> netInterfaces = NetworkInterface
                        .getNetworkInterfaces();
                while(netInterfaces.hasMoreElements()) {
                    if (findIp) {
                        break;
                    }
                    NetworkInterface ni = netInterfaces.nextElement();
                    // 跳过docker网卡
                    if (ni.getName().contains("docker")) {
                        continue;
                    }
                    // 遍历所有ip
                    Enumeration<InetAddress> ips = ni.getInetAddresses();
                    while(ips.hasMoreElements()) {
                        ip = (InetAddress) ips.nextElement();
                        // 127.开头的都是lookback地址
                        if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress()
                                && !ip.getHostAddress().contains(":")) {
                            findIp = true;
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (null != ip) {
            ipStr = ip.getHostAddress();
        }
        return ipStr;
    }

    /**
     * 判断当前系统是否windows
     *
     * @return boolean
     */
    public static boolean isWindowsOS() {
        boolean isWindowsOS = false;
        String osName = System.getProperty("os.name");
        if (osName.toLowerCase().contains("windows")) {
            isWindowsOS = true;
        }
        return isWindowsOS;
    }    
}

上面主要是通过动态获取当前服务器的ip以及应用启动的端口来作为队列名字,但是绑定交换机是同一个,由于交换机使用的是广播交换机,此模式下会将消息发送到所有队列。
当应用启动后可自动创建以 当前应用的的ip + 端口 为名称的队列

生产者

/**
 * @author peng
 * @program 
 * @description 生产者设置
 * @create 2022/03/30 22:08
 **/
@Component
@Slf4j
public class BroadcastmsgProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 发送推送websocket消息
     *
     * @param param
     */
    public void sendMsg(BroadcastSendDto param) {
        rabbitTemplate.convertAndSend(BroadcastMqConfig.BROADCAST_EXCHANGE,
                "",
                param);
    }
}

以上为生产者代码,因为是广播模式,所以路由key为空也可以广播到所有队列。

消费者

/**
 * @author peng
 * @program 
 * @description 消费者
 * @create 2022/03/30 22:08
 **/
@Component
@Slf4j
public class BroadcastmsgReceiver {

    @Autowired
    private BoadcastWebSocketServer webSocketServer;

    @RabbitListener(queues = {"#{T(com.server.mq.BroadcastMqConfig).getBroadcastmsgQueueName()}${server.port}"})
    public void pushMessageToUser(Channel channel, BroadcastSendDto param, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
        try {
            log.info("接收推送至具体用户websocket mq :{}", param);
            // 发送websocket消息
            webSocketServer.sendMessageToUser(param);
        } catch (Exception e) {
            log.error("获取推送至具体用户websocket 异常 ...", e);
        } finally {
            try {
                channel.basicAck(tag, false);
            } catch (IOException e) {
                log.error("---------消息确认异常---------", e);
            }
        }
    }
}

以上为消费者设置,消费者使用动态队列名称监听,“#T(com.server.mq.BroadcastMqConfig).getBroadcastmsgQueueName()}*” 是表示使用代码块获取返回字符串,“${server.port}”是动态获取配置文件参数值,获取端口。
当应用启动后可监听以 当前应用的的ip + 端口 为名称的队列消息

调用入口

	/**
     * 发送消息至某个用户
     * @param sendDto
     * @return
     * @throws IOException
     */
    @PostMapping("/sendMsg")
    public R sendMsg(@RequestBody @Validated BroadcastSendDto sendDto) {
        log.info("发送消息:{}", JSON.toJSONString(sendDto));
        broadcastmsgProducer.sendMsg(sendDto);
        return R.ok();
    }

以上为通过接口调用的入口,我们此部分由于是微服务的存在,所以对外提供接口调用,便在接口处修改为发送mq消息。
如若处于设计阶段且是单机应用,可考虑直接需发送消息代码处直接发送mq消息。

以上就是我使用的处理集群的方式,这种属于比较简单粗暴的,看到网上有通过记录所有连接与哪一台服务端连接的信息,然后再通过redis等中间来获取精准通知到具体某一台服务端来推送消息。但是由于时间有限,就没有仔细研究了。
在这里插入图片描述

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
WebSocketRabbitMQ 可以结合使用来实现实时消息推送WebSocket 是一种基于 TCP 的协议,它允许在客户端和服务器之间建立持久的双向通信通道。而 RabbitMQ 是一个消息代理和队列管理系统,可以实现消息的可靠传输和分发。 下面是使用 WebSocketRabbitMQ 实现实时消息推送的一般步骤: 1. 配置 WebSocket 服务器:在后端应用程序中,你需要配置一个 WebSocket 服务器,用于接收和处理客户端的 WebSocket 连接请求。可以使用 Spring Boot 中的 Spring WebSocket 或其他 WebSocket 框架进行配置。 2. 配置 RabbitMQ:在后端应用程序中,你需要配置 RabbitMQ 的连接信息,并创建一个或多个交换机和队列。可以使用 RabbitMQ 的 Java 客户端库进行配置。 3. 监听 RabbitMQ 消息:在后端应用程序中,你需要监听 RabbitMQ 中指定队列的消息。当有新的消息到达时,通过 WebSocket 服务器将消息推送给客户端。 4. 前端连接 WebSocket:在前端应用程序中,你需要使用 JavaScript 的 WebSocket API 连接到后端的 WebSocket 服务器。 5. 接收消息并更新 UI:在前端应用程序中,当接收到 WebSocket 服务器推送消息时,你可以在界面上实时展示或处理这些消息。 通过结合使用 WebSocketRabbitMQ,你可以实现实时、双向的消息通信,并将消息推送给多个客户端。这种方式适用于需要实时更新消息的应用场景,如聊天应用、实时监控等。需要根据具体的技术栈和需求进行相应的配置和开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我想出去玩-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值