1、安装ActiveMQ
⑴ 下载apache-activemq-5.15.1-bin.tar.gz
⑵ 解压缩
⑶ 到bin目录下启动和关闭
启动命令:./activemq start
关闭命令:./activemq stop
2、管理ActiveMQ
默认端口为8161,用户名和密码都为admin
3、队列模式
⑴ 特点
① 一个消息只能被一个消费者所消费
② 消费者可以随时消费消息
⑵ API
① ConnectionFactory
连接工厂
实现类:ActiveMQConnectionFactory
public ActiveMQConnectionFactory(String brokerURL) {}
需要传入ActiveMQ服务器的连接地址,端口默认为61616
② Connection
连接,由连接工厂所创建。通过连接可以创建会话(Session)
Connection createConnection() throws JMSException;
void start() throws JMSException;
打开连接。很重要
void close() throws JMSException;
关闭连接
③ Session
会话,可以创建目的地(Destination)、生产者(MessageProducer)和消费者(MessageConsumer)
Session createSession(boolean var1, int var2) throws JMSException;
第一个参数为是否开启事务,第二个参数为
可以选择Session的常量:
int AUTO_ACKNOWLEDGE = 1;
int CLIENT_ACKNOWLEDGE = 2;
int DUPS_OK_ACKNOWLEDGE = 3;
int SESSION_TRANSACTED = 0;
MessageProducer createProducer(Destination var1) throws JMSException;
传入目的地
MessageConsumer createConsumer(Destination var1) throws JMSException;
传入目的地
④ Queue
队列,由Session创建
Queue createQueue(String var1) throws JMSException;
传入队列的名称
public interface Queue extends Destination {}
Destination就是目的地
⑤ TextMessage
文本类型的消息,由Session创建
TextMessage createTextMessage(String var1) throws JMSException;
需要传入消息的内容
⑥ send
void send(Message var1) throws JMSException;
发送消息,由MessageProducer调用
⑦ setMessageListener
void setMessageListener(MessageListener var1) throws JMSException;
传入一个消息监听器
public interface MessageListener {
void onMessage(Message var1);
}
可以将Message对象强转为具体的消息对象,再调用具体的方法获取消息内容
例如:
public interface TextMessage extends Message {
String getText() throws JMSException;
}
可以获得文本消息类型的消息内容
⑶ 示例
private static final String brokerURL = ???;
private static final String QUEUE_NAME = ???;
【生产者】
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerURL);
Connection connection = connectionFactory.createConnection();
connection.start(); // 启动连接
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(QUEUE_NAME);
MessageProducer producter = session.createProducer(destination);
for (int i = 1; i <= 100; i++) {
TextMessage textMessage = session.createTextMessage("queue-message-" + i);
producter.send(textMessage);
System.out.println("发送消息" + textMessage.getText());
}
producter.close();
connection.close();
【消费者】
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerURL);
Connection connection = connectionFactory.createConnection();
connection.start(); // 启动连接
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(QUEUE_NAME);
MessageConsumer consumer = session.createConsumer(destination);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
注意消费者和连接都不能关闭,否则无法异步获取消息
4、主题模式
⑴ 特点
① 所有的消息都能被订阅者所消费
② 订阅者只能消费它所订阅的消息,订阅前的消息并不能被其消费
⑵ API
Topic createTopic(String var1) throws JMSException;
创建主题,由Session创建,需要传入主题的名称
⑶ 示例
private static final String brokerURL = "";
private static final String TOPIC_NAME = "";
【发布者】
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerURL);
Connection connection = null;
try {
connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createTopic(TOPIC_NAME);
MessageProducer messageProducer = session.createProducer(destination);
for (int i = 1; i <= 100; i++) {
TextMessage message = session.createTextMessage("topic-message-" + i);
messageProducer.send(message);
System.out.println(message.getText());
}
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (null != connection) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
【订阅者】
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerURL);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createTopic(TOPIC_NAME);
MessageConsumer consumer = session.createConsumer(destination);
consumer.setMessageListener((message) -> {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("接收消息:" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
});
注意订阅者和连接都不能关闭,否则无法异步获取消息
5、整合Spring
⑴ pom依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>${activemq.version}</version>
<!-- 排除ActiveMQ自身的Spring-Context的依赖 -->
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
⑵ API
① SingleConnectionFactory
由Spring-Jms提供的整合消息中间件的连接工厂(连接池)。它有一个ConnectionFactory targetConnectionFactory的属性,用于指定目标连接工厂,可以引用ActiveMQConnectionFactory
② CachingConnectionFactory
继承自SingleConnectionFactory,它可以缓存会话(Session),当然也包括生产者和消费者
③ ActiveMQQueue
由ActiveMQ提供的队列。需要指定消息的name属性,可以通过set注入或者构造注入
④ ActiveMQTopic
由ActiveMQ提供的主题。需要指定消息的physicalName,可以通过set注入或者构造注入
⑤ JmsTemplate
用于发送和接收消息的模版类。需要指定connectionFactory属性,可以set注入或者构造注入
它每次发送消息时,都会重新创建连接、会话和生产者
同时它也是线程安全的,可以在整个应用中使用它
⑥ MessageListener
消息监听器。接口,消费者可以实现它,并实现
void onMessage(Message message);方法,通过将Message对象强转为相应的消息对象,并获取对应的消息
⑦ DefaultMessageListenerContainer
消息监听容器。需要指定connectionFactory,destination以及messageListener
⑶ 示例
由于生产者和消费者都需要配置连接池、连接工厂、消息,所以可以将这些提取出来,再通过import引用即可复用
【common.xml】
<!-- 启用注解 -->
<context:annotation-config />
<!-- 引入配置文件 -->
<context:property-placeholder location="classpath:activemq.properties" />
<!-- ActiveMQ -->
<bean id="activeMQConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${activemq.brokerUrl}" />
</bean>
<!-- 连接池 -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="activeMQConnectionFactory" />
</bean>
<!-- Queue队列 -->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="${activemq.queueName}" />
</bean>
<!-- Topic主题 -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<property name="physicalName" value="${activemq.topicName}" />
</bean>
【producer.xml】
<import resource="classpath:common.xml" />
<!-- JmsTemplate -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
</bean>
<!-- 生产者实现类 -->
<bean class="???" />
【consumer.xml】
<import resource="classpath:common.xml" />
<!-- 消息监听器实现类 -->
<bean id="messageListener" class="???" />
<!-- 消息监听容器 -->
<bean id="messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<!-- 引入队列【仅能选择一个】 -->
<property name="destination" ref="queueDestination" />
<!-- 引入主题【仅能选择一个】 -->
<property name="destination" ref="topicDestination" />
<property name="messageListener" ref="messageListener" />
</bean>
【生产者接口】
public interface Producer {
void sendMessage(String message);
}
【生产者实现类】
public class Producer implements Producer {
@Autowired
private JmsTemplate jmsTemplate;
@Resource(name = "消息的id属性,队列或主题")
private Destination destination;
@Override
public void sendMessage(String name) {
jmsTemplate.send(destination, new MessageCreator(Session session) throws JMSException {
return session.createTextMessage(name);
});
}
}
【消费者】
public class Consumer implements MessageListener {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("接收消息" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
队列模式和主题模式的切换在于生产者/发布者通过@Resource所引用的消息,以及消息模式的配置和消息监听器容器所引用的消息模式