JAVA实现简单MQ队列服务
主要角色
首先我们必须搞明白MQ消息队列中的三个主要角色
Produce
Broker
Consumer
整体架构如下
首先从上一篇中介绍了协议的相关信息,具体厂商的 MQ(消息队列) 需要遵循某种协议或者自定义协议, 消息的 生产者和消费者需要遵循其协议(约定)才能后成功地生产消息和生产消息 ,所以在这里我们自定义一个协议如下.
消息处理中心:如果接收到的信息包含“SEND”字符串,即视为生产者发送的消息,消息处理中心需要将此信息存储等待消费者消费
消息处理中心:如果接收到的信息为CONSUME,即视为消费者发送消费请求,需要将存储的消息队列头部的信息转发给消费者,然后将此消息从队列中移除
消息处理中心:如果消息处理中心存储的消息达到队列预设深度后仍然没有消费者进行消费,则不再接受生产者的生产请求
消息生产者:需要遵循协议将生产的消息头部增加“SEND:”表示生产消息
消息消费者:需要遵循协议向消息处理中心发送“CONSUME”字符串表示消费消息
流程顺序
项目构建流程
下面将整个MQ的构建流程详述
1.新建一个Broker类,内部维护一个ArrayBlockingQueue队列,提供生产消息和消费消息的方法,仅仅具备存储服务功能;
2.新建一个BrokerServer类,将Broker发布为服务到本地9999端口,监听本地9999端口的Socket连接,在接受的信息中进行我们的协议校验,这里仅仅具备接受消息,校验协议,转发消息功能;
3.新建一个MqClient类,此类提供与本地9999的Socket连接,仅仅具备生产消息和消费消息的方法;
4.测试:新建两个MyClient类对象,分别执行其生产方法和消费方法。
具体使用流程
生产消息:客户端执行生产消息方法,传入需要生产的消息,该信息需要遵循我们自定义的协议,消息处理中心服务在接收到消息会根据自定义的协议校验该消息是否合法,如果合法就会将该消息存储到Broker内部维护的ArrayBlockingQueue队列中。如果ArrayBlockingQueue队列没有达到我们协议中的最大长度将将消息添加到队列中,否则输出生产消息失败;
消费消息:客户端执行消费消息方法,Broker服务会校验请求的信息的信息是否等于CONSUME,如果验证成功则从Broker内部维护的ArrayBlockingQueue对立的Poll出一个消息返回给客户端。
代码演示
客户端执行生产消费信息包(AppClient)
客户端生产消息(ProduceClient类)
package AppClient;
import CenterServer.MqClient;
public class ProduceClient {
//生产消息
public static void main(String[] args) throws Exception{
MqClient client = new MqClient();
client.produce("SEND:HELLO WORLD!");
}
}
客户端消费消息(ConsumeClient类)
package AppClient;
import CenterServer.MqClient;
public class ConsumeClient {
//消费消息
public static void main(String[] args) throws Exception{
MqClient client = new MqClient();
String message = client.consume();
System.out.println("获取的消息为:" + message);
}
}
消息中心服务(CenterServer包)
消息处理中心(Broker类)
package CenterServer;
import java.util.concurrent.ArrayBlockingQueue;
/**
* @author CK
* 消息处理中心
*/
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类)
package CenterServer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author CK
* 启动消息处理中心
*/
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() {
// TODO Auto-generated method stub
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream());
while(true){
String str = in.readLine();
if(str == null){
continue;
}
System.out.println("接收到原始数据:" + str);
if(str.equals("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 (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try{
in.close();
out.close();
}catch(IOException 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类)
package CenterServer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class MqClient {
//生产消息
public static void produce(String message) {
//本地的BrokerServer.SERVICE_PORT 创建SOCKET
Socket socket = null;
PrintWriter out = null;
try {
socket = new Socket(InetAddress.getLocalHost(),BrokerServer.SERVICE_PORT);
out = new PrintWriter(socket.getOutputStream());
out.println(message);
out.flush();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
socket.close();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//消费消息
public static String consume() {
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
String message = null;
try {
socket = new Socket(InetAddress.getLocalHost(),BrokerServer.SERVICE_PORT);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream());
out.println("CONSUME");
out.flush();
message = in.readLine();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
socket.close();
in.close();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return message;
}
}
接着多次执行生产消息客户端及消费消息客户端即可实现MQ消息队列的通讯过程。
本文部分摘自:
作者:跨过山河大海
来源:CSDN
链接:https://blog.csdn.net/qq_33814088/article/details/85029685