在工作过程中,有时候需要处理的请求太多,而这些请求对实时性的要求又不是非常高,这时候我们可以消息队列来提高我们的系统吞吐量,将请求暂放到队列中,然后另外一个应用持续从队列取得需要处理的请求进行处理,从而增强系统抵御大流量的请求能力。今天我们这里首先简单介绍一下ActiveMQ和它的简单使用。
目录
ActiveMQ介绍
ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。(摘自百度百科)
ActiveMQ作为消息中间件,支持两种模式,一种是队列模式,一种是主题模式。队列模式就是当我们的消息生产者(Producer)向一个目的地(Destination)存放消息,如果有多个消费者(Consumer),这些消费者会分别消费队列里的消息,但是一条消息只能被其中一个消费者消费。而主题模式,就是每条消息可以供多个消费者使用。
ActiveMQ简单示例
首先在官网下载最新版的ActiveMQ,这里我下载的是5.15.8版本的。
之后将下载下来的压缩包解压缩,在目录里面的bin下有win32和win64 可以根据自己的需要进行选择.
如果没有修改默认的配置文件,那么直接运行win64/wind32下的activemq.bat 就能启动ActiveMQ,启动完成之后 在浏览器访问localhost:8162 就会出现登录的弹窗,输入默认的用户名密码admin admin。
然后就能进入ActiveMQ监控页面
在这里我们可以模拟发送消息 以及 监控消息队列消息的数量等。
引用jar包
这里我使用的是SpringBoot环境,只需要在pom.xml增加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
队列模式
实现代码
消息生产者:
public class AppProducer {
private static final String URL="tcp://127.0.0.1:61616";
private static final String QueueName="QueueTest";
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(URL);
Connection conn = connectionFactory.createConnection();
conn.start();
Session session = conn.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination=session.createQueue(QueueName);
MessageProducer producer = session.createProducer(destination);
TextMessage message=session.createTextMessage("发送自AppProducer的消息");
producer.send(message);
conn.close();
}
}
消息消费者:
public class AppConsumer {
private static final String URL="tcp://127.0.0.1:61616";
private static final String QueueName="QueueTest";
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory(URL);
Connection conn=connectionFactory.createConnection();
conn.start();
Session session=conn.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination=session.createQueue(QueueName);
MessageConsumer consumer=session.createConsumer(destination);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message msg) {
TextMessage message=(TextMessage) msg;
try {
System.out.println("接收消息:"+" "+message.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
}
}
可以看出来两者还是比较类似的,首先都是先创建了ConnectionFactory,然后创建Connection开启连接,获得Session,然后根据session创建Destination(目的地)这里是使用了createQueue代表是建立队列模式消息,根据Destination创建Producer或者Consumer。之后生产者就可以发送消息,最后关闭生产者的连接。而消费者则是设置监听事件,由于消费者是异步处理消息,所以消费者不能关闭连接。
测试
首先运行生产者,然后去http://localhost:8161/admin/queues.jsp 查看到队列
之后运行消费者,看到控制台输出
而队列页面发现 消息确实被消费了
保持消费者不关闭,再次启动生产者main方法,会发现消费者控制台再次输出
并且队列页面也对应发生了变化。
开启两个消费者,会发现队列模式下,一条消息只能被一个消费者消费。而且从上面示例,可以看出来当消息进入消息队列时 可以没有消费者,在出现消费者之后,消费者会从队列中一条条取出处理。
主题模式
实现代码
主题发布者:
public class AppTopicPublisher{
private static final String URL="tcp://127.0.0.1:61616";
private static final String TopicName="topicTest";
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory(URL);
Connection conn=connectionFactory.createConnection();
conn.start();
Session session=conn.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination=session.createTopic(TopicName);
MessageProducer producer= session.createProducer(destination);
TextMessage message=session.createTextMessage("来自AppTopicProducer的消息");
producer.send(message);
conn.close();
}
}
主题订阅者:
public class AppTopicSubscriber {
private static final String URL="tcp://127.0.0.1:61616";
private static final String TopicName="topicTest";
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory(URL);
Connection conn=connectionFactory.createConnection();
conn.start();
Session session=conn.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination=session.createTopic(TopicName);
MessageConsumer consumer=session.createConsumer(destination);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message msg) {
TextMessage message=(TextMessage) msg;
try {
System.out.println("接收消息:"+" "+message.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
}
}
从上面代码可以看出来 其实队列模式和主题模式 代码区别不大,只是在创建Destination的时候,一个使用createQueue 另外一个 使用了createTopic。
测试
首先运行主题发布者程序,然后访问http://localhost:8161/admin/topics.jsp 可以看到
之后运行订阅者程序,发现订阅者控制台并没有出现我们发送的消息,这是因为一般情况订阅者只能在发布者发布消息之前订阅才能接受到消息,而发布者之前发布的消息不会被后订阅的订阅者消费。
我们启动两个个订阅者程序,然后保持不关闭,之后启动3次发布者,然后发现每个订阅者都可以收到发布者发布的消息,查看topics.jsp可以看到
有4条消息入栈 而有6条消息出栈,这是因为最开始的一条消息一直没被消费,而后面的3次消息 每个订阅者都收到了 所以出栈消息是 2*3=6。
两模式的区别
队列模式 一条消息只能被一次消费,并且可以消费生产者之前生成的消息。而主题模式类似于群体订阅推送,一条消息可以被多个订阅者消费,但是不能获得发布者之前发布的消息。