目录
下载安装
下载 apache-activemq-5.15.2-bin.tar.gz
cd /usr/local/
tar -xzvf apache-activemq-5.15.2-bin.tar.gz
cd /usr/local/apache-activemq-5.15.2/bin
./activemq start
我们可以通过访问 http://localhost:8161/ 进入控制台
Queue通信方式
Queue通信方式中只能有一个生产者一个消费者,且消息被消费后出队列.Queue通信方式下如果生产者发送消息时消费者宕机,消费者重启后依然可以收到消息
pom.xml
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.9.0</version>
</dependency>
生产者 JMSProducer.java
package cn.milo.producer;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class JMSProducer {
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
private static final String BROKEURL = "tcp://127.0.0.1:61616";
private static final int SENDNUM = 10;
public static void main(String[] args) {
//连接工厂
ConnectionFactory connectionFactory;
//连接
Connection connection = null;
//会话 接受或者发送消息的线程
Session session;
//消息的目的地
Destination destination;
//消息生产者
MessageProducer messageProducer;
//实例化连接工厂
connectionFactory = new ActiveMQConnectionFactory(JMSProducer.USERNAME, JMSProducer.PASSWORD, JMSProducer.BROKEURL);
try {
//通过连接工厂获取连接
connection = connectionFactory.createConnection();
//启动连接
connection.start();
//创建session 第一个参数代表开启事务,第二个参数是消息确认方式
session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);
//创建一个名称为HelloWorld的消息队列
destination = session.createQueue("HelloWorld");
//创建消息生产者
messageProducer = session.createProducer(destination);
//发送消息
sendMessage(session, messageProducer);
//如果不开启事务这里不需要commit
session.commit();
session.close();
} catch (Exception e) {
e.printStackTrace();
}finally{
if(connection != null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
/**
* 发送
*/
public static void sendMessage(Session session,MessageProducer messageProducer) throws Exception{
for (int i = 0; i < JMSProducer.SENDNUM; i++) {
//创建一条文本消息
TextMessage message = session.createTextMessage("ActiveMQ 发送消息" +i);
System.out.println("发送消息:Activemq 发送消息" + i);
//通过消息生产者发出消息
messageProducer.send(message);
}
}
}
消费者 JMSConsumer.java
package cn.milo.consumer;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;
public class JMSConsumer {
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
private static final String BROKEURL = "tcp://127.0.0.1:61616";
public static void main(String[] args) {
//连接工厂
ConnectionFactory connectionFactory;
//连接
Connection connection = null;
//会话 接受或者发送消息的线程
final Session session;
//消息的目的地
Destination destination;
//消息的消费者
MessageConsumer messageConsumer;
//实例化连接工厂
connectionFactory = new ActiveMQConnectionFactory(JMSConsumer.USERNAME, JMSConsumer.PASSWORD, JMSConsumer.BROKEURL);
try {
//通过连接工厂获取连接
connection = connectionFactory.createConnection();
//启动连接
connection.start();
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个连接HelloWorld的消息队列
destination = session.createQueue("HelloWorld");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
while (true) {
TextMessage textMessage = (TextMessage) messageConsumer.receive(100000);//同步接受方法
if(textMessage != null){
System.out.println("收到的消息:" + textMessage.getText() + "消息id : " + textMessage.getJMSMessageID() + " testMessage = " + textMessage);
}else {
break;
}
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
Topic通信方式
Topic通讯方式也叫pub/sub,这个道理很简单就像是我们听收音机一样,你听不听收音机都在播,多少人听收音机都是同样的内容,今晚7点播的内容你晚上9点再过来听就听不到了. 但是有一种特例,如果你的consumer设置了持久化订阅;这种情况下client1这个人如果7点播的时候没听到9点是可以听到的.
这里的收音机就是producer,每位听众就是consumer
生产者 JMSTopicProducer.java
package cn.milo.producer;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
class JMSTopicProducer {
public static void main(String[] arg){
String user = ActiveMQConnection.DEFAULT_USER;
String password = ActiveMQConnection.DEFAULT_PASSWORD;
String url = "tcp://localhost:61616";
String subject = "mq.topic";
ActiveMQConnectionFactory amcf = new ActiveMQConnectionFactory(user, password, url);
try {
Connection conn = amcf.createConnection();
conn.start();
Session session = conn.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);
Destination d = session.createTopic(subject);
MessageProducer producer = session.createProducer(d);
for (int i = 0; i <= 20; i++){
TextMessage message = session.createTextMessage(i+"");
producer.send(message);
System.out.println("--发送消息:" + i);
}
session.commit();
session.close();
conn.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
消费者 JMSTopicConsumer.java
package cn.milo.consumer;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQTopicSubscriber;
import javax.jms.*;
public class JMSTopicConsumer {
public static void main(String[] args) {
String USERNAME = "admin";//默认连接用户名
String PASSWORD = "admin";//默认连接密码
String BROKEURL = "tcp://localhost:61616";//默认连接地址
String subject = "mq.topic";
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory( USERNAME, PASSWORD, BROKEURL);
Connection connection;
try{
connection= connectionFactory.createConnection();
//创建持久订阅需要设置clientID
connection.setClientID("client1");
connection.start();
final Session session = connection.createSession(Boolean.FALSE,Session.CLIENT_ACKNOWLEDGE);
Topic topic = session.createTopic(subject);
// //创建持久订阅
//ActiveMQTopicSubscriber subscriber = (ActiveMQTopicSubscriber) session.createDurableSubscriber(topic, "Subscriber_name_test");
//subscriber.setMessageListener();//这里需要使用异步接受
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message msg){
TextMessage message = (TextMessage) msg;
try {
System.out.println("--订阅者一收到消息:" +message.getText());
}catch(Exception e) {
e.printStackTrace();
}
}
});
// connection.stop();
// connection.close();
}catch (JMSException e) {
e.printStackTrace();
}
}
}
持久化
消费者持久化(接收端持久化)
非持久订阅(non-durable subscription)和持久订阅(durablesubscription)
关于持久化订阅上边Topic已经提到了,持久化订阅也仅对Topic生效.Queue所有消息都是持久化的.而且持久化订阅是consumer的一个属性,换句话讲,这个consumer是持久化的consumer.或者这个consumer不是持久化的.这个大家要理解,不然和下边很容易混淆
消息持久化(发送端持久化)
发送端持久化指的是消息的持久化,换句话讲这个消息是持久化的.应对的场景是这样的.生产者发送了ABCD四个消息,这是消费者都不在线.而且突然activemq服务器宕机,那ABCD四个消息消费者是不是永远都拿不到了.activemq给我们提供了多种持久化方式分别是:JDBC,AMQ,KahaDB和LevelDB,但最终目的都是在activemq宕机的时候消息不会丢失.
当然这里消息持久化还有一种使用场景,消费者消费能力很差,但是生产者生产能力很强,这是activemq很可能在某个时间点有很多没有消费掉的消息,如果此时activemq宕机也会出现消息丢失,同样还是消息持久化机制来解决.
设置消息持久化方法如下:
message.setJMSDeliveryMode(1);
DeliveryMode.NON_PERSISTENT=1:非持久 ;
DeliveryMode.PERSISTENT=2:持久
ACK确认消息
JMS API中约定了Client端可以使用四种ACK_MODE,在javax.jms.Session接口中:
- AUTO_ACKNOWLEDGE = 1 自动确认
- CLIENT_ACKNOWLEDGE = 2 客户端手动确认
- DUPS_OK_ACKNOWLEDGE = 3 自动批量确认
- SESSION_TRANSACTED = 0 事务提交并确认
- 此外AcitveMQ补充了一个自定义的ACK_MODE: NDIVIDUAL_ACKNOWLEDGE = 4 单条消息确认
这里的ACK机制是针对消费者的.在生产者设置这个参数我测试是没起什么作用
Session session = conn.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);这个方法第一个参数是是否开启事务,如果开启事务的话第二个参数就会忽略,JMS会自动采用SESSION_TRANSACTED机制.
这里还有一个坑,在spring集成了activemq的时候,配置文件中设置CLIENT_ACKNOWLEDGE这种确认机制的时候并不生效,原因请看spring类org.springframework.jms.listener.AbstractMessageListenerContainer的一段源码:
protected void commitIfNecessary(Session session, Message message) throws JMSException {
// Commit session or acknowledge message.
if (session.getTransacted()) {
// Commit necessary - but avoid commit call within a JTA transaction.
if (isSessionLocallyTransacted(session)) {
// Transacted session created by this container -> commit.
JmsUtils.commitIfNecessary(session);
}
}
else if (message != null && isClientAcknowledge(session)) {
message.acknowledge();
}
}
就是当设置sessionAcknowledgeMode为2时,虽然是客户端手动确认,但是却被spring自动确认了,造成设置无效。这时只需要把sessionAcknowledgeMode的值设置成activemq自定义的类型INDIVIDUAL_ACKNOWLEDGE = 4即可。
Spring集成
大家到百度自己查一下吧,我这里自己写的demo,但是这个demo一直都在瞎改.有兴趣同学可以看一下 https://github.com/shangmingtao/dubbodemo
activemq内容真的很多.上边写的完全是为了学习而学习,所以难免很多地方都漏掉了.大家有好的activemq资料发我一下,感激不尽.