1.activeMq简介:
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。
2.JMS1.1规范相关概念:jms(Java Message Service)
- 提供者:实现JMS规范的消息中间件服务器.
- 客户端:发送或者接受消息的应用程序.
- 生产者/发布者:创建并发送消息客户端.
- 消费者/订阅者:接收并处理消息的客户端.
- 消息:应用程序之间传递数据内容.
- 消息模式:在客户端之间传递消息的方式,JMS中定义了主题和队列两种模式.
3.JMS的消息模式:
3.1.队列模型
- 客户端包括生产者和消费者
- 队列中的消息只能被一个消费者消费.
- 消费者可以随时消费队列中的消息.
队列模型中,消费者的每个连接会依次接收JMS队列中的消息.每个连接接收到的是不同的消息
3.2.主题模式
- 客户端包括发布者和订阅者
- 主题中的消息被所有订阅者消费
- 消费者不能消费订阅之前就发送到主题中的消息.每个消费者收到的是全部的消息
4.JMS编码接口
- ConnectionFactory:用于创建连接到消息中间件的连接工厂.
- Connection:代表了应用程序和消息服务之间的通讯链路.
- Destination:目的地,指消息发布和接收的地点,包括队列和主题.
- Session:表示一个单线程的上下文,用于发送和接收消息.
- MessageConsumer:由会话创建,用于接收发送到目标的主题和消息.
- MessageProducer:由会话创建,用于发送消息到目标.
- Message:是消息体,是在生产者和消费者之间传递的对象,由消息头(必须存在),消息属性,消息体组成.
5.activeMq的应用场景
异步处理,应用解耦,流量削峰,消息通讯
5.1.异步处理:用户注册后,发送邮件和短信告诉其注册成功
传统方式:串行处理,1.先将用户信息持久化到数据库,2.发送邮件,3.发送短信。
消息队列:异步处理,1.将用户信息持久化到数据库,2.将提醒相关信息放入队列,进行异步通知。加快响应时间。
5.2.应用解耦:用户下单后,订单系统需要通知库存系统。
传统方式:订单系统调用库存系统的接口,增加系统间的耦合度。
存在的问题:1.订单系统调用库存系统失败,导致订单失败。
消息队列:1.订单系统:用户下单后,订单系统将数据持久化到数据库,将消息写入消息队列,返回用户订单下单成功,请等待物流配送的提示信息
2.库存系统:订阅下单的消息,采用 发布/订阅 的方式,获取下单信息,库存系统根据下单信息,进行减库存操作。
解决库存系统失败导致订单失败的问题,订单系统将订单信息持久化到数据库,向队列发布一条消息,至此订单系统的操作完成,库存系统通过定阅消息进行相关操作。库存系统出现问题将不会影响到订单系统,实现系统间的解耦。
5.3.流量削锋:秒杀
流量过大,导致系统崩溃的问题。在用户请求到达秒杀系统之前放入队列,超过队列最大数量的请求抛弃,秒杀系统根据队列中的消息进行后续的相关处理,控制了并发量。
5.4.消息通讯:点对点,群发
6.P2P(queue)
消息生产者生产消息发送到 queue 中,然后消息消费者从 queue 中取出并且消费消息。消息被消费以后,queue 中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue 支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费、其它的则不能消费此消息了。当消费者不存在时,消息会一直保存,直到有消费消费。
创建生产者代码
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class Producter {
//ActiveMq 的默认用户名
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
//ActiveMq 的默认登录密码
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
//ActiveMQ 的链接地址
private static final String BROKEN_URL = "tcp://192.168.180.128:61616";
//链接工厂
ConnectionFactory connectionFactory;
//链接对象
Connection connection;
//事务管理
Session session;
//消息生产者
MessageProducer messageProducer;
public void init() {
try {
//创建一个链接工厂
connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEN_URL);
//从工厂中创建一个链接
connection = connectionFactory.createConnection();
//开启链接
connection.start();
//创建一个事务(通过参数可以设置事务的级别)
session = connection.createSession(true, Session.SESSION_TRANSACTED);
} catch (JMSException e) {
e.printStackTrace();
}
}
public void sendMessage(String disname) {
try {
//创建一个消息队列
Queue queue = session.createQueue(disname);
//消息生产者
messageProducer = session.createProducer(queue);
//创建一条消息
TextMessage msg = session.createTextMessage("message");
//发送消息
messageProducer.send(msg);
//提交事务
session.commit();
System.out.println("消息发送完成!");
} catch (Exception e) {
e.printStackTrace();
} finally {
//回收资源
if (messageProducer != null) {
try {
messageProducer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
消费者代码:
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class Comsumer {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
private static final String BROKEN_URL = "tcp://192.168.180.128:61616";
ConnectionFactory connectionFactory;
Connection connection;
Session session;
MessageConsumer consumer;
public void init() {
try {
connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEN_URL);
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
} catch (JMSException e) {
e.printStackTrace();
}
}
public void getMessage(String disname) {
try {
Queue queue = session.createQueue(disname);
consumer = session.createConsumer(queue);
TextMessage msg = (TextMessage) consumer.receive();
if (msg != null) {
msg.acknowledge();
System.out.println("Consumer:" + msg.getText());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (consumer != null) {
try {
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
生产者测试代码:
public class TestProducer {
public static void main(String[] args){
Producter producter = new Producter();
producter.init();
producter.sendMessage("queue1");
}
}
消费者测试代码:
public class TestConsumer {
public static void main(String[] args) {
Comsumer comsumer = new Comsumer();
comsumer.init();
comsumer.getMessage("queue1");
}
}
结果:
activeMq管理页面(启动消费者前)
启动消费者后:
7.Publish/Subscribe 处理模式(Topic )
消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到 topic 的消息会被所有订阅者消费。当生产者发布消息,不管是否有消费者。都不会保存消息一定要先有消息的消费者,后有消息的生产者。
生产者代码:
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* Desc:
* Created By zhangyh On 2018-10-13
*/
public class Producer {
//密码
private static final String PASSWORD = "admin";
//用户名
private static final String USERNAME = "admin";
//连接
private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
//连接工厂
ConnectionFactory connectionFactory;
//连接
Connection connection;
//回话
Session session;
//生产者
MessageProducer producer;
//topic主题目的地
Destination destination;
public void sendMessage() {
try {
connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, ACTIVEMQ_URL);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("topic1");
MessageProducer producer = session.createProducer(destination);
Message message = session.createTextMessage("data");
producer.send(message);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (producer != null) {
try {
producer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Producer producer = new Producer();
producer.sendMessage();
System.out.println("发送成功!!");
}
}
消费者代码:
import javax.jms.*;
/**
* Desc:
* Created By zhangyh On 2018-10-13
*/
public class Consumer {
//密码
private static final String PASSWORD = "admin";
//用户名
private static final String USERNAME = "admin";
//连接
private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
//连接工厂
ConnectionFactory connectionFactory;
//连接
Connection connection;
//回话
Session session;
//生产者
MessageConsumer consumer;
//topic主题目的地
Destination destination;
public void getMessage() {
try {
connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, ACTIVEMQ_URL);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("topic1");
MessageConsumer consumer = session.createConsumer(destination);
Message message = consumer.receive();
TextMessage textMessage = (TextMessage) message;
System.out.println(textMessage.getText());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (consumer != null) {
try {
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.getMessage();
}
}
先启动两个消费者对消息进行订阅
启动生产者发布消息:
activeMq控制台:
两种模式的对比:
8.activeMq的安全认证
9.activeMq的持久化
ActiveMQ 中,持久化是指对消息数据的持久化。在 ActiveMQ 中,默认的消息是保存在内存中的。当内存容量不足的时候,或 ActiveMQ 正常关闭的时候,会将内存中的未处理的消息持久化到磁盘中。具体的持久化策略由配置文件中的具体配置决定。
ActiveMQ 的默认存储策略是 kahadb。如果使用 JDBC 作为持久化策略,则会将所有的需要持久化的消息保存到数据库中。
所有的持久化配置都在 conf/activemq.xml 中配置,配置信息都在 broker 标签内部定义。
三种持久化方式:KAHADB、AMQ、JDBC
9.1 kahadb:
ActiveMQ 默认的持久化策略。kahadb 是一个文件型数据库。是使用内存+文件保证数据的持久化的。kahadb 可以限制每个数据文件的大小。不代表总计数据容量。特性是:1、日志形式存储消息;2、消息索引以 B-Tree 结构存储,可以快速更新;3、完全支持 JMS 事务;4、支持多种恢复机制
9.2 AMQ:
只适用于 5.3 版本之前。AMQ 也是一个文件型数据库,消息信息最终是存储在文件中。内存中也会有缓存数据。性能高于JDBC,写入消息时,会将消息写入日志文件,由于是顺序追加写,性能很高。为了提升性能,创建消息主键索引,并且提供缓存机制,进一步提升性能。每个日志文件的大小都是有限制的(默认 32m,可自行配置)。当超过这个大小,系统会重新建立一个文件。当所有的消息都消费完成,系统会删除这个文件或者归档。主要的缺点是 AMQ Message 会为每一个 Destination 创建一个索引,如果使用了大量的Queue,索引文件的大小会占用很多磁盘空间。而且由于索引巨大,一旦 Broker(ActiveMQ 应用实例)崩溃,重建索引的速度会非常慢。虽然 AMQ 性能略高于 Kaha DB 方式,但是由于其重建索引时间过长,而且索引文件
占用磁盘空间过大,所以已经不推荐使用。
9.3 JDBC:
ActiveMQ 将数据持久化到数据库中。 不指定具体的数据库。 可以使用任意的数据库
中
10.activeMq集群
11.activeMq,RabbitMq,RocketMq,kafka对比