Java语言快速实现简单MQ消息队列服务
MQ基础回顾
在上一篇消息通讯之关于消息队列MQ必须了解的相关概念中 , 我们尽可能地详细的了解了一些关于MQ (消息队列) 的相关概念,并且我们上一篇中提到一个最基本的MQ通讯模型如下所示,所以本章节使用JAVA语言自己动手来写一个MQ (类似ActiveMQ,RabbitMQ)

主要角色
首先我们必须需要搞明白MQ (消息队列) 中的三个基本角色
Producer: 消息生产者,负责生产消息并发送到BrokerBroker: 消息处理中心,负责接受消息,存储消息,转发消息Consumer:消息消费者,负责消费消息
整体架构如下所示

自定义协议
首先从上一篇中介绍了协议的相关信息,具体厂商的MQ(消息队列)需要遵循某种协议或者自定义协议 , 消息的生产者和消费者需要遵循其协议(约定)才能后成功地生产消息和生产消息,所以在这里我们自定义一个协议如下.
消息处理中心 : 如果接收到的信息包含"SEND"字符串,即视为生产者发送的消息,消息处理中心需要将此信息存储等待消费者消费
消息处理中心 : 如果接受到的信息为CONSUME,既视为消费者发送消费请求,需要将存储的消息队列头部的信息转发给消费者,然后将此消息从队列中移除
消息处理中心 : 如果消息处理中心存储的消息满3条仍然没有消费者进行消费,则不再接受生产者的生产请求
消息生产者:需要遵循协议将生产的消息头部增加 "SEND:" 表示生产消息
消息消费者:需要遵循协议向消息处理中心发送 "CONSUME"字符串表示消费消息
流程顺序
项目构建流程
下面将整个MQ的构建流程过一遍
- 新建一个
Broker类,内部维护一个ArrayBlockingQueue队列,提供生产消息和消费消息的方法,仅仅具备存储服务功能 - 新建一个
BrokerServer类,将Broker发布为服务到本地9999端口,监听本地9999端口的Socket链接,在接受的信息中进行我们的协议校验, 这里仅仅具备接受消息,校验协议,转发消息功能; - 新建一个
MqClient类,此类提供与本地端口9999的Socket链接 ,仅仅具备生产消息和消费消息的方法 - 测试:新建两个
MyClient类对象,分别执行其生产方法和消费方法
具体使用流程
- 生产消息:客户端执行生产消息方法,传入需要生产的信息,该信息需要遵循我们自定义的协议,消息处理中心服务在接受到消息会根据自定义的协议校验该消息是否合法,如果合法如果合法就会将该消息存储到Broker内部维护的
ArrayBlockingQueue队列中.如果ArrayBlockingQueue队列没有达到我们协议中的最大长度将将消息添加到队列中,否则输出生产消息失败. - 消息消息:客户端执行消费消息方法,
Broker服务会校验请求的信息的信息是否等于CONSUME,如果验证成功则从Broker内部维护的ArrayBlockingQueue队列的Poll出一个消息返回给客户端
代码演示
消息处理中心 Broker
/**
* 消息处理中心
*/
public class Broker {
// 队列存储消息的最大数量
private final static int MAX_SIZE = 3;
// 保存消息数据的容器
private static ArrayBlockingQueue<String> messageQueue = new ArrayBlockingQueue<String>(MAX_SIZE);
// 生产消息
public static void produce(String msg) {
if (messageQueue.offer(msg)) {
System.out.println("成功向消息处理中心投递消息:" + msg + ",当前暂存的消息数量是:" + messageQueue.size());
} else {
System.out.println("消息处理中心内暂存的消息达到最大负荷,不能继续放入消息!");
}
System.out.println("=======================");
}
// 消费消息
public static String consume() {
String msg = messageQueue.poll();
if (msg != null) {
// 消费条件满足情况,从消息容器中取出一条消息
System.out.println("已经消费消息:" + msg + ",当前暂存的消息数量是:" + messageQueue.size());
} else {
System.out.println("消息处理中心内没有消息可供消费!");
}
System.out.println("=======================");
return msg;
}
}
消息处理中心服务 BrokerServer
/**
* 用于启动消息处理中心
*/
public class BrokerServer implements Runnable {
public static int SERVICE_PORT = 9999;
private final Socket socket;
public BrokerServer(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())
)
{
while (true) {
String str = in.readLine();
if (str == null) {
continue;
}
System.out.println("接收到原始数据:" + str);
if (str.equals("CONSUME")) { //CONSUME 表示要消费一条消息
//从消息队列中消费一条消息
String message = Broker.consume();
out.println(message);
out.flush();
} else if (str.contains("SEND:")){
//接受到的请求包含SEND:字符串 表示生产消息放到消息队列中
Broker.produce(str);
}else {
System.out.println("原始数据:"+str+"没有遵循协议,不提供相关服务");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(SERVICE_PORT);
while (true) {
BrokerServer brokerServer = new BrokerServer(server.accept());
new Thread(brokerServer).start();
}
}
}
客户端 MqClient
/**
* 访问消息队列的客户端
*/
public class MqClient {
//生产消息
public static void produce(String message) throws Exception {
//本地的的BrokerServer.SERVICE_PORT 创建SOCKET
Socket socket = new Socket(InetAddress.getLocalHost(), BrokerServer.SERVICE_PORT);
try (
PrintWriter out = new PrintWriter(socket.getOutputStream())
) {
out.println(message);
out.flush();
}
}
//消费消息
public static String consume() throws Exception {
Socket socket = new Socket(InetAddress.getLocalHost(), BrokerServer.SERVICE_PORT);
try (
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream())
) {
//先向消息队列发送命令
out.println("CONSUME");
out.flush();
//再从消息队列获取一条消息
String message = in.readLine();
return message;
}
}
}
测试MQ
public class ProduceClient {
public static void main(String[] args) throws Exception {
MqClient client = new MqClient();
client.produce("SEND:Hello World");
}
}
public class ConsumeClient {
public static void main(String[] args) throws Exception {
MqClient client = new MqClient();
String message = client.consume();
System.out.println("获取的消息为:" + message);
}
}
我们多执行几次客户端的生产方法和消费方法就可以看到一个完整的MQ的通讯过程,下面是我执行了几次的一些日志
接收到原始数据:SEND:Hello World
成功向消息处理中心投递消息:SEND:Hello World,当前暂存的消息数量是:1
=======================
接收到原始数据:SEND:Hello World
成功向消息处理中心投递消息:SEND:Hello World,当前暂存的消息数量是:2
=======================
接收到原始数据:SEND:Hello World
成功向消息处理中心投递消息:SEND:Hello World,当前暂存的消息数量是:3
=======================
接收到原始数据:SEND:Hello World
消息处理中心内暂存的消息达到最大负荷,不能继续放入消息!
=======================
接收到原始数据:Hello World
原始数据:Hello World没有遵循协议,不提供相关服务
接收到原始数据:CONSUME
已经消费消息:SEND:Hello World,当前暂存的消息数量是:2
=======================
接收到原始数据:CONSUME
已经消费消息:SEND:Hello World,当前暂存的消息数量是:1
=======================
接收到原始数据:CONSUME
已经消费消息:SEND:Hello World,当前暂存的消息数量是:0
=======================
接收到原始数据:CONSUME
消息处理中心内没有消息可供消费!
=======================
小结
本章示例代码主要源自分布式消息中间件实践一书 , 这里我们自己使用Java语言写了一个MQ消息队列 , 通过这个消息队列我们对MQ中的几个角色 "生产者,消费者,消费处理中心,协议"有了更深的理解 ; 那么下一章节我们就来一块学习具体厂商的MQ RabbitMQ
Java语言快速实现简单MQ消息队列服务的更多相关文章
- IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列
1.引言 消息是互联网信息的一种表现形式,是人利用计算机进行信息传递的有效载体,比如即时通讯网坛友最熟悉的即时通讯消息就是其具体的表现形式之一. 消息从发送者到接收者的典型传递方式有两种: 1)一种我 ...
- 手把手教你用redis实现一个简单的mq消息队列(java)
众所周知,消息队列是应用系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息队列有 ActiveMQ,RabbitMQ,Zero ...
- C# Queue与RabbitMQ的爱恨情仇(文末附源码):Q与MQ消息队列简单应用(二)
上一章我们讲了队列( Queue),这一章我们讲Message Queue消息队列,简称MQ. 定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开 ...
- Java编程的逻辑 (61) - 内存映射文件及其应用 - 实现一个简单的消息队列
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
- 多维度对比5款主流分布式MQ消息队列,妈妈再也不担心我的技术选型了
1.引言 对于即时通讯网来说,所有的技术文章和资料都在围绕即时通讯这个技术方向进行整理和分享,这一次也不例外.对于即时通讯系统(包括IM.消息推送系统等)来说,MQ消息中件间是非常常见的基础软件,但市 ...
- 使用Rabbit MQ消息队列
使用Rabbit MQ消息队列 综合概述 消息队列 消息队列就是一个消息的链表,可以把消息看作一个记录,具有特定的格式以及特定的优先级.对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息 ...
- 阿里云ACE共创空间——MQ消息队列产品测试
一.产品背景消息队列是阿里巴巴集团自主研发的专业消息中间件. 产品基于高可用分布式集群技术,提供消息订阅和发布.消息轨迹查询.定时(延时)消息.资源统计.监控报警等一系列消息云服务,是企业级互联网架构 ...
- 【阿里云产品公测】消息队列服务MQS java SDK 机器人应用初体验
[阿里云产品公测]消息队列服务MQS java SDK 机器人应用初体验 作者:阿里云用户啊里新人 初体验 之 测评环境 由于MQS支持外网访问,因此我在本地做了一些简单测试(可能有些业余),之后 ...
- 高并发架构系列:MQ消息队列的12点核心原理总结
消息队列已经逐渐成为分布式应用场景.内部通信.以及秒杀等高并发业务场景的核心手段,它具有低耦合.可靠投递.广播.流量控制.最终一致性 等一系列功能. 无论是 RabbitMQ.RocketMQ.Act ...
随机推荐
- Ubuntu16.04下安装CUDA8.0和tensorflow
GPU版的Tensorflow无疑是深度学习的一大神器,当然caffe之类的框架也可以用GPU来加速训练. 注意:以下安装默认为python2.7 1. 安装依赖包 $ sudo apt-get in ...
- thinkphp 在阿里云上的nginx.config配置
# For more information on configuration, see: # * Official English Documentation: http://nginx.org/e ...
- 手动安装Laravel
http://www.golaravel.com/download/ 下载即可用 注意 wamp必须要在 2.5以上
- python __all__用法
主要是用来限定暴露的api a.py文件里面的内容 __all__ = ['major_fun'] def major_fun(): pass def assist_fun(): pass b.py ...
- 关于UIGestureRecognizerState
UIGestureRecognizerState的定义如下 typedef enum { UIGestureRecognizerStatePossible, UIGestureRecognizerSt ...
- 02-centOS6.7安装
CentOS6.5在VMware10中安装 1.启动VMware的画面 2.点击File--->New Virtual Machine 创建一台新虚拟机 3.在弹出框中选择典型安装 4.选择I ...
- 8 -- 深入使用Spring -- 1...3 容器后处理器
8.1.3 容器后处理器(BeanFactoryPostProcessor) 容器后处理器负责处理容器本身. 容器后处理器必须实现BeanFacotryPostProcessor接口.实现该接口必须实 ...
- NetBpm 示例:请假审批(6)
转载注明出处: http://www.cnblogs.com/anbylau2130/p/3877983.html 原文: 请假示例 流程定义包源码下载(注:par包就是zip格式压缩包).原文地址: ...
- mssql注入指令
and exists (select * from sysobjects) //判断是否是MSSQL and exists(select * from tableName) //判断某表是否存在..t ...
- Python闭包装饰器笔记
Python三大器有迭代器,生成器,装饰器,这三个中使用最多,最重要的就是装饰器.本篇将重要从函数嵌套开始讲起,从而引入闭包,装饰器的各种用法等. python中的一切都是一个对象(函数也是) 1.首 ...