一、关于中间件
(1)什么是中间件
- 非底层操作系统软件,非业务应用软件,不是直接给最终用户使用的,不能直接给客户带来价值的软件统称为中间件
- 关注于数据的发送和接收,利用高效可靠的异步消息传递机制集成分布式系统。
(2)消息中间件有什么好处
简单来说,使用中间件最主要的好处有三点
- 解耦
- 异步
- 横向扩展、使用较为灵活
(3)内容扩展JMS和AMQP
提到消息中间件,这里有两个名词需要了解下,就是JMS和AMQP
什么是JMS?
JMS(Java MessageService)实际上是指JMS API。JMS是由Sun公司早期提出的消息标准,旨在为java应用提供统一的消息操作,包括create、send、receive 等。JMS已经成为Java Enterprise Edition的一部分。从使用角度看,JMS和JDBC担任差不多的角色,用户都是根据相应的接口可以和实现了JMS的服务进行通信,进行相关的操作。
什么是AMQP?
AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。这使得实现了AMQP的provider天然性就是跨平台的。意味着我们可以使用Java的AMQP provider,同时使用一个python的producer加一个rubby的consumer。从这一点看,AQMP可以用http来进行类比,不关心实现的语言,只要大家都按照相应的数据格式去发送报文请求,不同语言的client均可以和不同语言的server链接。
目前AMQP逐渐成为消息队列的一个标准协议,当前比较流行的rabbitmq、stormmq都使用了AMQP实现。
最后将JMS和AMQP的各项对比如下:
JMS | AMQP | |
定义 | Java api | Wire-protocol |
跨语言 | 否 | 是 |
跨平台 | 否 | 是 |
Model | 提供两种消息模型: (1)、Peer-2-Peer (2)、Pub/sub | 提供了五种消息模型: (1)、direct exchange (2)、fanout exchange (3)、topic change (4)、headers exchange (5)、system exchange 本质来讲,后四种和JMS的pub/sub模型没有太大差别,仅是在路由机制上做了更详细的划分; |
支持消息类型 | 多种消息类型: TextMessage MapMessage BytesMessage StreamMessage ObjectMessage Message (只有消息头和属性) | byte[] 当实际应用时,有复杂的消息,可以将消息序列化后发送。 |
综合评价 | JMS 定义了JAVA API层面的标准;在java体系中,多个client均可以通过JMS进行交互,不需要应用修改代码,但是其对跨平台的支持较差; | AMQP定义了wire-level层的协议标准;天然具有跨平台、跨语言特性。 |
二、常用的中间件对比
- ActiveMQ
ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ是一个完全支持JMS1.1个J2EE规范的JMS Provider实现
- RabbitMQ
RabbitMQ是一个开源的AMQP实现,服务端采用Erlang语言编写。用于分布式系统中的存储消息转发,在易用性、扩展性、高可用性等方面表现不俗
- Kafka
Kafka是一种高吞吐量的分布式发布订阅消息系统,是一个分布式的、分区的、可靠的的分布式日志储存系统。他通过一种独特的设计提供了一个消息系统的功能
接下来我们对比一下这三种消息中间件的优劣及综合评级如下图
三、JMS规范讲解
(1)JMS规范详解
既然我们本文主要讲解ActiveMQ,那先在这说一下JMS规范,毕竟他是基于JMS实现的,先说一下JMS的相关概念:
- 提供者:实现JMS规范的消息中间件服务器
- 客户端:发送或接收消息的应用程序
- 生产者/发布者:创建并发送消息的的客户端
- 消费者/订阅者:接受并处理消息的客户端
- 消息:应用程序之间传递的数据内容
- 消息模式:在客户端之间消息传递的方式,JMS中定义了主题和队列两种模式
上面提到了JMS实现消息队列有两种模式,接下来就讲解下这两种模式的区别
(2)JSM消息模式
队列模式
- 客户端包括生产者和消费者
- 队列中的消息只能被一个消费者消费
- 消费者可以随时消费队列中的消息
主题模式
- 客户端包括发布者和订阅者
- 主题中的消息被所有订阅者消费
- 消费者不能消费订阅之前就发布的消息
三、使用ActiveMQ实现JMS的两种消息模式
(1)环境搭建
理论部分我们已经讲解完了,现在需要实现两种消息模式,首先我们准备消息服务器环境
- 首先下载消息服务器ActiveMQ
ActiveMQ官网下载地址:http://activemq.apache.org/download.html
- 接下来准备开发工具,我这里用的是win10环境下的idea
(2)实现队列消息模式
1、打开idea新建一个普通的Maven项目
2、填写项目信息
3、项目新建完成后我们先来了解下这几个常用的对象
4、开始编码,新建一个queue包,在包下创建AppProducer类(消息生产者),具体代码如下:
很简单,就是创建一个消息生产者,循环发送消息
package vip.ablog.jms.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class AppProducer {
private static final String url = "tcp://127.0.0.1:61616";
private static final String queueName = "queue-test";
public static void main(String[] args) throws JMSException {
Connection connection = null;
//1、创建connectionFactory连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
//2、创建Connection
connection = connectionFactory.createConnection();
//3、启动连接
connection.start();
//4、创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5、创建一个目标
Destination destination = session.createQueue(queueName);
//6、创建生产者
MessageProducer producer = session.createProducer(destination);
//7、开始发送消息
for (int i = 0; i < 100; i++) {
//创建消息
TextMessage msg = session.createTextMessage("test:" + i);
//发布消息
producer.send(msg);
System.out.println("发送消息:" + msg.getText());
}
connection.close();
}
}
6、生产者创建后接下来我们在queue包下创建AppConsumer类(消息的消费者),以接收生产者发送的消息,具体代码如下:
package vip.ablog.jms.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/***
* 消息的消费者
*/
public class AppConsumer {
private static final String url = "tcp://127.0.0.1:61616";
private static final String queueName = "queue-test";
public static void main(String[] args) throws JMSException {
Connection connection = null;
//1、创建connectionFactory连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
//2、创建Connection
connection = connectionFactory.createConnection();
//3、启动连接
connection.start();
//4、创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5、创建一个目标
Destination destination = session.createQueue(queueName);
//6、创建消费者
MessageConsumer consumer = session.createConsumer(destination);
//创建一个监听器
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
try {
System.out.println("接收到消息:"+((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
//connection.close();
}
}
7、接下来代码我们就写完了,可以发现这两个类是包含mian方法的,先不用管他,接下来然我们配置刚刚下载好的ActiveMQ,下载完成后解压即可使用,目录结构如下:
我们进入bin目录下,进入对应系统位数的目录下,运行activemq.bat文件,不要关闭窗口
8、运行后,在浏览器输入下面地址,用户名密码默认都是admin,即可进入ActiveMQ的后台管理系统
9、现在我们进入idea 运行我们的消息发布者AppProducer,运行后进入Queue队列菜单即可看到我们发送的消息
10、现在我们运行AppConsumer类,来消费接受这些消息,运行后会在控制台打印消息内容,接下来我们进入管理页面会看到,之前消息队列中等待的消息已经被消费
(3)实现主题消息模式
1、现在我们新建topic包,在包下新建AppProducer和AppConsumer类,来创建订阅者和消费者,具体代码如下:
AppProducer.java
package vip.ablog.jms.topic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class AppProducer {
private static final String url = "tcp://127.0.0.1:61616";
private static final String topicame = "topic-test";
public static void main(String[] args) throws JMSException {
Connection connection = null;
//1、创建connectionFactory连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
//2、创建Connection
connection = connectionFactory.createConnection();
//3、启动连接
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5、创建一个目标
Destination destination = session.createTopic(topicame);
//6、创建生产者
MessageProducer producer = session.createProducer(destination);
//7、开始发送消息
for (int i = 0; i < 100; i++) {
//创建消息
TextMessage msg = session.createTextMessage("test:" + i);
//发布消息
producer.send(msg);
System.out.println("发送消息:" + msg.getText());
}
connection.close();
}
}
AppConsumer.java
package vip.ablog.jms.topic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/***
* 消息的消费者
*/
public class AppConsumer {
private static final String url = "tcp://127.0.0.1:61616";
private static final String topicName = "topic-test";
public static void main(String[] args) throws JMSException {
Connection connection = null;
//1、创建connectionFactory连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
//2、创建Connection
connection = connectionFactory.createConnection();
//3、启动连接
connection.start();
//4、创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5、创建一个目标
Destination destination = session.createTopic(topicName);
//6、创建消费者
MessageConsumer consumer = session.createConsumer(destination);
//创建一个监听器
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
try {
System.out.println("接收到消息:"+((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
//connection.close();
}
}
接下来我们我们像上面队列模式一样运行:
首先运行AppProducer消息发布者,发送100条消息
然后运行AppConsumer消息订阅者,
然后你会惊奇的发现,毛都没收到,哈哈
没收到就对了!
上面我们提到过,在主题模式下,我们不能接收未订阅之前的消息
换言之,就是说我发消息时你订阅者也要在运行,不然肯定收不到,没订阅要啥自行车!
还有:上面还提到过队列模式和主题模式的区别,这个由于篇幅问题先不讲解了,你们自己实践下就行,就是在主题模式和队列模式下开启多个消息的消费者,然后发送消息就会看到他俩的另一个区别!