设计一个简单的消息队列

由于分布式系统的广泛应用,越来越多地涉及到系统间通信。系统间通信一般有两种方式,一种是基于远程过程调用的方式,另一种是基于消息队列的方式。基于消息队列的方式是指由应用中的某个系统负责发送消息,由关心这条消息的系统负责接收消息,并在接收到消息后进行各自的业务处理。
目前主流的消息中间件有RabbitMQ、RocketMQ、ActiveMQ、Kafka等

一、消息队列的作用

(1)解耦

消息队列的各种实现产品又叫,既然是中间件,就是用消息队列实现两个模块的远程调用,模块只关心自己的核心流程,而不依赖调用的执行结果。

(2)流量削峰

利用消息队列可以将短时间高并发请求持久化,然后逐步处理,从而削平高峰期的并发流量,改善系统性能

(3)日志收集

利用消息队列产品在接收和持久化消息方面的高性能,引入消息队列快速收集日志信息,避免为写入日志时的某些故障导致业务系统访问阻塞、请求延迟等。

(4)事务最终一致性

二、消息队列的功能特点

消息队列这个属于包含消息和队列两个关键词,消息是指应用间传递的数据,可以使简单的字符串,也可以是复杂的结构化对象定义格式;队列指消息的进和出,它包含一个容器,至少需实现消息的发送、接收和暂存功能。在生产环境中,消息队列还需解决诸如消息堆积、消息持久化、可靠投递、消息重复、严格有序、集群等各种问题。
消息队列的简单模型:
在这里插入图片描述
Broker:消息处理中心,负责消息的接收、存储、转发
Producer:消息生产者,负责产生和发送消息到消息处理中心
Consumer:消息消费者,负责从消息中心获取消息,并进行相应的处理

三、用java实现一个简单的消息队列

结构图:
在这里插入图片描述

Broker类:

package com.youzi.MQ;

import java.util.concurrent.ArrayBlockingQueue;

/**
 * 消息处理中心
 */
public class Broker {
    //设置存储消息的最大数量
    private final static int MAX_SIZE = 5;
    //保存消息的容器
    private static ArrayBlockingQueue<String> MassageQueue = new ArrayBlockingQueue<String>(MAX_SIZE);
    //生产消息
    public static void produce(String msg){
        if (MassageQueue.offer(msg)){
            System.out.println("成功向消息中心投递消息:"+msg+",当前暂存消息数目为"+MassageQueue.size());
        }else{
            System.out.println("消息中心已满,不能继续放入消息!");
        }
        System.out.println("==================================");
    }

    //消费消息
    public static String consume(){
        String msg = MassageQueue.poll();
        if(msg!=null){
            System.out.println("已经消费消息:"+msg+",当前暂存消息数目为"+MassageQueue.size());
        }else{
            System.out.println("消息处理中心已经没有消息可供消费!");
        }
        System.out.println("==================================");
        return msg;
    }
}

用BrokerServer类对外提供Broker类的服务:

package com.youzi.MQ;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 消息队列服务
 */
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 msg = Broker.consume();
                    out.println(msg);
                    out.flush();
                }else{//其他情况都表示要生产消息到消息队列中
                    Broker.produce(str);
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(BrokerServer.SERVICE_PORT);
        while(true){
            BrokerServer bs = new BrokerServer(server.accept());
            new Thread(bs).start();
        }
    }
}

客户端访问:

package com.youzi.MQ;

import org.omg.CORBA.portable.UnknownException;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class MQClient {

    //生产消息
    public static void produce(String msg) throws Exception {
        Socket socket = new Socket(InetAddress.getLocalHost(),BrokerServer.SERVICE_PORT);
        try (
            PrintWriter out = new PrintWriter(socket.getOutputStream());
            ){
            out.println(msg);
            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())){
            //先向消息队列发送CONSUME表示消费消息
            out.println("CONSUME");
            out.flush();
            //再从队列获取一条消息
            String message = in.readLine();
            return message;
        }
    }
}

生产者客户端测试类:

package com.youzi.MQ;

public class ProduceClient {
    public static void main(String[] args) throws Exception {
        MQClient client = new MQClient();
        client.produce("Hello World4!!");
    }
}

消费者客户端测试类:

扫描二维码关注公众号,回复: 4272715 查看本文章
package com.youzi.MQ;

public class ConsumeClient {
    public static void main(String[] args) throws Exception {
        MQClient client = new MQClient();
        String message = client.consume();
        System.out.println("获取的消息为:"+message);
    }
}

先启动服务端BrokerServer,因为消息处理中心最大容量设置了5,这里生产者启动6次,服务端输出:
在这里插入图片描述
然后启动6次消息消费端,服务端输出:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41172473/article/details/84580334