linux在activemq.apache.org下载,选择保存文件。自己使用命令解压到自己想要的文件目录中。
在此之前需要确保自己的虚拟机中的确有java环境,因为activemq的底层为java,所以需要java环境支持。
activemq的默认进程端口为61616
命令行 | 效果 |
---|---|
ps -ef|grep activemq|grep -v grep | 查出来的就是结果 |
ps -ef|grep active | 会多带一行grep activemq的结果 |
netstat -anp|grep 61616 | 查询端口是否被使用 |
lsof -i:61616 | 查询端口号占用情况 |
./activemq stop/start/ | 启动或者关闭 |
./activemq start>/soft/activemq.log | 带运行日志地启动方式 |
在windows系统使用虚拟机地址:8161/admin/来访问图形化界面,账号密码为admin
依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.xjjgroupId>
<artifactId>activemq_001artifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.apache.activemqgroupId>
<artifactId>activemq-allartifactId>
<version>5.15.9version>
dependency>
<dependency>
<groupId>org.apache.xbeangroupId>
<artifactId>xbean-springartifactId>
<version>3.16version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
<scope>providedscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
project>
procedure端
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author 徐敬杰
*/
public class Produce {
public static final String ACTIVEMQ_URL="tcp://192.168.216.128:61616";
public static final String QUEUE_NAME="queue02";
public static void main(String[] args) throws JMSException {
/*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);
/*5、创建消息的生产者*/
MessageProducer producer = session.createProducer(queue);
/*6、通过使用消息生产者生产三条消息发送到mq队列里面*/
for(int i=1;i<=3;i++){
/*7、创建消息*/
TextMessage textMessage = session.createTextMessage("msg02--" + i);
/*8、生产者发送消息*/
producer.send(textMessage);
}
/*9、关闭连接*/
producer.close();
session.close();
connection.close();
System.out.println("消息发布到mq上了,已经完成");
}
}
consume端
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author 徐敬杰
*/
public class Consumer {
public static final String ACTIVEMQ_URL="tcp://192.168.216.128:61616";
public static final String QUEUE_NAME="queue02";
public static void main(String[] args) throws JMSException {
//1、创建工厂
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//2、获取连接
Connection connection=activeMQConnectionFactory.createConnection();
connection.start();
//3、创建session
Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4、创建queue
Queue queue=session.createQueue(QUEUE_NAME);
//5、创建接收者
MessageConsumer consumer = session.createConsumer(queue);
//6、接收消息
while(true){
//这种方法会一直等候,一旦有新消息就会直接取出来
//TextMessage textMessage = (TextMessage)consumer.receive();
//这种方法当取完了队列中的信息以后会等待4秒,如果时间到了还没有信息就退出程序
TextMessage textMessage=(TextMessage)consumer.receive(4000L);
if(textMessage!=null) {
System.out.println(textMessage.getText());
} else{
break;
}
}
consumer.close();
session.close();
connection.close();
}
}
consumer端使用MessageListener
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
/**
* @author 徐敬杰
*/
public class Consumer {
public static final String ACTIVEMQ_URL="tcp://192.168.216.128:61616";
public static final String QUEUE_NAME="queue02";
public static void main(String[] args) throws JMSException, IOException, InterruptedException {
//1、创建工厂
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//2、获取连接
Connection connection=activeMQConnectionFactory.createConnection();
connection.start();
//3、创建session
Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4、创建queue
Queue queue=session.createQueue(QUEUE_NAME);
//5、创建接收者
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
//如果信息不为空,同时也可以强制转型为TextMessage
if(message!=null&&message instanceof TextMessage){
TextMessage textMessage=(TextMessage)message;
try {
System.out.println("接收到了消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
//如果使用messageListener的话,就必须暂缓程序,不能直接走完,否则接收不到信息
//不会自动停止,会无限期等待新的信息到来,类似于不带参数的写法
//System.in.read();
//会自动停止,但只停留4s,类似于带参数的写法
Thread.sleep(4000L);
consumer.close();
session.close();
connection.close();
}
}
消费者三大消费情况
情况 | 结果 |
---|---|
先生产,只启动一号消费者 | 1号可以消费到,2号不行 |
先生产,先启动一号消费者,再启动二号消费者 | 1号可以消费到,2号不行 |
先启动两个消费者,再生产6条消息 | 根据谁先启动进入的消费者队列判定,然后大家一条一条轮流交替。 |
生产者端
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author 徐敬杰
*/
public class ProducerTopic {
public static final String ACTIVEMQ_URL="tcp://192.168.216.128:61616";
public static final String TOPIC_NAME="topic01";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection=activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic=session.createTopic(TOPIC_NAME);
MessageProducer producer = session.createProducer(topic);
for(int i=1;i<=3;i++){
TextMessage message =(TextMessage) session.createTextMessage("topic:" + i);
producer.send(message);
}
producer.close();
session.close();
connection.close();
System.out.println("消息发布到topic成功");
}
}
消费者端
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author 徐敬杰
*/
public class ConsumerTopic {
public static final String ACTIVEMQ_URL="tcp://192.168.216.128:61616";
public static final String TOPIC_NAME="topic01";
public static void main(String[] args) throws JMSException {
System.out.println("启动了一号消费者");
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection=activeMQConnectionFactory.createConnection();
connection.start();
Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic=session.createTopic(TOPIC_NAME);
MessageConsumer consumer = session.createConsumer(topic);
while(true){
//这种方法会一直等候,一旦有新消息就会直接取出来
TextMessage textMessage = (TextMessage)consumer.receive();
//这种方法当取完了队列中的信息以后会等待4秒,如果时间到了还没有信息就退出程序
//TextMessage textMessage=(TextMessage)consumer.receive(4000L);
if(textMessage!=null){
System.out.println("消息接收成功:"+textMessage.getText());
} else {
break;
}
}
consumer.close();
session.close();
connection.close();
}
}
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
/**
* @author 徐敬杰
*/
public class ConsumerTopic {
public static final String ACTIVEMQ_URL="tcp://192.168.216.128:61616";
public static final String TOPIC_NAME="topic01";
public static void main(String[] args) throws JMSException, IOException {
System.out.println("启动了一号消费者");
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection=activeMQConnectionFactory.createConnection();
connection.start();
Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic=session.createTopic(TOPIC_NAME);
MessageConsumer consumer = session.createConsumer(topic);
//使用高级特性来写
consumer.setMessageListener((message) -> {
if(message!=null&message instanceof TextMessage){
try {
System.out.println("接收到了消息:"+((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
//如果使用messageListener的话,就必须暂缓程序,不能直接走完,否则接收不到信息
//不会自动停止,会无限期等待新的信息到来,类似于不带参数的写法
//System.in.read();
//会自动停止,但只停留4s,类似于带参数的写法
Thread.sleep(4000L);
consumer.close();
session.close();
connection.close();
}
}
比较项目 | topic | queue |
---|---|---|
工作模式 | 订阅发布模式,如果当前没有订阅者,消息将会被丢弃,如果有多个订阅者,那么这些订阅者都会收到消息 | 负载均衡模式,如果当前没有消费者,消息也不会丢弃;如果有多个消费者,那么一条消息也只会发送给其中一个消费者,并且要求消费者ack信息 |
有无状态 | 无状态 | Queue数据默认会在mq服务器上以文件的形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面。也可以配置成DB存储 |
传递完整性 | 如果没有订阅者,消息会被丢弃 | 消息不会丢弃 |
处理效率 | 由于消息要按照订阅者的数量进行复制,所以处理性能会随着订阅者的增加而明显降低,还要结合不同消息协议自身的性能差异 | 由于一条消息只发送给一个消费者,所以就算消费者再多,性能也不会有明显降低,当然不同消息协议的具体性能也是有差异的。 |
TextMessage:普通字符串消息,包含一个String
MapMessage:一个Map类型的消息,key为String类型,而值为Java的基本类型
//发送部分
MapMessage mapMessage=session.createMapMessage();
//要放入什么类型的value就调用什么类型的方法
mapMessage.setString("k1","v1");
producer.send(mapMessage);
//接收部分
if(message!=null&&message instanceof MapMessage){
MapMessage mapMessage=(MapMessage)message;
try {
//接收参数为同理
System.out.println("接收到了map:"+mapMessage.getString("k1"));
} catch (JMSException e) {
e.printStackTrace();
}
}
BytesMessage:二进制数组消息,包含一个byte[]
StreamMessage:Java数据流消息,用标准流操作来顺序的填充呵读取。
ObjectMessage:对象消息,包含一个可序列化的Java对象。
如果需要去除消息头字段以外的值,那么可以使用消息属性
识别/去重/重点标注等操作非常有用的方法
相当于消息头的拓展,所有的消息格式都可以发送消息属性
//发送消息属性
for(int i=1;i<=3;i++){
TextMessage textMessage = session.createTextMessage("msg02--" + i);
//添加消息属性,可以是别的基本类型
textMessage.setStringProperty("tezheng","tezheng");
producer.send(textMessage);
}
//接收消息属性
if(message!=null&&message instanceof TextMessage){
TextMessage textMessage=(TextMessage)message;
try {
System.out.println("接收到了消息:"+textMessage.getText()+"特征为:"+textMessage.getStringProperty("tezheng"));
} catch (JMSException e) {
e.printStackTrace();
}
}
队列持久化
//队列部分,默认策略为执行持久化
//取消持久化。直接将信息删除。
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//执行持久化。没有被消费过的消息,当activemq被关掉以后也还会存在于虚拟机中。
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
主题持久化
由于普通的主题进行持久化是没有效果的,因为普通consumer的就是当我程序在启动的状态下,会去接收procedure发送来的消息。所以将consumer改变为subscribe。
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import javax.xml.soap.Text;
/**
* @author 徐敬杰
*/
public class TopicProcedurePersistent {
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory("tcp://192.168.216.128:61616");
Connection connection=activeMQConnectionFactory.createConnection();
Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic=session.createTopic("topic01");
MessageProducer messageProducer = session.createProducer(topic);
//整体流程大致一样,只是将connection.start()往后了几步
connection.start();
for(int i=1;i<=3;i++){
TextMessage message = (TextMessage) session.createTextMessage("topic" + i);
messageProducer.send(message);
}
messageProducer.close();
session.close();
connection.close();
System.out.println("消息发布成功");
}
}
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author 徐敬杰
*/
public class TopicConsumerPersistent {
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory("tcp://192.168.216.128:61616");
Connection connection = activeMQConnectionFactory.createConnection();
//设置我的id,这样子就算我下线了,也会保存记录,方便我下次上线的时候将我订阅的信息发送给我
connection.setClientID("张三");
Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic=session.createTopic("topic01");
//不能使用普通的consumer,需要改变为subscribe
TopicSubscriber topicSubscriber=session.createDurableSubscriber(topic,"备注......");
connection.start();
TextMessage textMessage=(TextMessage) topicSubscriber.receive();
while(textMessage!=null){
System.out.println("接收到了消息:"+textMessage.getText());
textMessage=(TextMessage) topicSubscriber.receive();
}
session.close();
connection.close();
}
}
事务
生产者:
false:只要执行send就会进入到队列。关闭事务,那第二个签收参数的设置需要为有效。
true:先执行send再执行commit,消息才会被真正的提交到队列中。消息需要批量发送,需要缓冲区处理。既能commit,也能rollback。
消费者:
false:只要读取了,就会自动将消息给移出队列。
true:必须要执行commit,否则读取以后也不会将消息移出队列。
签收
消费者:
AUTO_ACKNOWLEDGE:自动签收,当读取了信息以后,自动将信息从队列中取出。
CLIENT_ACKNOWLEDGE:手动签收,当读取完信息以后,需要调用消息.acknowledge()方法才能将消息从队列中出队。
签收和事物的关系:事务大于签收
在事务性会话中,当一个事务被成功提交则消息被自动签收。如果事务回滚,则消息被再次传送。
非事务性会话中,消息何时被确认取决于创建会话时的应答模式。
点对点模型是基于队列的,生产者发消息到队列,消费者从队列接收消息,队列的存在使得消息的异步传输成为可能。者和我们平时给朋友发送短信类似。
如果在Session关闭时有部分消息已被收到但还没有被签收,那当消费者下次连接到相同的队列时,这些消息还会被再次接收。
队列可以长久的保存消息直到消费者收到消息。消费者不需要因为担心而时刻和队列保持激活的连接状态,充分体现了异步传输模式的优势。
JMS Pub/Sub 模型定义了如何向一个内容节点发布和订阅消息,这些节点被称作为topic。
主题可以被认为是消息的传输中介,发布者发布消息到主题。订阅者从主题订阅消息。
主题使得消息订阅者和消息发布者保持相互独立,不需要接触即可保证消息的传送。
非持久订阅:非持久订阅只有当客户端处于激活状态,也就是和MQ保持连接状态才能收到发送到某个主题的消息。如果消费者处于离线模式,生产者发送的主题消息将会丢失作废,消费者永远不会收到。
持久订阅:客户端首先向MQ注册一个自己的身份ID识别号,当这个客户端处于离线状态时,生产者会为这个ID保存所有发送到主题的消息,当客户再次连接到MQ时,会根据客户的ID得到所有当自己处于离线时生产者发送到主题的消息。
非持久订阅状态下,不能恢复或重新派送一个未签收的消息。
持久订阅才能恢复或重新派送一个未签收的消息。
是什么:相当于一个ActiveMQ的服务器实例。Broker其实就是实现了用代码的形式启动ActiveMQ将MQ嵌入到Java代码中,以便随时用随时启动,在用的时候再去启动这样能节省资源,也保证可靠性。
在activemq的conf目录下有配置文件
可以通过以下的命令来启动
./activemq start xbean:file:/soft/activemq/apache-activemq-5.15.10/conf/activemq02.xml
java自启动一个模拟的activeMQ
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.5version>
dependency>
import org.apache.activemq.broker.BrokerService;
/**
* @author 徐敬杰
*/
public class Broker {
public static void main(String[] args) throws Exception {
BrokerService brokerService=new BrokerService();
brokerService.setUseJmx(true);
brokerService.addConnector("tcp://localhost:61616");
brokerService.start();
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.xjjgroupId>
<artifactId>SpringActiveMQartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.apache.activemqgroupId>
<artifactId>activemq-allartifactId>
<version>5.15.9version>
dependency>
<dependency>
<groupId>org.apache.xbeangroupId>
<artifactId>xbean-springartifactId>
<version>3.16version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.5version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jmsartifactId>
<version>4.3.23.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.activemqgroupId>
<artifactId>activemq-poolartifactId>
<version>5.15.9version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.0.8.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.8.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>5.0.8.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>5.0.8.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.9.2version>
dependency>
<dependency>
<groupId>aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>2.1_2version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
<scope>providedscope>
dependency>
dependencies>
project>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">
<context:component-scan base-package="com.xjj.activemq"/>
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.216.128:61616"/>
bean>
property>
<property name="maxConnections" value="100"/>
bean>
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="spring-active-queue"/>
bean>
<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="spring-active-topic"/>
bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="defaultDestination" ref="destinationTopic"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter">bean>
property>
bean>
beans>
package com.xjj.activemq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
/**
* @author 徐敬杰
*/
@Component
public class SpringActiveMQ_Producer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args){
ApplicationContext ctx=new ClassPathXmlApplicationContext("application.xml");
SpringActiveMQ_Producer springActiveMQ_producer=(SpringActiveMQ_Producer)ctx.getBean("springActiveMQ_Producer");
springActiveMQ_producer.jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage=session.createTextMessage("spring和activemq的整合");
return textMessage;
}
});
System.out.println("添加成功");
}
}
package com.xjj.activemq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
/**
* @author 徐敬杰
*/
@Component
public class SpringActiveMQ_Consumer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
SpringActiveMQ_Consumer springActiveMQ_consumer=(SpringActiveMQ_Consumer)applicationContext.getBean("springActiveMQ_Consumer");
String value =(String) springActiveMQ_consumer.jmsTemplate.receiveAndConvert();
System.out.println("收到的消息为:"+value);
}
}
实现消费者无需启动,自动监听队列或者主题
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">
<context:component-scan base-package="com.xjj.activemq"/>
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.216.128:61616"/>
bean>
property>
<property name="maxConnections" value="100"/>
bean>
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="spring-active-queue"/>
bean>
<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="spring-active-topic"/>
bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="defaultDestination" ref="destinationQueue"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter">bean>
property>
bean>
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="destination" ref="destinationQueue"/>
<property name="messageListener" ref="activeMQMessageListener"/>
bean>
beans>
package com.xjj.activemq;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
* @author 徐敬杰
*/
@Component
public class ActiveMQMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
if(message!=null && message instanceof TextMessage){
TextMessage textMessage=(TextMessage)message ;
try {
System.out.println("OK,获取的信息为:"+((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
server:
port: 7777
spring:
activemq:
broker-url: tcp://192.168.216.128:61616
user: admin
password: admin
jms:
pub-sub-domain: false #false为queue,true为topic
myqueue: SpringBoot-ActiveMQ-Queue
package com.xjj.springbootactivemq.config;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
/**
* @author 徐敬杰
*/
@Component
@EnableJms
public class Config {
@Value("${myqueue}")
private String myQueue;
@Bean
public Queue queue(){
return new ActiveMQQueue(myQueue);
}
}
package com.xjj.springbootactivemq.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import java.util.UUID;
/**
* @author 徐敬杰
*/
@Component
public class Procedure {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Queue queue;
//需要使用Junit等单元测试方法调用使用
public void produceMsg(){
jmsMessagingTemplate.convertAndSend(queue,"SpringBoot和ActiveMQ整合"+ UUID.randomUUID().toString().substring(0,6));
}
}
server:
port: 7777
spring:
activemq:
broker-url: tcp://192.168.216.128:61616
user: admin
password: admin
jms:
pub-sub-domain: false #false为queue,true为topic
myqueue: SpringBoot-ActiveMQ-Queue
package com.xjj.springbootactivemq.util;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.TextMessage;
@Component
public class Consumer {
//直接启动主启动类即可开启监听功能
@JmsListener(destination = "${myqueue}")
public void receive(TextMessage message) throws JMSException{
try {
System.out.println("接收到了信息:"+message.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
server:
port: 7777
spring:
activemq:
broker-url: tcp://192.168.216.128:61616
user: admin
password: admin
jms:
pub-sub-domain: true #false为queue,true为topic
mytopic: SpringBoot-ActiveMQ-Topic
package com.xjj.springbootactivemq.config;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import javax.jms.Topic;
/**
* @author 徐敬杰
*/
@Component
@EnableJms
public class Config {
@Value("${mytopic}")
private String myTopic;
@Bean
public Topic topic(){
return new ActiveMQTopic(myTopic);
}
}
package com.xjj.springbootactivemq.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Topic;
/**
* @author 徐敬杰
*/
@Component
public class Procedure {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Topic topic;
//需要使用junt等方法来调用测试
public void sendMsg(){
jmsMessagingTemplate.convertAndSend(topic,"SpringBoot和ActiveMQ的Topic的demo.....");
System.out.println("发送成功");
}
}
server:
port: 6666
spring:
activemq:
broker-url: tcp://192.168.216.128:61616
user: admin
password: admin
jms:
pub-sub-domain: true #false为queue,true为topic
mytopic: SpringBoot-ActiveMQ-Topic
package com.xjj.springbootactivemq.util;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.TextMessage;
@Component
public class Consumer {
//直接启动主启动类即可使用
@JmsListener(destination = "${mytopic}")
public void listen(TextMessage textMessage) throws JMSException {
if(textMessage!=null&&textMessage instanceof TextMessage){
System.out.println("收到的消息为:"+textMessage.getText());
}
}
}
ActiveMQ支持的client-broker通讯协议有:TCP、NIO、UDP、SSL、Http(s)、VM。
其中的配置Transport Connector在配置文件的标签中
NIO协议和TCP协议类似,但是NIO更加侧重于底层的访问操作。它允许开发人员对同一资源可有更多的client调用和服务端有更多的负载。
适合使用NIO的场景
NIO的连接形式:nio://hostname:port?key=value
修改为NIO连接有以下的步骤
一阶段为BIO+TCP也就是默认设置,二阶段为NIO+TCP也就是加上了NIO阶段,三阶段为NIO+TCP/Mqtt/stomp实现如下:然后java代码中destination中tcp或者nio可以随意写,而端口号则为61608
<transportConnector name="auto+nio" uri="auto+nio://0.0.0.0:61608?maximumConnections=1000&wireFormat.maxFrameSize=104857600&org.apache.activemq.transport.nio.SelectorManager.corePoolSize=20&org.apache.activemq.transport.nio.SelectorManager.maximumPoolSize=50"/>
前面的消息的可靠性中有三点:持久、事务、签收。但这都是ActiveMQ自带的,如果ActiveMQ死了,那么这些都无用。所以外接消息持久化功能,持久到数据库里面。
为了避免意外宕机以后丢失信息,需要做到重启以后可以恢复消息队列,消息系统一般都采用持久化机制。ActiveMQ的消息持久化机制有JDBC、AMQ、KahaDB和LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。
就是在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等再试图将信息发送给接收者,成功则将消息从存储中删除,失败则继续尝试发送。
消息中心启动以后首先要检查指定的存储位置,如果有未发送成功的消息,则需要把消息发送出去。
先在activemq/lib目录下新增一个mysql的连接驱动包
修改配置文件
<persistenceAdapter>
<jdbcPersistenceAdapter dataSource="#mysql-ds"/>
persistenceAdapter>
dataSource指定要将引用的持久化数据库的bean名称,createTableOnStartup是否在启动的时候创建数据表,默认值是true,这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为true之后改成false
配置MySQL连接池(粘贴在与之间)
<bean id="mysql-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.216.7:3306/activemq?relaxAutoCommit=true"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
<property name="poolPreparedStatements" value="true"/>
bean>
如果出现问题可以去data目录下的日志文件中查看
遇到了以下问题:windows下的数据库不允许远程访问,即拒绝了activemq的连接请求
通过修改本地mysql来解决问题
use mysql;
update user set host = ‘%’ where user = ‘root’;
flush privileges;
然后重启mysql和activemq即可
出现以下三张表
ACTIVEMQ_MSGS:消息表
列名 | 备注 |
---|---|
ID | 自增的数据库主键 |
CONTAINER | 消息的Destination |
MSGID_PROD | 消息发送者的主键 |
MSG_SEQ | 是发送消息的顺序,MSGID_PROD+MSG_SEQ可以组成JMS的MessageID |
EXPIRATION | 消息的过期时间,存储的是从1970-01-01到现在的毫秒数 |
MSG | 消息本体的Java序列化对象的二进制数据 |
PRIORITY | 优先级,从0-9,数字越大,优先级越高 |
ACTIVEMQ_ACKS:用户存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存。
列名 | 备注 |
---|---|
CONTAINER | 消息的Destination |
SUB_DEST | 如果是使用Static集群,这个字段会有集群其他系统的信息 |
CLIENT_ID | 每个订阅者都必须有一个唯一的客户端ID用以区分 |
SUB_NAME | 订阅者名称 |
SELECTOR | 选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作。 |
LAST_ACKED_ID | 记录消费过的消息的ID。 |
ACTIVEMQ_LOCK:表activemq_lock在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,其他的只能作为备份等待MasterBroker不可用,才可能成为下一个Maste Broker。这个表用于记录哪个Broker是当前的Master Broker。
在队列模型中:
当DeliveryMode设置为NON_PERSISTENCE时,消息被保存在内存中
当DeliveryMode设置为PERSISTENCE时,消息保存在broker的相应的文件或者数据库中。当数据被消费掉时,便会自动从broker中删除掉。改变的为msg表。
在主题队列中:
修改的为msg(传入的信息)和ack(订阅者的信息)表,同时信息被消费掉以后也不会被删除。
可能会遇到的问题:
这种方式克服了JDBC Store的不足,JDBC每次消息过来,都需要去写库和读库。
ActiveMQ Journal,使用了高速缓存写入技术,大大提高了性能。
当消费者的消费速度能够及时跟上生产者消息的生产速度时,journal文件能够大大减少需要的写入到DB中的消息。
当生产者生产了1000条信息,这1000条消息会被保存到journal中,如果消费者的消费速度很快的情况下,在journal文件还没有同步到DB之前,消费者已经消费了90%以上的消息,那么这个时候只需要同步剩余的10%的消息到DB。如果消费者的消费速度很慢,那么这个时候journal文件可以使消息以批量方式写道DB。
<persistenceFactory>
<journalPersistenceAdapterFactory
journalLogFiles="4"
journalLogFileSize="32768"
useJournal="true"
useQuickJournal="true"
dataSource="#mysql-ds"
dataDirectory="activemq-data"/>
persistenceFactory>
改的为msg(传入的信息)和ack(订阅者的信息)表,同时信息被消费掉以后也不会被删除。
可能会遇到的问题: