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 ...
随机推荐
- WebGL 绘制和变换
1.使用缓冲区对象向顶点着色器传入多个顶点的数据,需要遵循以下五个步骤: 1.1 创建缓冲区对象(gl.createBuffer()). 1.2 绑定缓冲区对象(gl.bindBuffer()). 1 ...
- 基金、社保和QFII等机构的重仓股排名评测
来源:基金前20大重仓股持仓股排名 基金前15大重仓股持仓股排名 基金重仓前15大个股,相较于同期沪深300的平均收益,近1月:-1.05%,近3月:-0.49%,近6月:1.45%,近1年:3.92 ...
- Go 语言机制之逃逸分析
https://blog.csdn.net/weixin_38975685/article/details/79788254 Go 语言机制之逃逸分析 https://blog.csdn.net/ ...
- [Scikit-learn] 1.1 Generalized Linear Models - Lasso Regression
Ref: http://blog.csdn.net/daunxx/article/details/51596877 Ref: https://www.youtube.com/watch?v=ipb2M ...
- Laravel5.1 填充数据库
当我们创建好表结构后 通常都要生成一些测试用的数据来测试,应对这个场景呢 Laravel提供了相当好的服务 --seed Laravel的seeder都会放在:/database/seeders 目录 ...
- MySQL 高性能表设计规范
良好的逻辑设计和物理设计是高性能的基石, 应该根据系统将要执行的查询语句来设计schema, 这往往需要权衡各种因素. 一.选择优化的数据类型 MySQL支持的数据类型非常多, 选择正确的数据类型对于 ...
- Java -- 异常的捕获及处理 -- 自定义异常类
7.4 自定义异常类 定义异常类只需要继承Exception类即可. 例:自定义异常类 Class : MyException package limeThrowable._7_4; public c ...
- GSAP JS基础教程--认识GSAP JS
第一次写博文呢,这次写博客是因为应一位同学的要求,写一下GSAP JS的一个小教程.为什么说小呢?因为它实际上就是小,只是一个入门级的小教程.如果你想问:“那你为什么不写详细一点呢?”,我想说,说., ...
- 使用Bind搭建DNS服务
DNS域名解析服务(Domain Name System)是用于解析域名与IP地址对应关系的服务,功能上可以实现正向解析与反向解析: 正向解析:根据主机名(域名)查找对应的IP地址. 反向解析:根据I ...
- pojo与DTO的区别
ational Mapping(对象关系映射)的缩写.通俗点讲,就是将对象与关系数据库绑定,用对象来表示关系数据.在O/R Mapping的世界里,有两个基本的也是重要的东东需要了解,即VO,PO. ...