RabbitMQ 实现简易即时通讯

news2024/11/15 11:28:00

设计思路

利用消息队列的特性进行消息投递,假设客户端 A 要与客户端 B 进行通信。
客户端 A :创建队列 A-B ,发送的消息推送到 A-B 队列, 绑定 B-A 队列,接收 B-A 队列推送给客户端的消息。
客户端 B :创建队列 B-A ,发送的消息推送到 B-A 队列, 绑定 A-B 队列,接收 A-B 队列推送给客户端的消息。

如果其中某个客户端主动退出或者异常中断时,销毁队列 A-B 以及队列 B-A(是否销毁视实际应用场景来定)。

实现

创建RabbitMQ 工具类,用以获取连接及销毁队列。

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ClientUtils {

    private static ConnectionFactory DEFAULT_FACTORY_INSTANCE = null;

    static {
        DEFAULT_FACTORY_INSTANCE = new ConnectionFactory();
        DEFAULT_FACTORY_INSTANCE.setHost("127.0.0.1");
        DEFAULT_FACTORY_INSTANCE.setUsername("user");
        DEFAULT_FACTORY_INSTANCE.setPassword("123456");
        DEFAULT_FACTORY_INSTANCE.setVirtualHost("testVirtualHost");
    }

    public static Connection getConnection() throws IOException, TimeoutException {
        return DEFAULT_FACTORY_INSTANCE.newConnection();
    }

    public static void queueDelete(Channel channel, String queue) throws IOException {
        channel.queueDelete(queue);
    }

    public static void queueDelete(Channel channel, String... queues) throws IOException {
        for (int i = 0; i < queues.length; i++) {
            channel.queueDelete(queues[i]);
        }
    }

}

创建聊天客户端核心类,用于处理消息发送逻辑以及消息接收逻辑。


import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DeliverCallback;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

/**
 * 需保证 send 与 reciver 的值是唯一的
 */

public class ChatClient {

    private String send;

    private String reciver;

    public ChatClient(String send, String reciver) {
        this.send = send;
        this.reciver = reciver;
    }

    public void start() {

        try (
                Channel channel = ClientUtils.getConnection().createChannel();
                Scanner scanner = new Scanner(System.in);
        ) {
            String pub = send + "-" + reciver;
            String rec = reciver + "-" + send;
            channel.queueDeclare(pub, false, false, false, null);
            channel.queueDeclare(rec, false, false, false, null);

            System.out.println(String.format("%s连接到%s", send, reciver));

            String message = null;
            while (true) {
                //接收到信息回调接口,目的是当接收到一条信息时,进行一些操作,比如可以在控制台里打印出来
                DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                	//todo 这里可以做一个消息类型的识别,使用策略模式不同类型使用不同的解析器
                    String receiveMessage = new String(delivery.getBody(), "UTF-8");
                    System.out.println(String.format("收到%s消息:%s", rec, receiveMessage));
                };

                //取消接收的回调接口,目的是如在接收消息的时候队列被删除掉了,可以进行一些操作。
                CancelCallback cancelCallback = (consumerTag) -> {
                    System.out.println("消息中断");
                    ClientUtils.queueDelete(channel, pub, rec);
                    //退出
                    System.exit(0);
                };
                //管道接收消息
                channel.basicConsume(rec, true, deliverCallback, cancelCallback);

                if ((message = scanner.nextLine()) != null && StringUtils.hasText(message)) {
                    if ("exit".equals(message)) {
                        System.out.println("终止发送消息");
                        ClientUtils.queueDelete(channel, pub, rec);
                        System.exit(0);
                        break;
                    }
                    // todo 对发送的消息进行转换,以支持更多的类型
                    channel.basicPublish("", pub, null, message.getBytes());
                    System.out.println(String.format("%s发送消息:%s", pub, message));
                    message = null;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

实现客户端 A

public class Client1 {

    public static void main(String[] args) {
        ChatClient client = new ChatClient("A", "B");
        client.start();
    }
}

实现客户端 B

public class Client2 {

    public static void main(String[] args) {
        ChatClient client = new ChatClient("B", "A");
        client.start();
    }
}

测试

客户端 A 上线,发送消息,此时客户端 B 未上线。

在这里插入图片描述

由于此时客户端 B 还未上线,消息会积压在RabbitMQ队列中,等待客户端 B 上线消费。

在这里插入图片描述

客户端 B 上线,消费积压在MQ的消息。

在这里插入图片描述

此时客户端 A 与客户端 B 同时在线,可以进行即时通信。

小结

这里只是讲述了文本消息的传递,输入内容也是使用控制台输入。实际上可以拓展到其它类型,比如图片,音频等,只需要统一消息体,在消息发送及接收时进行消息的封装和解析。

比如,发送图片消息时可以标识此条消息为图片类型,然后将图片转换为二进制文本,封装成一个完整的消息投递。客户端接收到的消息根据消息投递的类型使用不同的解析器去进行解析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1927561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ROS2入门到精通—— 2-2 ROS2实战:运动规划仿真环境Nav2搭建和导航测试

1 Nav2总体架构 头文件源文件约八万行 安装cloc&#xff1a;sudo apt install cloc bigdavidbigdavid-Legion-Y9000P-IRX8:~/Nav2$ cloc .1137 text files.1133 unique files. 57 files ignored.github.com/AlDanial/cloc v 1.90 …

72.WEB渗透测试-信息收集- WAF、框架组件识别(12)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;71.WEB渗透测试-信息收集- WAF、框架组件识别&#xff08;11&#xff09;-CSDN博客 常用…

视频剪辑软件如何选?FCPX和PR更适合新手呢

随着抖音、快手等短视频平台的迅速兴起&#xff0c;短视频数量急剧增加。想要发布一款简单、高质量的短视频&#xff0c;运用剪辑软件至关重要。目前比较流行的有Adobe家的Premiere&#xff0c;以及Final Cut Pro X&#xff0c;经常有用户在二者间&#xff0c;不知如何选择&…

【自媒体直播】手机充当电脑摄像头的方法

你知道如何使用手机作为电脑摄像头进行直播吗&#xff1f;当我们短时间内需要通过线上直播的方式进行录课、召开线上会议等紧急情况&#xff0c;但又不想花费昂贵的费用购买高清电脑摄像设备时&#xff0c;我们可以通过使用第三方专业虚拟摄像头软件通过手机充当电脑摄像头的方…

LinK3D: Linear Keypoints Representation for 3D LiDAR Point Cloud【SLAM-翻译与解读】

LinK3D: Linear Keypoints Representation for 3D LiDAR Point Cloud 摘要 特征提取和匹配是许多机器人视觉任务的基本组成部分&#xff0c;如 2D 或 3D 目标检测、识别和配准。2D 特征提取和匹配已取得巨大成功。然而&#xff0c;在 3D 领域&#xff0c;当前方法由于描述性差…

亲测有效!2024年11月软考详细备考计划分享

一、基础奠基阶段&#xff08;1-2个月&#xff09; 目标设定&#xff1a;全面理解考试要求&#xff0c;奠定坚实的知识基础。 研究考纲&#xff1a;深入研读考试大纲&#xff0c;精确把握考试范围与核心考点。 个性化规划&#xff1a;根据个人学习进度与理解能力&#xff0c…

Dockerfile介绍及创建流程

什么是Dockerfile Dockerfile 是一个文本文件&#xff0c;其内包含了一条条的指令(Instruction)&#xff0c;用于构建镜像。每一条指令构建一层镜像&#xff0c;因此每一条指令的内容&#xff0c;就是描述该层镜像应当如何构建。 Dockerfile 用于指示 docker image build 命令…

2024最新AI创作系统,ChatGPT商业运营系统,AI绘画系统源码,AI视频生成系统,AI智能体、文档分析、多模态模型一站式AIGC解决方案

一、人工智能 人工智能技术正在迅速发展&#xff0c;AI语言模型、AI绘画、AI视频在多个领域都有广泛的应用。它们不仅在科技创新方面表现出色&#xff0c;还在艺术创作、内容生产和商业应用中展现出巨大的潜力。 AI语言模型可以用于自动化内容生成、智能客服、文本翻译等方面…

科技云报道:产业为根大模型应用为擎,容联云推动企业营销服场景重塑

科技云报道原创。 “没有应用&#xff0c;光有一个基础模型&#xff0c;不管是开源还是闭源&#xff0c;一文不值。”在2024世界人工智能大会&#xff08;WAIC 2024&#xff09;现场&#xff0c;百度创始人、董事长兼首席执行官李彦宏直言。 国产大模型的种类越发丰富&#x…

浅谈大模型工程化中的安全防护

之前在社区看很多人谈论大模型&#xff0c;感觉大模型无所不能。但是近期我也是陆续做了两个大模型应用项目&#xff0c;深刻感受到目前阶段大模型还是存在很多问题&#xff0c;深刻体验到了大模型在迈向工程化的过程中任重而道远。 今天把发现的一些主要问题粗略梳理&#xff…

CAN总线学习

can主要用于汽车、航空等控制行业&#xff0c;是一种串行异步通信方式&#xff0c;因为其相较于其他通信方式抗干扰能力更强&#xff0c;更加稳定。原因在于CAN不像其他通信方式那样&#xff0c;以高电平代表1&#xff0c;以低电平代表0&#xff0c;而是通过电压差来表示逻辑10…

树莓派关机

文件 shutdown.sh #!/usr/bin/bash sudo shutdown -r nowpython 文件开头添加 #!/usr/bin/python3

在VSCode上创建Vue项目详细教程

1.前期环境准备 搭建Vue项目使用的是Vue-cli 脚手架。前期环境需要准备Node.js环境&#xff0c;就像Java开发要依赖JDK环境一样。 1.1 Node.js环境配置 1&#xff09;具体安装步骤操作即可&#xff1a; npm 安装教程_如何安装npm-CSDN博客文章浏览阅读836次。本文主要在Win…

企业全历史行为数据 让你对竞争对手的一切清清楚楚

关于商业竞争&#xff0c;在刚进入信息时代的那些年&#xff0c;人们说“现代商战就是信息战”&#xff0c;强调用非对称的或者更快获得的信息来赢得竞争&#xff1b;近些年进入大数据时代&#xff0c;人们又说“得数据者得天下”&#xff0c;发现“数据算法”有很多妙用&#…

890亿,瞄准集成电路和人工智能等领域!

近日&#xff0c;上海市政府近期宣布拟设立总规模达890亿元的三只产业先导母基金&#xff0c;专注于集成电路、生物医药和人工智能三大先导产业的发展。这一举措旨在推动这些领域的创新和产业升级&#xff0c;同时也响应了国家战略新兴产业的发展需求&#xff0c;为支持上海市集…

pytest测试框架+allure超详细教程

1、测试识别和运行 文件识别&#xff1a; 在给定的目录中&#xff0c;搜索所有test_.py或者_test.py文件 用例识别&#xff1a; Test*类包含的所有test_*的方法&#xff08;测试类不能有__init__方法&#xff09;不在类中的所有test_*方法pytest也能执行unit test写的用例和…

一个真正有本事的人,往往有这4个特征,遇见一定要深交!

点击上方△腾阳 关注 转载请联系授权 你好&#xff0c;我是腾阳。 根据社会学和心理学的研究&#xff0c;一个人在其一生中可能会有更深层次交往的人数通常是有限的。 英国人类学家罗宾邓巴提出了著名的“邓巴数字”&#xff0c;即一个人能够维持稳定社交关系的人数大约在15…

使用嵌入式知识打造智能手环:nRF52蓝牙开发实战(C++/BLE/传感器)

项目概述 现代人越来越注重健康管理&#xff0c;智能穿戴设备应运而生。本项目旨在利用低功耗蓝牙芯片nRF52832&#xff0c;结合加速度计、心率传感器、陀螺仪等传感器&#xff0c;开发一款功能完善、性能稳定的智能运动手环。该手环能够实时采集用户的运动数据和生理指标&…

c++ map operator[] 陷阱

背景&#xff1a;以前做的一个功能&#xff0c;在程序中保存了一个map&#xff0c;这个map的类型是这样的&#xff1a; std::map<int, std::set<自定义类型>>&#xff0c;一般情况下这个 set 只有一个元素&#xff0c;特殊情况是允许set有两个元素。现在是项目测试…

线程间通信与变量修改感知:几种常用方法

线程间通信与变量修改感知&#xff1a;几种常用方法 1. 使用volatile关键字2. 使用synchronized关键字3. 使用wait/notify/notifyAll机制4. 使用轮询&#xff08;Polling&#xff09; &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java…