ActiveMQ

1.JMS介绍

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持(百度百科给出的概述)。我们可以简单的理解:两个应用程序之间需要进行通信,我们使用一个JMS服务,进行中间的转发,通过JMS 的使用,我们可以解除两个程序之间的耦合。
JMS(JAVA Message Service,java消息服务)API是一个消息服务的标准或者说是规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
Java消息队列–JMS概述

2.ActiveMQ介绍

JMS是一个用于提供消息服务的技术规范,它制定了在整个消息服务提供过程中的所有数据结构和交互流程。而MQ则是消息队列服务,是面向消息中间件(MOM)的最终实现,是真正的服务提供者;MQ的实现可以基于JMS,也可以基于其他规范或标准。
支持JMS的开源MQ:
目前选择的最多的是ActiveMQ(是对JMS的一种实现)。
MQ常用的有ActiveMQ,RabbitMQ,kafka。
在这里插入图片描述
在这里插入图片描述
消息中间件
active MQ介绍

2.1 名词解释

Destinationo
目的地,JMSProvider(消息中间件)负责维护,用于对Message进行管理的对象。
MessageProducer需要指定Destination才能发送消息,MessageConsumer要指定Destination才能接收消息;
Producere
消息生成者(客户端、生成消息),负责发送Message到目的地;应用接口为MessageProducere在JMS规范中,所有的标准定义都在javax.jms包中。
Consumer〖Receiver〗
消息消费者(处理消息),负责从目的地中消费〖处理|监听|订阅〗Messagee应用接口为MessageConsumer
Message
消息,消息封装一次通信的内容。常见类型有:
TextMessage、ObjectMessage、MapMessage、StreamMessage、BytesMessage
ConnectionFactory
链接工厂,用于创建链接的工厂类型。注意,不能和JDBC中的ConnecnonFactory混淆
Connection
链接.用于建立访问ActiveMQ连接的类型,由链接工厂创建.注意,不能和JDBC中的Connection混淆
Session
会话,一次持久有效状态的访问.由链接创建.是具体操作消息的基础支撑
Queue&Topico
Queue是队列目的地,Topic是主题目的地:都是Destination的子接口。
Queue特点:队列中的消息,默认只能由唯一的一个消费者处理。一旦处理消息就删除
Topic特点:主题中的消息,会发送给所有的消费者同时处理:只有在消息可以重复处
理的业务场景中可使用。

2.2 常用配置

2.2.1 端口查看

1.jetty.xml

    <bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
             <!-- the default port number for the web console -->
        <property name="host" value="0.0.0.0"/>
        <property name="port" value="8161"/>
    </bean>
2.2.2 账户查看

1.jetty-realm.properties

# username: password [,rolename ...]
#账户名:密码,角色
admin: admin, admin
user: user, user
2.2.3 安全认证配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.4 持久化策略
  • ActiveMQ中,持久化是指对消息数据的持久化。在ActiveMQ中,默认的消息是保存在内存中的。当内存容量不足的时候,或ActiveMQ正常关闭的时候,会将内存中的未处理的消息持久化到磁盘中。具体的持久化策略由配置文件中的具体配置决定.
  • ActiveMQ的默认存储策略是kahadb。如果使用JDBC作为持久化策略,则会将所有的需要持久化的消息保存到数据库中。
  • 所有的持久化配置都在conf/activemq.xml中配置,配置信息都在broker标签内部定义。

在这里插入图片描述
Activemq.xml
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在activemq.xml中配置如下:注意:若缺包,则把相关包上传到Lib目录去
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.下载

官网地址
在这里插入图片描述

4.启动

前提:必须按照JDK并配置环境变量
在下面目录bin里找到activemq.bat脚本启动
在这里插入图片描述
从它的目录来说,还是很简单的:

  • bin 存放的是脚本文件
  • conf 存放的是基本配置文件
  • data 存放的是日志文件
  • docs 存放的是说明文档
  • examples 存放的是简单的实例
  • lib 存放的是activemq所需jar包
  • webapps 用于存放项目的目录

5.登陆

启动后,访问:http://127.0.0.1:8161/admin/
默认用户与密码为:admin admin
登陆后,管理界面如图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.编程使用

前提:导入必要包

    <dependencies>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.11.1</version>
        </dependency>
    </dependencies>

OR
在这里插入图片描述

6.1 PTP模型(基于Queue)

成小胖学习ActiveMQ·基础篇
在这里插入图片描述

  • 消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息
  • 消息被消费以后,queue中不再有存储,所消息消费者不可能消费到已经被消费的消息
  • Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费、其它
    的则不能消费此消息了
  • 当消费者不存在时,消息会一直保存,直到有消费消费
  • 比如生产者生产了M1消息,而目前有两个消费则C1,C2,那么M1放到服务器队列Queue中后,C1消费了消息M1,那么C2就无法再获取消息了,因为队列Queue中已经没有消息M1了
    生产者
public class JMSProducer {

	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; // 默认的连接地址
	private static final int SENDNUM=10; // 发送的消息数量

	public static void main(String[] args) {

		ConnectionFactory connectionFactory; // 连接工厂
		Connection connection = null; // 连接
		Session session = null; // 会话 接受或者发送消息的线程
		Destination destination = null ; // 消息的目的地
		MessageProducer messageProducer = null; // 消息生产者

		// 1.实例化连接工厂
		//创建连接工厂,连接ActiveMQ服务的连接工厂。
		//创建工厂,构造方法有三个参数,分别是用户名,密码,连接地址
		//无参构造,有默认的链接地址。本地链接。localhost
		//单参数构造,无验证模式的。没有用户的认证。
		//三参数构造,有认证+指定地址。默认端口是61616。从ActiveMQ的conf/activemq.xml配置文件中查看。
		//factory =new ActiveMQConnectionFactory("guest","guest","tcp://192.168.159.129:61616");
		connectionFactory=new ActiveMQConnectionFactory(JMSProducer.USERNAME, JMSProducer.PASSWORD, JMSProducer.BROKEURL);

		try {
			// 2.通过连接工厂获取连接
			//创建连接的方法有重载,其中有createConnection(String username,String password)
			//可以在创建连接工厂时,只传递连接地址,不传递用户信息.
			connection=connectionFactory.createConnection();
			// 3.启动连接
			//建议启动连接,消息的发送者不是必须启动连接。消息的消费者必须启动连接。
			//producer在发送消息的时候,会检查是否启动了链接。如果未启动,自动启动。
			//如果有特殊的配置,建议配置完毕后再启动链接。
			connection.start();
			// 4.创建Session
			/*
			创建会话的时候,必须传递两个参数,分别代表是否支持事务和如何确认消息处理。
			transacted-是否支持事务,数据类型是boolean.true-支持,false-不支持
				true-支持事务,第二个参数默认无效,建议传递的数据是Session.SESSION TRANSACTED
				false-不支持事务,常用参数。第二个参数必须传递,且必须有效。
			acknowledgeMode-如何确认消息的处理。使用确认机制实现的。
				AUTO ACKNOWLEDGE-自动确认消息。消息的消费者处理消息后,自动确认。常用
				CLIENT ACKNOWLEDGE-客户端手动确认,消息的消费者处理后,必须手工确认!
				DUPS OK ACKNOWLEDGE-有副本的客户端手动确认。
			一个消息可以多次处理。
			可以降低Session的消耗,在可以容忍重复消息时使用。(不推荐使用)
			*/
			session=connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
			// 5.创建消息队列
			//创建目的地。参数是目的地名称。是目的地的唯一标记。会在MQ服务器创建一个名字为FirstQueue1目的地类
			//用于接收该条目的地接收和发送消息
			destination=session.createQueue("FirstQueue1");
			// 6.创建消息生产者
			//创建的消息发送者,发送的消息一定到指定的目的地中。
			//创建producer的时候,可以不提供目的地。在发送消息的时候制定目的地。比如createProducer(nul)
			messageProducer=session.createProducer(destination);
			// 7.发送消息
			sendMessage(session, messageProducer);
			// 8.提交事务
			//由于session是开启了事务,所以必须执行提交操作,不然缓存中的数据是不会上传到服务器中去的
			session.commit();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			// 9 关闭资源
			if(messageProducer !=null){
				try {
					messageProducer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session!=null){
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection!=null){
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 发送消息
	 * @param session
	 * @param messageProducer
	 * @throws Exception
	 */
	public static void sendMessage(Session session,MessageProducer messageProducer)throws Exception{
		for(int i=0;i<JMSProducer.SENDNUM;i++){
			TextMessage message=session.createTextMessage("ActiveMQ 发送的消息"+i);
			System.out.println("发送消息:"+"ActiveMQ 发送的消息"+i);
			messageProducer.send(message);
		}
	}
}

在这里插入图片描述

Number Of Consumers :表示消费者数量;
Number Of Pending Messages :等待消费的消息,这个是当前未出队列的数量;
Messages Enqueued :进入队列的消息;( 这个数量只增不减,重启acmq后会清零)
Messages Dequeued :出了队列的消息 可以理解为是消费这消费掉的数量 (重启acmq后会清零)

6.1.1 主动消费(receive调用一次,获取一次消息)

消费者(循环获取)

public class JMSConsumer {

    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; // 默认的连接地址

    public static void main(String[] args) {
        ConnectionFactory connectionFactory; // 连接工厂
        Connection connection = null; // 连接
        Session session; // 会话 接受或者发送消息的线程
        Destination destination; // 消息的目的地
        MessageConsumer messageConsumer; // 消息的消费者

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

        try {
            connection=connectionFactory.createConnection();
            // 启动连接
            connection.start();
            // 创建Session
            session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            // 创建连接的消息队列
            destination=session.createQueue("MyQueueNO.1");
            // 创建消息消费者
            messageConsumer=session.createConsumer(destination);
            // 每10秒查询一次
            while(true){
            	//receive主动获取消息,每执行一次,就从消息中心消费一条消息
                TextMessage textMessage=(TextMessage)messageConsumer.receive(10000);
                if(textMessage!=null){
                    System.out.println("收到的消息:"+textMessage.getText());
                }else{
                    break;
                }
            }
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

若队列里没有生产者产生的消息,那么启动消费者就会一直阻塞,知道队列里有消息了,消费者才算执行下去

6.1.2 观察者消费

消费者(监听获取)

public class JMSConsumer2 {

    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; // 默认的连接地址

    public static void main(String[] args) {
        ConnectionFactory connectionFactory; // 连接工厂
        Connection connection = null; // 连接
        Session session; // 会话 接受或者发送消息的线程
        Destination destination; // 消息的目的地
        MessageConsumer messageConsumer; // 消息的消费者

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

        try {
            connection=connectionFactory.createConnection();
            // 启动连接
            connection.start();
            // 创建Session
            session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            // 创建连接的消息队列
            destination=session.createQueue("MyQueueNO.1");
            // 创建消息消费者
            messageConsumer=session.createConsumer(destination);
            // 注册一个监听器
            // 当启动消费者的时候,就会监听MyQueueNO.1这个队列里的消息,有则获取过来
            messageConsumer.setMessageListener(new Listener());

        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这里需要增加一个监听器,如下:
监听器

public class Listener implements MessageListener{

	public void onMessage(Message message) {
		try {
			System.out.println("监听器收到消息"+((TextMessage)message).getText());
		} catch (JMSException e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

6.2 PUB/SUB模型(基于Topic)

在这里插入图片描述

  • 消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息
  • 和点对点方式不同,发布到topic的消息会被所有订阅者消费
  • 当生产者发布消息,不管是否有消费者。都不会保存消息
  • 一定要先有消息的消费者,后有消息的生产者

主题生产者

public class JMSProducer {

    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; // 默认的连接地址
    private static final int SENDNUM=5; // 发送的消息数量

    public static void main(String[] args) {

        ConnectionFactory connectionFactory; // 连接工厂
        Connection connection = null; // 连接
        Session session; // 会话 接受或者发送消息的线程
        Destination destination; // 消息的目的地
        MessageProducer messageProducer; // 消息生产者

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

        try {
            connection=connectionFactory.createConnection();
            // 启动连接
            connection.start();
            // 创建Session
            session=connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            // 创建消息主题名
            destination=session.createTopic("MyTopicNO.1");
            // 创建消息生产者
            messageProducer=session.createProducer(destination);
            // 发送消息
            for(int i = 0; i< JMSProducer.SENDNUM; i++){
                TextMessage message=session.createTextMessage("ActiveMQ 发送的消息"+i);
                System.out.println("发送消息:"+"ActiveMQ 发送的消息"+i);
                messageProducer.send(message);//通过生产者发送消息
            }
            // 提交事务
            session.commit();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally{
            if(connection!=null){
                try {
                    connection.close();
                } catch (JMSException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

在这里插入图片描述
上图名词解释
主题消费者

public class JMSConsumer {

    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; // 默认的连接地址

    public static void main(String[] args) {
        ConnectionFactory connectionFactory; // 连接工厂
        Connection connection = null; // 连接
        Session session; // 会话 接受或者发送消息的线程
        Destination destination; // 消息的目的地
        MessageConsumer messageConsumer; // 消息的消费者

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

        try {
            connection=connectionFactory.createConnection();
            // 启动连接
            connection.start();
            // 创建Session
            session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            // 创建主题名
            destination=session.createTopic("MyTopicNO.1");
            // 创建消息消费者
            messageConsumer=session.createConsumer(destination);
            // 注册消息监听
            messageConsumer.setMessageListener(new Listener()); 
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

监听器

public class Listener implements MessageListener {

    @Override
    public void onMessage(Message message) {
        try {
            System.out.println("订阅者一收到的消息:"+((TextMessage)message).getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

6.3 ptp和pub/sub 对比

在这里插入图片描述
在这里插入图片描述

6.4 在线发送

在这里插入图片描述
在这里插入图片描述

注意:
主题消费者一定要比主题生产者先启动起来,不然主题生产者前面发的消息,消费者是不会接受到的,只有消费者先启动监听,后续发过来的消息才能收到
队列不存在这种情况,队列生产者先产生消息,队列消费者后启动也能收到这个消息

与Spring集成

后续更新…

参考资料

一头扎进 JMS 之 ActiveMQ
浅析jms之ActiveMQ
ActiveMQ简介

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值