消息队列的作用是什么?
关于消息队列的使用
这篇文章讲得好好高深,看它就没错了。
搭建ActiveMQ
在没有接触消息队列之前我们一直熟悉请求/响应的模式,但是这种模式一直有很多问题产生,特别是在实际的生产环境中,于是便引入了消息队列这种模式,把所有待执行指令存入消息队列等待程序调用,这种方法的好处在上面那位大牛的文章中也写得十分清楚了。
ActiveMQ是一种消息队列,它是对JMS(Java Message Service)的一种实现,现在常用的消息队列基本就是ActiveMQ和KAFKA了吧。
下载地址:ActiveMQ
进入ActiveMQ官网下载最新版,解压后,进入/bin文件夹有一个64位,一个32位,文件夹里面的activemq.bat双击运行,一般双击开启服务,服务默认被挂在本地61616端口上,如果开启不了很有可能该端口被占用,关于这个问题百度上很多解答,不赘述。
开启成功后,进入消息队列的管理平台,默认密码账号都是admin,进入后点击queue按钮能看到如下界面。
这里可以看到你的ActiveMQ中存在哪些消息队列,有哪些消费者在消费这些队列。点击Browse能看到具体的消息来自哪台主机哪个端口。
好像不用码代码就有了我们的消息队列了,毕竟是别人实现好的。我们要做的是在我们对应的模块上写对应的方法或者接口,它们或负责发送消息到我们的消息队列,或监听消息队列中中是否有待处理的消息。
消息队列的响应模式
叫这个名儿不知对不对,不对别打我。
消息队列有两种响应方式,p2p和发布/订阅。p2p是点对点,即一个生产者对应一个消费者,在队列中的消息一旦被消费就不在存在队列中。发布/订阅是一对多的消费方式,生产者发布的消息被多个模块消费,每个模块获得该消息的一份拷贝,具体在上面大牛文章概念已十分清晰,我就是卖个萌。
消息队列的一个小Demo
JMSProducer代码如下:
package com.icebear.activemq;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class JMSProducer {
//默认用户名,admin
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
//默认登陆密码,admin
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
//默认链接,tcp://localhost:61616
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
//默认信息条数,1000
private static final int SENDSUM = ActiveMQConnection.DEFAULT_THREAD_POOL_SIZE;
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 = (Connection) connectionFactory.createConnection();
//开始连接
connection.start();
//创建session
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
//目的地,我们的消息具体发送到哪一个消息队列
destination = session.createQueue("queue");
//创建messageProducer,它需要被指明生产信息到哪一个地方
messageProducer = session.createProducer(destination);
//send message
for(int i=0; i<JMSProducer.SENDSUM; i++){
TextMessage message = session.createTextMessage("JMSProducer 发送消息 " + i);
System.out.println("JMSProducer 发送消息 " + i);
//调用messageProducer的send方法发送消息
messageProducer.send(message);
}
//提交,没有提交的信息发送不会发送出去
session.commit();
}catch(Exception e){
}
}
}
总结一下上面的代码,无非是创建了五个对象,connectionFactory,connection,session,destination,messageProducer,这几个对象存在关系如下:
connectionFactory 创建了 connection
connection 创建了 session
session 创建了 destination
session 用 destination 创建了 messageProducer
最后 messageProducer 往destination 里面塞message
大概流程就是这样。
现在看看消费者的代码是如何的。
package com.icebear.activemq;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class JMSConsumer {
//默认用户名
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
//默认密码
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
//默认队列地址
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
public static void main(String[] args){
ConnectionFactory connectionFactory;
Connection connection = null;
Session session;
Destination destination;
MessageConsumer messageConsumer;
//创建连接工厂
connectionFactory = new ActiveMQConnectionFactory(JMSConsumer.USERNAME,
JMSConsumer.PASSWORD, JMSConsumer.BROKEURL);
try{
//创建连接
connection = (Connection)connectionFactory.createConnection();
//开启连接
connection.start();
//创建session
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
//创建目的地
destination = session.createQueue("queue");
//创建消费者
messageConsumer = session.createConsumer(destination);
while(true){
TextMessage message = (TextMessage)messageConsumer.receive(100000);
if(message != null){
System.out.println("接收到这样的消息:" + message.getText());
}else{
break;
}
}
}catch(Exception e){
}
}
}
大体的实现代码和生产者是一样的,这里需要提出几点,首先上面只是实现了p2p模式,实现发布/订阅只要把创建的队列改为createTopic,而且上面的消费方式是同步的,意味着在等待消息到来之前,这个程序是干等什么都不干的,要将其实现成异步需要实现一个监听器,这部分我们在下面讲。
SSM整合JMS
ActiveMQ配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">
<context:component-scan base-package="com.icebear" />
<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory" />
</bean>
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>
<!--这个是队列目的地 -->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>queue</value>
</constructor-arg>
</bean>
<!-- 消息监听器 -->
<bean id="consumerMessageListener"
class="com.icebear.springactivemq.service.impl.ConsumerMessageListener" />
<!-- 消息监听容器 -->
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="queueDestination" />
<property name="messageListener" ref="consumerMessageListener" />
</bean>
</beans>
熟悉了上面程序调用JMS接口,那么这里的整合步骤也相当清晰,它也是在实现我们之前实现的步骤,在这里我们可以看到它创建了一个监听器,监听器的实现如下:
package com.icebear.springactivemq.service.impl;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class ConsumerMessageListener implements MessageListener{
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
System.out.println("接收一个纯文本信息");
try {
System.out.println("信息内容:" + textMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
只要实现了MessageListener接口,重写它的onMessage方法就能获得监听器监听到的所有内容,然后在这里对这些数据进行具体的操作。
生产者跟我们普通的面向接口开发的习惯一样,首先设计一个接口:
package com.icebear.springactivemq.service;
import javax.jms.Destination;
public interface ProductorService {
public void sendMessage(Destination destiantion, final String message);
}
它只有一个方法,就是sendMessage
具体实现如下:
package com.icebear.springactivemq.service.impl;
import javax.jms.Destination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import com.icebear.springactivemq.service.ProductorService;
@Service
public class ProductorServiceImpl implements ProductorService{
@Autowired
private JmsTemplate jmsTemplate;
@Override
public void sendMessage(Destination destination, final String message) {
System.out.println("生产者发送了一个消息");
System.out.println("消息内容:" + message);
jmsTemplate.convertAndSend(destination, message);
}
}
任何时候需要向队列推入信息就可以调用这个接口,另一个监听同样队列的模块会在监听到消息的时候执行相应的方法,这样就完全把ActiveMQ整合到SSM中啦。