ActiveMQ 的官网 : http://activemq.apache.org
ActiveMQ
扩展出:
为什么要使用 MQ ?
解决了耦合调用、异步模型、抵御洪峰流量,保护了主业务,消峰。
在linux 的opt 目录下上传 mq 的压缩包,(使用vmware-tools 上传的)
并且将压缩包放到 /myactivemq 下
直接进入myactivemq 的 文件下的activemq 下的 bin 目录,使用 ./activemq start 命令启动
检查activemq 是否启动的三种方法: 也是三种查看后台进程的方法
ps -ef|grep activemq|grep -v grep // grep -v grep 可以不让显示grep 本来的信息netstat -anp|grep 61616 // activemq 的默认后台端口是61616lsof -i:61616docker ps -a
让启动的日志信息不在控制台打印,而放到专门的文件中:
./activemq start > /myactivemq/myrunmq.log
JMS : Java 消息中间件的服务接口规范,activemq 之上是 mq , 而 mq 之上是JMS 定义的消息规范 。 activemq 是mq 技术的一种理论实现(与之相类似的实现还有 Kafka RabbitMQ RockitMQ ),而 JMS 是更上一级的规范。
package com.db.privategoodsplatform.test;import org.apache.activemq.ActiveMQConnectionFactory;import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;/*** @author xuqinglei* @date 2023/03/14 10:50**/
public class QueueProduce {// linux 上部署的activemq 的 IP 地址 + activemq 的端口号public static final String ACTIVEMQ_URL = "tcp://8.130.105.216:61616";public static final String QUEUE_NAME = "test-queue";public static void main(String[] args) throws Exception{// 1 按照给定的url创建连接工程,这个构造器采用默认的用户名密码ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);// 2 通过连接工厂连接 connection 和 启动javax.jms.Connection connection = activeMQConnectionFactory.createConnection();// 启动connection.start();// 3 创建回话 session// 两个参数,第一个事务, 第二个签收Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 4 创建目的地 (两种 : 队列/主题 这里用队列)Queue queue = session.createQueue(QUEUE_NAME);// 5 创建消息的生产者MessageProducer messageProducer = session.createProducer(queue);// 6 通过messageProducer 生产 3 条 消息发送到消息队列中for (int i = 1; i < 7 ; i++) {// 7 创建字消息TextMessage textMessage = session.createTextMessage("msg--" + i);// 8 通过messageProducer发布消息messageProducer.send(textMessage);}// 9 关闭资源messageProducer.close();session.close();connection.close();System.out.println(" **** 消息发送到MQ完成 ****");}
}
package com.db.privategoodsplatform.test;import org.apache.activemq.ActiveMQConnectionFactory;import javax.jms.*;
import java.io.IOException;/*** @author xuqinglei* @date 2023/03/14 10:56**/
public class QueueConsumer {// linux 上部署的activemq 的 IP 地址 + activemq 的端口号public static final String ACTIVEMQ_URL = "tcp://8.130.105.216:61616";public static final String QUEUE_NAME = "test-queue";public static void main(String[] args) throws JMSException, IOException {// 1 按照给定的url创建连接工程,这个构造器采用默认的用户名密码ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);// 2 通过连接工厂连接 connection 和 启动Connection connection = activeMQConnectionFactory.createConnection();// 启动connection.start();// 3 创建回话 session// 两个参数,第一个事务, 第二个签收Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 4 创建目的地 (两种 : 队列/主题 这里用队列)Queue queue = session.createQueue(QUEUE_NAME);MessageConsumer consumer = session.createConsumer(queue);// while(true){
// // 这里是 TextMessage 是因为消息发送者是 TextMessage , 接受处理的
// // 也应该是这个类型的消息
// TextMessage message = (TextMessage)consumer.receive(1);
// if (null != message){
// System.out.println("****消费者的消息:"+message.getText());
// }else {
// break;
// }
// }consumer.setMessageListener(message -> {if (null != message && message instanceof TextMessage) {TextMessage textMessage = (TextMessage) message;try {System.out.println("****消费者的消息:" + textMessage.getText());} catch (JMSException e) {e.printStackTrace();}}});System.in.read();consumer.close();session.close();connection.close();}}
package com.db.privategoodsplatform.test;import org.apache.activemq.ActiveMQConnectionFactory;import javax.jms.*;/*** @author xuqinglei* @date 2023/03/22 14:56**/
public class TopicProduce {// linux 上部署的activemq 的 IP 地址 + activemq 的端口号public static final String ACTIVEMQ_URL = "tcp://8.130.105.216:61616";public static final String TOPIC_NAME = "test-topic-22";public static void main(String[] args) throws JMSException {// 1 按照给定的url创建连接工程,这个构造器采用默认的用户名密码ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);// 2 通过连接工厂连接 connection 和 启动javax.jms.Connection connection = activeMQConnectionFactory.createConnection();// 启动connection.start();// 3 创建回话 session// 两个参数,第一个事务, 第二个签收Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 4 创建目的地 (两种 : 队列/主题 这里用主题 )Topic topic = session.createTopic(TOPIC_NAME);// 5 创建消息的生产者MessageProducer producer = session.createProducer(topic);// 6 通过messageProducer 生产 3 条 消息发送到消息队列中for (int i = 1; i < 8 ; i++) {// 7 创建字消息TextMessage textMessage = session.createTextMessage("msg--" + i);// 8 通过messageProducer发布消息producer.send(textMessage);}// 9 关闭资源producer.close();session.close();connection.close();System.out.println(" **** 消息发送到MQ完成 ****");}}
package com.db.privategoodsplatform.test;import org.apache.activemq.ActiveMQConnectionFactory;import javax.jms.*;
import java.io.IOException;/*** @author xuqinglei* @date 2023/03/22 15:08**/
public class TopicConsumer {// linux 上部署的activemq 的 IP 地址 + activemq 的端口号public static final String ACTIVEMQ_URL = "tcp://8.130.105.216:61616";public static final String TOPIC_NAME = "test-topic-22";public static void main(String[] args) throws JMSException, IOException {// 1 按照给定的url创建连接工程,这个构造器采用默认的用户名密码ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);// 2 通过连接工厂连接 connection 和 启动javax.jms.Connection connection = activeMQConnectionFactory.createConnection();// 启动connection.start();// 3 创建回话 session// 两个参数,第一个事务, 第二个签收Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 4 创建目的地 (两种 : 队列/主题 这里用主题 )Topic topic = session.createTopic(TOPIC_NAME);MessageConsumer consumer = session.createConsumer(topic);consumer.setMessageListener(message -> {if (null != message && message instanceof TextMessage) {TextMessage textMessage = (TextMessage) message;try {System.out.println("****消费者的消息:" + textMessage.getText());} catch (JMSException e) {e.printStackTrace();}}});System.in.read();consumer.close();session.close();connection.close();}
}
这里的一点经验: activemq 好像自带负载均衡,当先启动两个队列(Queue)的消费者时,在启动生产者发出消息,此时的消息平均的被两个消费者消费。 并且消费者不会消费已经被消费的消息(即为已经出队的消息)
但是当有多个主题(Topic)订阅者时,发布者发布的消息,每个订阅者都会接收所有的消息。topic 更像是被广播的消息,但是缺点是不能接受已经发送过的消息。
1.JAVAEE 是一套使用Java 进行企业级开发的13 个核心规范工业标准 , 包括:
JDBC 数据库连接
JNDI Java的命名和目录接口
EJB Enterprise java bean
RMI 远程方法调用 一般使用TCP/IP 协议
Java IDL 接口定义语言
JSP
Servlet
XML
JMS Java 消息服务
JTA
JTS
JavaMail
JAF
JMS 可靠性:Persistent 持久性 、 事务 、 Acknowledge 签收
// 在队列为目的地的时候持久化消息
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);// 队列为目的地的非持久化消息
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
持久化的消息,服务器宕机后消息依旧存在,只是没有入队,当服务器再次启动,消息任就会被消费。
但是非持久化的消息,服务器宕机后消息永远丢失。 而当你没有注明是否是持久化还是非持久化时,
默认是持久化的消息。
对于目的地为主题(topic)来说,默认就是非持久化的,
让主题的订阅支持化的意义在于:对于订阅了公众号的人来说,
当用户手机关机,在开机后任就可以接受到关注公众号之前发送的消息。
代码实现:持久化topic 的消费者
代码实现:持久化topic 的消费者…… // 前面代码相同,不复制了 …… // 前面代码相同,不复制了 Topic topic = session.createTopic(TOPIC_NAME);TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic,"remark...");// 5 发布订阅connection.start();Message message = topicSubscriber.receive();// 一直等while (null != message){TextMessage textMessage = (TextMessage)message;System.out.println(" 收到的持久化 topic :"+textMessage.getText());message = topicSubscriber.receive(3000L); // 等1秒后meesage 为空,跳出循环,控制台关闭}……
持久化生产者…… MessageProducer messageProducer = session.createProducer(topic);// 6 通过messageProducer 生产 3 条 消息发送到消息队列中// 设置持久化topic 在启动messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT); connection.start();for (int i = 1; i < 4 ; i++) {// 7 创建字消息TextMessage textMessage = session.createTextMessage("topic_name--" + i);// 8 通过messageProducer发布消息messageProducer.send(textMessage);MapMessage mapMessage = session.createMapMessage();// mapMessage.setString("k1","v1");// messageProducer.send(mapMessage);}// 9 关闭资源……
createSession的第一个参数为true 为开启事务,开启事务之后必须在将消息提交,才可以在队列中看到消息
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
提交:
session.commit();
事务开启的意义在于,如果对于多条必须同批次传输的消息,可以使用事务,如果一条传输失败,可以将事务回滚,再次传输,保证数据的完整性。对于消息消费者来说,开启事务的话,可以避免消息被多次消费,以及后台和服务器数据的不一致性。举个栗子:
如果消息消费的 createSession 设置为 ture ,但是没有 commit ,
此时就会造成非常严重的后果,那就是在后台看来消息已经被消费,
但是对于服务器来说并没有接收到消息被消费,此时就有可能被多次消费。
非事务 :
Session.AUTO_ACKNOWLEDGE 自动签收,默认Session.CLIENT_ACKNOWLEDGE 手动签收
手动签收需要acknowledge
textMessage.acknowledge();
而对于开启事务时,设置手动签收和自动签收没有多大的意义,都默认自动签收,也就是说事务的优先级更高一些。
Session session = connection.createSession(true,Session.AUTO_ACKNOWLEDGE);
//Session session = connection.createSession(true,Session.CLIENT_ACKNOWLEDGE); // 也是自动签收 ……session.commit();
但是开启事务没有commit 任就会重复消费
小知识: broker
broker 就是实现了用代码形式启动 ActiveMQ 将 MQ 内嵌到 Java 代码中,可以随时启动,节省资源,提高了可靠性。
就是将 MQ 服务器作为了 Java 对象 使用多个配置文件启动 activemq cp activemq.xml activemq02.xml // 以active02 启动mq 服务器
./activemq start xbean:file:/myactivemq/apache-activemq-5.15.9/conf/activemq02.xml 把小型 activemq 服务器嵌入到 java 代码: 不在使用linux 的服务器需要的包:
com.fasterxml.jackson.core jackson-databind 2.9.5
启动代码
public class Embebroker {public static void main(String[] args) throws Exception {// broker 服务BrokerService brokerService = new BrokerService();// 把小型 activemq 服务器嵌入到 java 代码brokerService.setUseJmx(true);// 原本的是 192.…… 是linux 上的服务器,而这里是本地windows 的小型mq 服务器brokerService.addConnector("tcp://localhost:61616");brokerService.start();}
}
上一篇:代码随想录二刷——动规篇章
下一篇:移除链表元素