一、创建生产者代码
public class QueueProducer {
private static final String ACTIVEMQ_URL = "tcp://localhost:61616";
private static String queneName = "myQueue1";
public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = connectionFactory.createConnection();
//打开连接
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列
Queue queue = session.createQueue(queneName);
//创建生产者
MessageProducer producer = session.createProducer(queue);
for(int i = 0 ; i < 3 ; i++){
//创建文本消息
TextMessage textMessage = session.createTextMessage("第" + i + "个文本消息");
//发送消息
producer.send(textMessage);
System.out.println("生产者生产消息:"+textMessage.getText());
}
//关闭连接
producer.close();
session.close();
connection.close();
System.out.println("生产消息完成");
}
}
二、创建消费者代码
public class QueueConsumer {
private static final String ACTIVEMQ_URL = "tcp://localhost:61616";
private static String queneName = "myQueue1";
public static void main(String[] args) throws JMSException, IOException {
//创建连接工厂
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = connectionFactory.createConnection();
//开启连接
connection.start();
//创建会话 第一个参数为是否开启事务,第二个参数是消息签收方式为自动签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列
Queue queue = session.createQueue(queneName);
//创建消费者
MessageConsumer consumer = session.createConsumer(queue);
//接收消息
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
if(message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("消费者消费消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
System.in.read();
consumer.close();
session.close();
connection.close();
System.out.println("消费消息完成");
}
}
三、ActiveMQ消息可靠性
如何保证消息可靠性?可从消息生产者、消费者、以及消息中间件三方面来分析
1、从消息生产者来说
生产者将数据发送到 MQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。此时可以选择用MQ 提供的事务功能,就是生产者发送数据之前开启 MQ 事务然后发送消息,如果消息没有成功被MQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务,然后重试发送消息;如果收到了消息,那么可以提交事务
上述代码中connection.createSession()
方法的第一个参数,即设置是否开启事务,默认是不开启事务,如果设置为true,需要进行commit,消息才会被发送到队列中去,即设置session.commit()
try{
session.commit();
}catch{
session.rollback();
}finally{
session.close();
}
2、从消息消费者来说
刚消费到,还没处理,结果进程挂了,MQ 认为你都消费了,这数据就丢了。这个时候得用MQ 提供的 ack 机制,简单来说,就是你必须关闭MQ 的自动 ack,然后每次你自己代码里确保处理完的时候,再在程序里 ack
connection.createSession()
方法的第二个参数,即设置签收方式,Session.AUTO_ACKNOWLEDGE
为自动签收,Session.CLIENT_ACKNOWLEDGE
为手动签收。设置手动签收的时候,需要调用textMessage.acknowledge()
方法来表示接收消息成功
3、从消息中间件MQ本身来说
就是 MQ 自己弄丢了数据,这个你必须开启MQ 的持久化,就是消息写入之后会持久化到磁盘,哪怕是 MQ 自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。除非极其罕见的是,MQ 还没持久化,自己就挂了,可能导致少量数据丢失,但是这个概率较小
(1)、设置消息持久化:
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT)
(2)、设置消息非持久化:
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT)
(3)、队列的默认传送方式是持久化的,此模式保证这些消息只被传送一次和成功使用一次
(4)、持久化的主题(Subscriber):
一定要先运行一次消费者,类似于像MQ注册,表明我订阅了这个主题。
然后再运行生产者生产消息,此时,无论消费者是否在线,都会接收到,不在线的话,下次连接的时候,会将没收到的消息接收过来
4、事务和签收的关系
在事务性会话中,当一个事务被成功提交则消息被自动签收。
如果事务回滚,则消息会被再次传送。
非事务性会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgementmode)