目录
1、四大 MQ 介绍
在介绍 ActiveMQ 之前,我想先把当下存在且有一定认可度的四大 MQ 简单做下说明。为了便于整体观看和比对,我列了如下表格:
名称 | 单机吞吐量 | Topic 数量对吞吐量的影响 | 时效性 | 可用性 | 可靠性 | 优势 |
---|---|---|---|---|---|---|
ActiveMQ | 万级 | 大 | ms | 高,基于主从架构实现高可用性 | 有较低概率丢失数据 | |
RabbitMQ | 万级 | 大 | um | 高,基于主从架构实现高可用性 | 有较低概率丢失数据 | 延迟极低 |
RocketMQ | 十万级 | 小 | ms | 非常高,分布式架构 | 经过参数优化配置,消息可以做到 0 丢失 | 可支持大量 Topic |
Kafka | 十万级 | 大 | ms | 非常高,分布式架构 | 经过参数优化配置,消息可以做到 0 丢失 | 吞吐量 |
其中,RabbitMQ
和 RocketMQ
是目前中小型公司用的比较多的。RabbitMQ 是用 erlang 语言开发的,RocketMQ 是用的 Java。如果你是做 Java 开发的,最好选用 RocketMQ,可控制性高。
Kafka
那当然是大数据必选。至于今天要介绍的 ActiveMQ
,用起来比较简单,但是社区已经不怎么活跃,疲态日显。别问我为啥还在用,我只能告诉你:人在公司,身不由己。
2、JMS
JMS(Java Messaging Service)是 Java 平台上有关面向消息中间件的技术规范,就像你可以用 JDBC 去连接 MySql、Oracle 等数据库一样,你可以用 JMS 来连接不同厂商的 MQ 产品。
2.1 JMS 的消息格式
- TextMessage:一个字符串对象
- MapMessage:一套名称 - 值对
- ObjectMessage:一个序列化的 Java 对象
- BytesMessage:一个字节的数据流
- StreamMessage:Java 原始值的数据流
2.2 JMS 的消息传递类型
-
P2P(Point - to - Point):
点对点的,即一个生产者和一个消费者一一对应。一个生成者产生一个消息只能被一个消费者消费,消费完,消息就没有了。如下图:
-
P / S(Publish / Subscribe):
发布 / 订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收。发布即接收,没有消费者就自动结束,不会存在服务器里。如下图:
3、安装配置
3.1 安装
- 下载地址:https://activemq.apache.org/components/classic/download/
- 按平台下载:
- 下载完后解压;
- 进入 bin 目录,window 选择对应的 32 或 64 位文件夹后,直接双击“activemq.bat”即可完成启动,liunx 下执行“./activemq start”完成启动。然后打开浏览器输入地址:http://localhost:8161/ 进入 index 页,即表示成功,
默认账号密码均为 admin
:
3.2 修改管理界面密码
- 进入 conf 目录下;
- 打开 jetty-realm.properties 文件;
- 找到 admin: admin, admin,格式为 - 用户名 : 密码, [角色名, …]
- 想改成什么密码修改即可修改即可。
3.3 修改消息传送密码
- 进入 conf 目录下;
- 打开 credentials.properties 文件;
- activemq.username 为用户名,activemq.password 为密码,按照自己的意愿修改;
- 打开 activemq.xml 文件,在 上面添加如下配置:
<!-- 添加访问 ActiveMQ 的账号密码 -->
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="${activemq.username}" password="${activemq.password}" groups="users,admins"/>
</users>
</simpleAuthenticationPlugin>
</plugins>
- 还有一种
jssa
的方式,复杂一点,不过可以精确到消息的读写权限控制,感兴趣的话,可以搜一搜,这里就不介绍了。
4、代码示例
4.1 P2P
Producer:
import javax.jms.*;
public class QueueProducer {
public static void main(String[] args) throws JMSException {
// 1. 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
// 2. 获取连接
Connection connection = connectionFactory.createConnection();
// 3. 启动连接
connection.start();
/* 4. 获取 session:
* Session.AUTO_ACKNOWLEDGE = 1,自动确认
* CLIENT_ACKNOWLEDGE = 2,客户端手动确认
* DUPS_OK_ACKNOWLEDGE = 3,自动批量确认
* SESSION_TRANSACTED = 0,事务提交并确认
*/
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5. 创建队列对象
Queue queue = session.createQueue("test-queue");
// 6. 创建消息生产者
MessageProducer producer = session.createProducer(queue);
// 7. 创建消息
TextMessage textMessage = session.createTextMessage("Hello, world!");
// 8. 发送消息
producer.send(textMessage);
// 9. 关闭资源
producer.close();
session.close();
connection.close();
}
}
Consumer:
import javax.jms.*;
import java.io.IOException;
public class QueueConsumer {
public static void main(String[] args) throws JMSException, IOException {
// 1. 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
// 2. 获取连接
Connection connection = connectionFactory.createConnection();
// 3. 启动连接
connection.start();
// 4. 获取 session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5. 创建队列对象
Queue queue = session.createQueue("test-queue");
// 6. 创建消息消费者
MessageConsumer consumer = session.createConsumer(queue);
// 7. 监听消息
consumer.setMessageListener(message -> {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("接收到消息:" + textMessage.getText());
} catch (JMSException e) {
System.out.println("接收消息异常:" + e.getMessage());
}
});
// 8. 等待键盘输入,防止程序退出
System.in.read();
// 9. 关闭资源
consumer.close();
session.close();
connection.close();
}
}
运行示例:
- 先运行 Producer,然后在 ActiveMQ 界面会出先一个 Queue:
- 再运行 Consumer,控制台会输出:
接收到消息:Hello, world!
,且不会退出; - 多次运行 Producer,观察 Consumer 会一直输出。
4.2 P/S
同时创建 2 个消费者,分别修改生产者和消费者创建队列对象
为创建主题对象
:
// 5. 创建主题对象
Topic topic = session.createTopic("test-topic");
运行示例:
- 要先运行 2 个 Consumer;
- 再运行 Producer;
- 观察两个 Consumer 控制台会分别输出:
Topic1:接收到消息:Hello, world!
,Topic2:接收到消息:Hello, world!
,且不会退出; - 多次运行 Producer,观察 Consumer 会一直输出;
- ActiveMQ 管理界面如下:
5、整合到 SpringBoot
- 添加 pom 依赖:
<!-- activemq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
- 添加配置信息:
# activemq 基础配置
spring.activemq.broker-url=tcp://127.0.0.1:61616
# 生产环境设置密码
spring.activemq.user=[根据实际情况填写]
spring.activemq.password=[根据实际情况填写]
spring.activemq.pool.enabled=false
- 编写生产者工具类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Destination;
@Component
public class ActivemqUtil {
private final JmsMessagingTemplate jmsTemplate;
@Autowired
public ActivemqUtil(JmsMessagingTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
/**
* 发送消息
*
* @param destination Queue or Topic
* @param message 消息内容
*/
public void sendMessage(Destination destination, final String message) {
jmsTemplate.convertAndSend(destination, message);
}
}
- 添加消费者类:
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
@Service
public class Consumer {
@JmsListener(destination = "test-queue")
public void receiveQueue(String message) {
System.out.println("receiveQueue: " + message);
}
}
- 修改启动类:
import com.sohu.usstock.common.utils.ActivemqUtil;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.Resource;
import javax.jms.Destination;
@SpringBootApplication
public class UsstockApplication implements CommandLineRunner {
@Resource
private ActivemqUtil activemqUtil;
public static void main(String[] args) {
SpringApplication.run(UsstockApplication.class, args);
}
@Override
public void run(String... args) {
Destination queue = new ActiveMQQueue("test-queue");
// Destination topic = new ActiveMQTopic("test-topic");
activemqUtil.sendMessage(queue, "Hello, world!");
}
}
- 运行后查看结果,即可。