消息队列之ActiveMQ的P2P、PubSub的实现小demo

本文介绍了使用ActiveMQ实现P2P和PubSub模式的消息传输。JMS是Java消息服务接口,而ActiveMQ是其实现之一。在P2P模式中,消息通过队列异步传输,类似于邮箱系统。消费者竞争接收消息,只有一个能接收到。在PubSub模式下,消息发布者与多个订阅者之间是一对多的关系,实时性较强,且所有订阅者接收到的消息相同。文章还提供了相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记忆之中没有辛苦,只有遗憾。——(忘了哪看到了)

首先说一下JMS:
JMS:JAVA 消息服务。定义了Java 中访问消息中间件的接口。JMS 只是接口, 并没有给予实现,实现JMS接口的消息中间件称为JMS Provider,例如 ActiveMQ。

与之相关的几个关键字也罗列一下:

JMS Provider:实现JMS 接口的消息中间件;
PTP:Point to Point,即点对点的消息模型;
Pub/Sub:Publish/Subscribe,即发布/订阅的消息模型;
Queue:队列目标;
Topic:主题目标;
ConnectionFactory:连接工厂,JMS 用它创建连接;
Connection:JMS 客户端到JMS Provider 的连接;
Destination:消息的目的地;
Session:会话,一个发送或接收消息的线程;
MessageProducer:由Session 对象创建的用来发送消息的对象;
MessageConsumer:由Session 对象创建的用来接收消息的对象;
Acknowledge:签收;
Transaction:事务。

注意:在执行p2p或者发布订阅的时候,不要忘了首先启动从官网https://mirror.bit.edu.cn/apache//activemq/5.15.12/apache-activemq-5.15.12-bin.zip下载的apache-activemq-5.15.12,这是我用的版本,其他版本都行,下载下来解压后不用作任何配置,直接在bin\win64 ( 还有个win32的目录,不过没有人用32的了吧 ) 下执行activemq.bat就行

P2P:

P2P(Point-to-Point)模型是基于队列的,生产者发消息到队列,消费者从队列接收消息,队列的存在使得消息的异步传输成为可能。和邮件系统中的邮箱一样,
队列可以包含各种消息,JMS Provider 提供工具管理队列的创建、删除。
JMS P2P 模型定义了客户端如何向队列发送消息,从队列接收消息,浏览队列中的消息。

pom文件
只新加了一个activemq-all依赖

<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.15.0</version>
        </dependency>

provider类

package com.yuxi.test;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

import javax.jms.*;

public class ActiveMQTest {

    @Test
    public void testProvider() {

//        创建一个activeMQ的连接工厂,使用三参构造器,值使用提供的默认值
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnectionFactory.DEFAULT_USER,
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL);

        try {
//            利用工厂生产出一个连接
            Connection connection = connectionFactory.createConnection();
//            启动连接
            connection.start();
//            通过连接创建会话对象,第一个参数为是否开启事物,第二个参数为自动应答模式
            Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//            通过session创建队列对象
            Queue queue = session.createQueue("queue-01");

            MessageProducer producer = session.createProducer(queue);

            sendMessage(session, producer);

            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    private void sendMessage(Session session, MessageProducer producer) {

        try {
            TextMessage message = session.createTextMessage("hello world");

            producer.send(message);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }


}

consumer类

package com.yuxi.test;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

import javax.jms.*;

public class ConsumerTest {

    @Test
    public void test(){
//        创建一个activeMQ的连接工厂,使用三参构造器,值使用提供的默认值
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnectionFactory.DEFAULT_USER,
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL);

        try {
//            利用工厂生产出一个连接
            Connection connection = connectionFactory.createConnection();
//            启动连接
            connection.start();
//            通过连接创建会话对象,第一个参数为是否开启事物,第二个参数为自动应答模式
            Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//            通过session创建队列对象
            Queue queue = session.createQueue("queue-01");

            MessageConsumer consumer = session.createConsumer(queue);

            while (true){
                TextMessage msg = (TextMessage) consumer.receive(100000);

                if(msg != null){
                    System.out.println("接收到的消息为:" + msg.getText());
                }else{
                    break;
                }
            }

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

PubSub

pub类:

package com.yuxi.test;

import jdk.internal.org.objectweb.asm.commons.TryCatchBlockSorter;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

import javax.jms.*;

public class PubTest {

    @Test
    public void test(){
        try {
            ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");

            Connection connection = factory.createConnection();
            connection.start();

            Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);

            Queue queue = session.createQueue("queue-pubsub");

            MessageProducer messageProducer = session.createProducer(queue);

            sendMessages(session,messageProducer);

            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    private void sendMessages(Session session, MessageProducer messageProducer) throws JMSException {

        for (int i = 0; i < 20; i++) {
            TextMessage textMessage = session.createTextMessage("activemq发送的消息:"+i);

            System.out.println("发送消息:" + "ActiveMQ 发送的消息" + i);

            messageProducer.send(textMessage);
        }
    }
}

sub类:

package com.yuxi.test;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;


/**
 * 消息消费者
 *
 * @author wangzhipeng
 */

public class SubTest {

    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; // 默认的连接用户名

    private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; // 默认的连接密码

    private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL; // 默认的连接地址

    @Test
    public void test() {

        ConnectionFactory connectionFactory; // 连接工厂

        Connection connection = null; // 连接

        Session session; // 会话 接受或者发送消息的线程

        Destination destination; // 消息的目的地

        MessageConsumer messageConsumer;// 消息消费者

        // 实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);

        try {

            connection = connectionFactory.createConnection();// 通过工厂获取连接

            connection.start();// 启动连接

            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 第一个参数为是否开启事务

            destination = session.createQueue("queue-pubsub");// 创建消息队列

            messageConsumer = session.createConsumer(destination);// 创建消息消费者

            /*
             * 实际应用中,不会这么用,会注册一个监听
             */
            while (true) {

//                receive中的参数代表等待接受的时间,时间到了就不等着接受了,无参代表长连接
                TextMessage textMessage = (TextMessage) messageConsumer.receive(100000);

                if (textMessage != null) {
                    System.out.println("收到的消息:" + textMessage.getText());
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意p2p和pubsub的区别:

p2p是点对点的传输,
1、在provider发送之后,如果有两个同时开启的consumer的话,只会有先开启的consumer接受到消息,另一个不会接受到,在接受到消息的consumer关闭之后,另一个consumer才会接受到。此时指的消息包括了历史消息(服务器一直运行的情况下)

2、如果先依次启动两个consumer,在启动provider的话,先启动的consumer会接受到provider发来的消息(包括历史消息),后启动的consumer即使在第一个consumer停止了后,也不会接受到消息(包括历史消息)
pubsub是一对多的传输
pubsub具有实时性,在这种模式下,必须要先启动两个consumer,再启动provider,应为在provider发送消息的时候,如果没有consumer接受,那就以后接受不到了,就像直播一样,在那个时间不在的话就看不到了,没有重播的。
在provider发送之后,两个consumer接受到的消息是一模一样的,这点和p2p不同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值