ActiveMQ
1、ActiveMQ在Linux下安装
1.1、上传到/opt目录下
1.2、解压缩 tar -zxvf 包名
1.3、在根目录下创建文件夹 mkdir /myactiveMQ
1.4、cp -r apache-activemq-5.15.9 /myactiveMQ/
1.5、启动activemq ./activemq start
1.6、查看activemq是否启动,activemq的默认进程端口是61616 ps -ef|grep activemq -v grep
1.7、带日志的启动:./activemq start > /myactiveMQ/run_cativemq.log
1.8、关闭activemq ./activemq stop
2、ActiveMQ控制台
控制台访问:http://Linux的IP地址:8161/admin/
默认的用户名和密码都是admin、admin
3、ActiveMQ通讯
3.1、导入依赖
<dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.10.1</version> </dependency> <dependency> <groupId>org.apache.xbean</groupId> <artifactId>xbean-spring</artifactId> <version>4.18</version> </dependency>
3.3、队列queue
在点对点的消息传递域中,目的地被称为队列。
消息生产者
public class JmsProduce{ public static final String ACTIVEMQ_URL = "tcp://192.168.111.136:61616"; public static final QUEUE_NAME = "queue01" public static void main(String[] args)throws JMSException{ //1、创建连接工厂,采用默认用户名和密码 ActiveMQConnectionFactory am = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2、通过连接工厂,获得连接connection Connection conn = am.createConnection(); conn.start(); //3.创建回话session,第一个叫事务,第二个叫签收 Session session = conn.createSisson(false,Session.AUTO_QCKNOWLEDG); //4 创建目的地 Queue queue = session.createQueue(QUEUE_NAME); //5 创建消息的生产者 MessageProducer mess = session.createProducer(queue); for(int i = 0; i<=3;i++){ //7创建消息 TextMessage textMessage = session.createTextMessage("msg---"+i);//理解为一个字符串 //8 通过messageproducer发送msg mess.send(textMessage); } //9 关闭资源 mess.close(); session.close(); connection.close(); System.out.println("发送成功"); } }
消息消费者
public class JmsConsumer{ public static final String ACTIVEMQ_URL = "tcp://192.168.111.136:61616"; public static final QUEUE_NAME = "queue01" public static void main(String[] args)throws JMSException{ //1、创建连接工厂,采用默认用户名和密码 ActiveMQConnectionFactory am = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2、通过连接工厂,获得连接connection Connection conn = am.createConnection(); conn.start(); //3.创建回话session,第一个叫事务,第二个叫签收 Session session = conn.createSisson(false,Session.AUTO_QCKNOWLEDG); //4 创建目的地 Queue queue = session.createQueue(QUEUE_NAME); //5 创建消费者 MessageProducer mess = session.createConsumer(queue); /** 同步阻塞方式 订阅者或接受者调用messageconsumer的receive()方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞。 while(true){ TextMessage message =(TextMessage) mess.receive(); if(message!=null){ System.out.println("消费者接收到信息"+message.getText()) }else{ break; } } mess.close(); session.close(); connection.close(); */ } }
3.5、消费者消费情况
-
先生产,然后启动一号消费者,1号消费者消费了消息,在启动二号消费者,二号消费者消费不到消息。
-
先启动2个消费者,再生产6个消息,这样的情况下,消费情况是平均分配(轮询)。
3.6、主题Topic(一对多)
生产者将消息发布到topic中,每个消息可以有多个消费者,属于1:N关系
生产者和消费者之间有时间上的相关性,订阅某一个主题的消费者只能消费自它订阅之后发布的消息
生产者生产时,topic不保存消息他是无状态的不落地,加入无人订阅就去生产,那就一条废消息,所以,一般先启动消费者在启动生产者。
消息生产者:
public class JmsProduce{ public static final String ACTIVEMQ_URL = "tcp://192.168.111.136:61616"; public static final TOPIC_NAME = "topic01" public static void main(String[] args)throws JMSException{ //1、创建连接工厂,采用默认用户名和密码 ActiveMQConnectionFactory am = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2、通过连接工厂,获得连接connection Connection conn = am.createConnection(); conn.start(); //3.创建回话session,第一个叫事务,第二个叫签收 Session session = conn.createSisson(false,Session.AUTO_QCKNOWLEDG); //4 创建目的地 Topic topic = session.createTopic(TOPIC_NAME); //5 创建消息的生产者 MessageProducer mess = session.createProducer(topic); for(int i = 0; i<=3;i++){ //7创建消息 TextMessage textMessage = session.createTextMessage("msg---"+i);//理解为一个字符串 //8 通过messageproducer发送msg mess.send(textMessage); } //9 关闭资源 mess.close(); session.close(); connection.close(); System.out.println("发送成功"); } }
消息消费者
public class JmsConsumer{ public static final String ACTIVEMQ_URL = "tcp://192.168.111.136:61616"; public static final TOPIC_NAME = "topic01" public static void main(String[] args)throws JMSException{ //1、创建连接工厂,采用默认用户名和密码 ActiveMQConnectionFactory am = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2、通过连接工厂,获得连接connection Connection conn = am.createConnection(); conn.start(); //3.创建回话session,第一个叫事务,第二个叫签收 Session session = conn.createSisson(false,Session.AUTO_QCKNOWLEDG); //4 创建目的地 Topic topic = session.createTopic(TOPIC_NAME); //5 创建消费者 MessageConsumer mess = session.createConsumer(topic); //通过监听的方式来消费消息 messConsumer.setMessageLister(( 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(); mess.close(); session.close(); connection.close(); } }
4、JMS
4.1、JMS是什么
两个应用程序之间的异步通信API。
4.2、JMS的组成结构
JMS provider:实现JMS接口个规范的消息中间件,也就是我们的MQ服务器
JMS producer:消息生产者,创建个发送JMS消息的客户端应用
JMS consumer:消息消费者,接受和处理JMS消息的客户端应用
JMS message:
-
消息头:
. JMSDestination 消息发送的⽬的地(队列或主题);创建消息时可以设置JMSDestination,但是在发送完成时其值会更新为发送⽅所指定的 JMSDestination,也就是说发送前该字段会被忽略;当消息被消费时,该字段的值与在它被发送时(send⽅法)被设置的值是相同的
JMSDeliveryMode:持久模式和非持久模式,默认是持久的
一条持久性的消息,:应该被传送”一次仅仅一次“,这就意味着如果JMS提供者出现故障,该消息并不会丢失,他会在服务器恢复之后再次传递。
一条非持久的消息:最多会传送一次,这就意味着服务器出现故障,该消息将永远丢失。
JMSExpriation:消息的过期时间,默认的
永不过期
JMSPriority:消息的优先级,从0-9级别,0-4是普通消息,5-9是加急消息,
默认是4级
JMSMessageID:唯一识别每个消息的编号
-
消息体:
封装具体的消息数据
5种消息体格式:
TextMessage:普通的字符串消息,包含⼀个String
MapMessage:Map类型的消息,Key为String类型,值为java基本类型
BytesMessage:二进制数组消息,包含一个byte[]
StreamMessage:java数据流消息,用标准流操作来顺序的填充和读取
ObjectMessage:对象消息,包含一个可序列化的Java对象
发送和接受的消息体类型必须一致对应
-
消息属性:
如果需要除消息头字段以外的值,那么可以使用消息属性。
识别/去重/重点标注等操作非常有用的方法。
消息属性是什么?
如果需要去除消息头字段以外的值,那么可以使用消息属性,消息属性是以属性名和属性值对的形式制定的,可以将属性视为消息头的扩展,属性指定一些消息头没有包括的附加信息,比如可以在属性里指定消息选择器。消息的树形就像可以分配给一条消息的附加消息头一样。它们允许开发者添加有关消息的不透明附加消息。
TextMessage textMessage = session.createTextMessage("msg---"+i);
textMessage.setStringProperty("co1","vip");
消费者
textMessage.getStringProperty("co1");
4.3、JMS可靠性
4.3.1、Persistent:持久性
-
参数说明:
-
非持久:
-
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT)
-
非持久化:当服务器宕机,消息不存在
-
-
持久:
-
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT)
-
持久化:当服务器宕机,消息依然存在
-
-
-
持久的Queue:
队列的默认是持久化的
,此模式保证这些消息只被传送一次和成功使用一次
,对于这些消息,可靠性
是优先考虑的因素。可靠性的另一个方面是确保持久性消息传送至目标后,消息服务再向消费者传送他们之前不会丢失这些消息。
-
持久的Topic:
-
一定要先运行一次消费者,等于向MQ注册,类似我订阅这个主题
-
然后再运行生产者发送信息,此时,无论消费者是否在线,都会接收到,不在线的话,下次连接的时候,会把没有收到的消息都接收下来。
-
4.3.2、transaction:事务
//3.创建回话session,第一个叫事务,第二个叫签收
Session session = conn.createSisson(true/false,Session.AUTO_QCKNOWLEDG);
生产者在提交事务的时候有两个选项,分别是true和false
false:
-
只要执行send,就进入到队列中
-
关闭事务,那第2个签收参数的设置需要有效
true:
-
先执行send在执行commit,消息才被真正的提交到队列中
需要在关闭资源的时候编写:session.commit();
-
消息需要批量,需要缓冲区处理
4.3.3、Acknowledge:签收
Session session = conn.createSisson(false,Session.CLIENT_QCKNOWLEDG);
-
Session session = conn.createSisson(true,Session.CLIENT_QCKNOWLEDG); while(true){ TextMessage textMessage = (TextMessage)messageConsumer.receive(4000L); if(textMessage != null){ System.out.println("消费者接收到消息"+textMessage.getText()); textMessage.acknowledge; }else{ break; } } session.commit();
-
在事务性回话中,当一个事务被成功提交则消息自动签收,如果事务回滚,则消息会被再次传送
-
5、ActiveMQ的Broker
相当于一个ActiveMQ服务器实例。
实现了代码的形式启动ActiveMQ将MQ嵌入到Java中,以便随时用随时启动,在用的时候再去启动这样能节省资源,也保证了可靠性。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
EmbedBroker
public class EmbedBroker { public static void main(String[] args)throws Exception { //ActiveMQ也支持在vm中通信基于嵌入式的Broker BrokerService brokerService = new BrokerService(); brokerService.setUseJmx(true); brokerService.addConnector("tcp://localhost:61616"); brokerService.start(); } }
6、SpringBoot整合ActiveMQ
6.1、队列
6.1.1、生产者
-
新建工程
-
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> <version>2.6.7</version> </dependency>
application.yml
server.port:7777 spring.activemq.broker-url=tcp://193.168.111.136:61616 #MQ服务器地址 spring.activemq.user=admin spring.activemq.password=admin spring.jms.pub-sub-domain=false #false = queue true = topic 不写默认就是false #自己定义队列名称 myqueue=boot-activemq-queue
配置Bean
@Component @EnableJms public class ConfigBean { @Value("${myQueue}") private String Queue; @Bean public javax.jms.Queue queue() { return new ActiveMQQueue(Queue); } }
Queue_Producer
@Component public class Queue_Producer { @Autowired private JmsMessagingTemplate jmsMessagingTemplate; @Autowired private Queue queue; public void produceMsg(){ jmsMessagingTemplate.convertAndSend(queue,"******:"+ UUID.randomUUID().toString().substring(0,6)); } }
主启动类MainApp_Produce
测试单元
@SpringBootTest("classes = MainApp_Produce.class") @Runwith(SpringJunit4ClassRunner.class) @WebAppConfiguration public class TestActiveMQ { @Resource private Queue_Producer queue_producer; public void testSend()throws Exception{ queue_producer.produceMsg(); } }
每3秒往MQ推送消息,以下定时发送case:
定时投递:
@Component public class Queue_Producer { @Autowired private JmsMessagingTemplate jmsMessagingTemplate; @Autowired private Queue queue; public void produceMsg(){ jmsMessagingTemplate.convertAndSend(queue,"******:"+ UUID.randomUUID().toString().substring(0,6)); } //间隔3秒钟定投 @Scheduled(fixedDelay = 3000) public void produceMsgScheduled(){ jmsMessagingTemplate.convertAndSend(queue,"定时投放******:"+ UUID.randomUUID().toString().substring(0,6)); } }
主启动类上需要添加@EnableScheduling注解
队列消费者
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> <version>2.6.7</version> </dependency>
application.yml
server.port:8888 spring.activemq.broker-url=tcp://193.168.111.136:61616 #MQ服务器地址 spring.activemq.user=admin spring.activemq.password=admin spring.jms.pub-sub-domain=false #false = queue true = topic 不写默认就是false #自己定义队列名称 myqueue=boot-activemq-queue
Queue_Consumer
@Component public class Queue_Consumer{ @JMSListener(destination = "${myqueue}") public void receive(TextMessage textMessage)throws JMSException{ System.out.pritln("消费者收到消息"+textMessage.getText); } }
6.2、发布订阅
6.2.1、Topci生产者
application.yml
server.port:6666 spring.activemq.broker-url=tcp://193.168.111.136:61616 #MQ服务器地址 spring.activemq.user=admin spring.activemq.password=admin spring.jms.pub-sub-domain=true #false = queue true = topic 不写默认就是false #自己定义队列名称 myTopic=boot-activemq-topic
配置Bean
Topic_Producer
主启动类
消费者
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)