一,环境准备
* IDEA
* ActiveMQ5.15.5
* Maven
二,ActiveMQ安装
1,从ActiveMQ官网下载安装包
http://activemq.apache.org/download.html
2,上传安装包到Linux环境并解压tar包
* tar -zxvf activemq* -C /user/develop
3,{activemq\bin}目录下启动
* sh activemq start
4,在浏览器通过host:port进行连接,记得关闭防火墙
* ActiveMQ默认开启端口:8161
* service iptables status --> service iptables stop
* 启动账号密码:admin;admin
三,Queue
* Queue队列是一种点对点的消费模式, 即服务端发布一条消息,消费端根据发布的消息名称进行消息队列消费。消费后,该消息状态为已消费并回收,不再支持消费端进行消费。点对点消费不过时,服务端发布消息后,如果消费端未进行及时消费,则消息一直处于挂起状态,直到消费端启动并消费,整个消息队列流程才算完成。如果在等待消费过程中,服务端系统宕机,此时如果服务端发送消息状态为持久化消息(默认),则重新启动后会出现消息回流,消费端可继续进行消费;若消息状态为非持久化状态消息,消息丢失。
1,Jar包依赖
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.5</version>
</dependency>
2,消息发布
public class ActiveMQProducer {
public static void main(String[] args) throws JMSException {
// 获取链接工厂
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("tcp://192.168.91.128:61616");
// 创建链接
Connection connection = connectionFactory.createConnection();
connection.start();
// 创建会话
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue("myQueue");
// 创建生产者
MessageProducer producer = session.createProducer(destination);
// 创建消息
TextMessage message = session.createTextMessage("producer send message");
// 生产者发送消息
producer.send(message);
session.commit();
session.close();
}
}
3,消息消费
public class ActiveMQConsumer {
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("tcp://192.168.91.128:61616");
// 创建链接
Connection connection = connectionFactory.createConnection();
connection.start();
// 创建会话
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue("myQueue");
// 创建消费者
MessageConsumer consumer = session.createConsumer(destination);
// 接收信息
TextMessage textMessage = (TextMessage)consumer.receive();
// 消费信息
System.out.println(textMessage.getText());
session.commit();
session.close();
}
}
运行结果:
4,消息持续消费
* 如上,直接通过receivce()进行消费消费,消费消费过后,服务端程序处于假挂起状态,当服务端再次发送消息后不再进行消费,在实际开发过程中,需要保证持续消费,可以通过循环监听队列或者循环获取信息实现;
* 循环获取消息
while (true) {
// 接收信息
TextMessage textMessage = (TextMessage) consumer.receive();
// 消费信息
System.out.println(textMessage.getText());
session.commit();
}
* 循环监听队列
// 创建消费者
MessageConsumer consumer = session.createConsumer(destination);
// 创建监听
MessageListener listener = new MessageListener() {
@Override
public void onMessage(Message message) {
try {
System.out.println(((TextMessage)message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
};
while (true) {
consumer.setMessageListener(listener);
session.commit();
}
* 多次执行服务端后执行结果
5,服务端消息持久化
a,默认状态宕机处理
* 服务端发送消息
* ActiveMQ服务器重启
* 消息中心消息查看
* 消费端消费消息
b,持久化状态下宕机处理
* 设置消息发送持久化状态
// 创建生产者
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage("producer send message");
* 服务端发送消息
* ActiveMQ服务器重启
* 消息中心消息查看
* 消费端消费消息
c, 非持久化状态下宕机处理
* 设置消费发送非持久化状态
// 创建生产者
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage("producer send message");
* 服务端发送消息
* ActiveMQ服务重启
* 消息中心消息查看 -- 队列中无消息
* 消息消费服务 -- 消费端没有消息可以消费
四,Topic
* Topic队列是不定向队列,服务端根据一定的规则发布服务(如视频会议)到注册过该topic队列的消费端,消费端进行服务接收。在此过程中,如果消费端宕机或者未处于服务状态,则会导致信息丢失,服务端不会进行消息保存,类似与视频授课或者网络会议;当然,消费端也可以向该topic队列注册持久化订阅。持久化订阅后,服务端发送消息如果没有被持久化订阅的消费端消息,则服务端会进行消息流程,待消费端连接上服务后重新进行消息消费。
1,Jar包依赖
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.5</version>
</dependency>
2,消息发布
* Topic主题消息发送类似与Queue队列消息,创建消息时从createQueue转为createTopic
public class ActiveMQProducer {
public static void main(String[] args) throws JMSException {
// 获取链接工厂
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("tcp://192.168.91.128:61616");
// 创建链接
Connection connection = connectionFactory.createConnection();
connection.start();
// 创建会话
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createTopic("myTopic");
// 创建生产者
MessageProducer producer = session.createProducer(destination);
// 创建消息
TextMessage message = session.createTextMessage("producer send message");
// 生产者发送消息
producer.send(message);
session.commit();
session.close();
}
}
3,消息消费
public class ActiveMQConsumer {
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("tcp://192.168.91.128:61616");
// 创建链接
Connection connection = connectionFactory.createConnection();
// 持久化存储
connection.start();
// 创建会话
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 非持久化订阅 --> 未在线不能接收消息
// 创建目的地
Destination destination = session.createTopic("myTopic");
// 创建消费者
MessageConsumer consumer = session.createConsumer(destination);
// 接收信息
while (true) {
TextMessage textMessage = (TextMessage) consumer.receive();
// 消费信息
System.out.println(textMessage.getText());
session.commit();
}
}
}
* 服务启动应先启动消费端,保证消息消费状态一直处于连接中;然后启动服务端进行消息广播,消费端及时接收到服务端发布的广播消息后进行消费
* Topic模式也可以通过监听进行
// 创建消费者
MessageConsumer consumer = session.createConsumer(destination);
// 创建监听
MessageListener messageListener = new MessageListener() {
@Override
public void onMessage(Message message) {
try {
System.out.println(((TextMessage)message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
};
// 接收信息
while (true) {
consumer.setMessageListener(messageListener);
session.commit();
}
* 如果先启动服务端,后启动客户端,则消费端会造成消息丢失,不能进行消息消费,不做演示
4,持久化订阅
* 持久化订阅,通过代码配置后,启动一次消费端注册消费端服务到消息中心,服务端发送消息后,若注册消费端未进行消息消费,则服务端会进行消息流程,待注册消费端消息消费完成后,整个消息流程结束
* 消费端注册服务
public class ActiveMQPersistentConsumer {
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("tcp://192.168.91.128:61616");
// 创建链接
Connection connection = connectionFactory.createConnection();
// 持久化存储
connection.setClientID("ZPJ-001");
connection.start();
// 创建会话
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 持久化订阅 --> 不在线依旧可以接收信息
Topic topic = session.createTopic("myTopic");
MessageConsumer consumer = session.createDurableSubscriber(topic, "ZPJ-001");
// 接收信息
while (true) {
TextMessage textMessage = (TextMessage) consumer.receive();
// 消费信息
System.out.println(textMessage.getText());
session.commit();
}
}
}
* 启动服务端发送Topic消息
* 启动完成后,可以看到注册消费端未消息消息为一条
* 消费端消费消息
五,消费性能优化
1,ActiveMQ默认消费模式
* 如图所示,在ActiveMQ配置文件activemq.xml中,配置一次默认消息消费数为1000条,所以如果有1500条消息由两个消费者进行消费,则消费者A消费1-1000条消息,消费者2消费1001-1500条;这在正式开发环境中如果有多个线程进行消息消费,一个线程一次抢占1000条消息无疑会造成性能浪费,再不改动配置文件的前提下,可以通过代码设置进行批量获取量设置和批量提交
2,默认消费模式演示
* 发送端连续发送1500条消息
// 创建消息
for (int i = 1; i <= 1500; i++) {
TextMessage message = session.createTextMessage("producer send message : " + i);
// 生产者发送消息
producer.send(message);
}
session.commit();
session.close();
* 消息中心消息查看
* 消费端创建两个消费者进行消息消费
// 创建消费者
MessageConsumer consumer = session.createConsumer(destination);
while (true) {
// 接收信息
TextMessage textMessage = (TextMessage) consumer.receive();
// 消费信息
System.out.println("消费者2 : " + textMessage.getText());
Thread.sleep(1000);
session.commit();
}
// 创建消费者
MessageConsumer consumer = session.createConsumer(destination);
while (true) {
// 接收信息
TextMessage textMessage = (TextMessage) consumer.receive();
// 消费信息
System.out.println("消费者1 : " + textMessage.getText());
Thread.sleep(1000);
session.commit();
}
* 消费结果
3,消费模式优化
* 通过配置批量获取数据量和是否批量提交操作来进行消费优化,用以实现在一定数量的消息内,各消费者服务能够平均的对消息进行消费
* 批量获取数据量配置
// 创建目的地
Destination destination = session.createQueue("myQueue?consumer.prefetchSize=10");
此配置表示每个消费端线程每次消费消息数量为10条,消费完成后再从消息中心获取进行消息消费
* 是否批量提交配置
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory("tcp://192.168.91.128:61616?" +
"jms.optimizeAcknowledge=true&" +
"jms.optimizeAcknowledgeTimeOut=10000");
此配置表示开启消息批量提交,默认连接超时事件为10S,该配置必须同上一条配置出现,单独出现无意义;
* 执行结果
六,持久化存储机制
1,kahadb(默认方式)
* 配置文件,./conf/activemq.xml Line81
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
* 数据存储在./bin/data/kahadb中
2,JDBC
* 更改默认kamaDB存储方式为JDBC存储方式,注意修改数据库连接,另外需要刻意修改brokerName
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="cluster_1" dataDirectory="${activemq.data}">
<persistenceAdapter>
<!-- <kahaDB directory="${activemq.data}/kahadb"/> -->
<jdbcPersistenceAdapter dataSource="#Mysql-DS" createTableOnStartup="true" />
</persistenceAdapter>
<bean id="Mysql-DS" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.91.3:3306/activemq?relaxAutoCommit=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
* 更改完成后重启ActiveMQ服务,查看数据库,数据库会自动创建出三张表
* 服务端发布消息,消息会存储到数据中
* 消费端消费完成后,数据表中数据自动删除
3,Memory
4,LevelDB+Zopkeeper节点存储
* 集群处理
5,JDBC with AcitveMQ journal(日志同步未消费数据到数据库)
* 克服JDBC不足,JDBC连接每次消息发送和消费事务提交后,都需要进行读库和写库处理。使用日志处理,对日志记录状态未处理的消息,根据一定时效性进行整体写库操作,提升了JDBC的处理性能
* 配置文件,注释原来persistenceAdapter标签,新增persistenceFactory标签
<!--
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
<jdbcPersistenceAdapter dataSource="#Mysql-DS" createTablesOnStartup="true" />
</persistenceAdapter>
-->
<persistenceFactory>
<journalPersistenceAdapterFactory
dataSource="#Mysql-DS"
dataDirectory="activemqdata"
useQuickJournal="true"/>
</persistenceFactory>
* 启动成功后,会根据日志自动将服务端发布的未消费消息根据一定的实效规则存入数据库
* 消息写入1000条,然后消费10条中止,坐等同步日志记录到数据库
* 下图可以看到数据已经同步,这个过程大概等了10分钟左右
* 消息全部消费完成后,数据库也需要等到指定时间进行数据同步,此处不同步!