一、概述:
消息中间件属于分布式系统中的一个子系统,关注数据发送和接收,利用高效可靠异步消息传递机制对分布式系统中的其余各个子系统进行集成。
二、与RPC的区别:
RPC(Remote Procedure Call—远程过程调用),他是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络的技术
三、应用场景:
异步处理,应用解耦,流量削峰,日志处理,消息通讯
四、常见中间件比较:
| ActiveMQ | RabbitMQ | RocketMQ | Kafka |
性能(单台) | 6000+ | 万级(12000+) | 十万级 | 百万级 |
消息持久化 | 支持 | 支持 | 支持 | 支持 |
多语言支持 | 支持 | 支持 | 很少 | 支持 |
社区活跃度 | 高 | 高 | 中 | 高 |
支持协议 | 多(JMS,AMQP….. ) | 多(AMQP,STOMP,MQTT….. ) | 少 | 少 |
综合评价 | 优点: 成熟,已经在很多公司得到应用。较多的文档。各种协议支持较好,有多个语言的成熟客户端。 缺点: 性能较弱。缺乏大规模吞吐的场景的应用,有江河日下之感。 | 优点:性能较好,管理界面较丰富,在互联网公司也有较大规模的应用,有多个语言的成熟客户端。 缺点: 内部机制很难了解,也意味很难定制和掌控。集群不支持动态扩展。 | 优点:模型简单,接口易用。在阿里有大规模应用。分布式系统,性能很好,版本更新很快。 缺点:文档少,支持的语言较少,尚未主流。 | 优点:天生分布式,性能最好,所以常见用于大数据领域。 缺点:运维难度大,偶尔有数据混乱的情况,对ZooKeeeper强依赖。多副本机制下对带宽有一定的要求。 |
五、JSM规范:
包含要素:连接工厂 , JMS目的 , JMS连接 , JMS会话, JMS生产者 , JMS消费者
消息模型:
Point-to-Point(p2p)/ 点对点,消息生产者发送的消息,只有一个消费者将消息消费,消息发送没有消费者消费将持久化
Topic / 主题(发布订阅),生产者发送消息,所有消费者都获得消息,消息不会持久化到磁盘
消息类型:TextMessage,ByteMessage,ObjectMessage,MapMessage,StreamMessage
TextMessage 主体包含字符串
BytesMessage 主体包含连续字节流
ObjectMessage 主体包含对象
MapMessage 主体包含键值对
StreamMessage主体包含流
六、安装,部署,运行:
下载地址:http://activemq.apache.org/download.html
Windows安装:解压后进入bin 目录,直接运行activemq.bat
浏览器输入:http://127.0.0.1:8161/admin/ ,用户名密码admin/admin
Linux安装一样,解压后进入bin目录,运行 ./activemq start 启动 ./activemq stop 停止
注意开放61616端口,这是ActiveMQ的服务端口
七、ActiveMQ原生API
添加依赖:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.8.0</version>
</dependency>
消息生产者:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class Producter {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
private static final String BROKERURL = "failover://tcp://192.168.244.2:61616";
private static final String MESSAGE_TOPIC = "hello-ActiveMQ";
public static void main(String[] args) {
//连接工厂
ConnectionFactory factory;
//连接
Connection connection = null;
//会话
Session session;
//目的地
Destination destination;
//生产者
MessageProducer producer ;
//获取连接工厂
factory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKERURL);
try {
//获取连接,并启动
connection = factory.createConnection();
connection.start();
//创建会话,参数1:是否启用事务,参数2:是否自动确认
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//消息目的地
//destination = session.createTopic(MESSAGE_TOPIC);
destination = session.createQueue(MESSAGE_TOPIC);
//生产者
producer = session.createProducer(destination);
//循环发送
for(int i = 0;i<3;i++) {
String msg = "消息: "+(i+1)+" "+System.currentTimeMillis();
TextMessage message = session.createTextMessage(msg);
producer.send(message);
}
} catch (JMSException e) {
e.printStackTrace();
}finally {
if(connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
消息消费者—同步模式
consumer.receive()是阻塞方法,如果没有接收到消息,就是一直阻塞,直到接收到消息
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class SyncConsumer {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
private static final String BROKERURL = "failover://tcp://192.168.244.2:61616";
private static final String MESSAGE_TOPIC = "hello-ActiveMQ";
public static void main(String[] args) {
//连接工厂
ConnectionFactory factory;
//连接
Connection connection = null;
//会话
Session session;
//目的地
Destination destination;
//消费者
MessageConsumer consumer;
//获取连接工厂
factory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKERURL);
try {
//获取连接,并启动
connection = factory.createConnection();
connection.start();
//创建会话,参数1:是否启用事务,参数2:是否自动确认
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//消息目的地
//destination = session.createTopic(MESSAGE_TOPIC);
destination = session.createQueue(MESSAGE_TOPIC);
//创建消费者
consumer = session.createConsumer(destination);
TextMessage text ;
while((text = (TextMessage)consumer.receive())!=null) {
System.out.println("同步接收消息:"+text.getText());
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
消息消费者—异步模式
消息监听的方式
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class AyncConsumer {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
private static final String BROKERURL = "failover://tcp://192.168.244.2:61616";
private static final String MESSAGE_TOPIC = "hello-ActiveMQ";
public static void main(String[] args) {
//连接工厂
ConnectionFactory factory;
//连接
Connection connection = null;
//会话
Session session;
//目的地
Destination destination;
//消费者
MessageConsumer consumer;
//获取连接工厂
factory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKERURL);
try {
//获取连接,并启动
connection = factory.createConnection();
connection.start();
//创建会话,参数1:是否启用事务,参数2:是否自动确认
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//消息目的地
//destination = session.createTopic(MESSAGE_TOPIC);
destination = session.createQueue(MESSAGE_TOPIC);
//创建消费者
consumer = session.createConsumer(destination);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
try {
String msg = ((TextMessage)message).getText();
System.out.println("异步接收消息:"+msg);
} catch (JMSException e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
}
}
}
八、与springboot集成,利用JmsTemplate来发送消息:
目录结构:采用父子工程,依赖全部在父工程定义
父工程pom文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>
<dependencies>
<!-- web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
</dependency>
</dependencies>
<modules>
<module>ActiveMQ-noSpring</module>
<module>ActiveMQ-P</module>
<module>ActivqMQ-C</module>
</modules>
生产者模块:ActiveMQ-P
application.yml文件配置:
server:
port: 8080
spring:
activemq:
user: admin
password: admin
broker-url: tcp://192.168.244.2:61616
pool:
enabled: true
配置类:将工厂类注入即可,由于配置连接池,spring会从工厂里自动获取 jmsTemplate
@Configuration
@EnableJms
public class ActiveMQConfig {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
private static final String BROKERURL = "failover://tcp://192.168.244.2:61616";
private static final String TOPIC_NAME = "activemq-topic";
private static final String QUEUE_NAME = "activemq-queue";
@Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory factory = new
ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKERURL);
return factory;
}
@Bean("activeMQTopic")
public Destination ActiveMQTopic() {
Destination queue = new ActiveMQTopic(TOPIC_NAME);
return queue;
}
@Bean("activeMQQueue")
public Destination ActiveMQQueue() {
Destination queue = new ActiveMQQueue(QUEUE_NAME);
return queue;
}
发送消息:两种模式,按名称注入
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/activemq")
public class ProducterController {
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
@Qualifier("activeMQTopic")
private Destination destinationTopic;
@Autowired
@Qualifier("activeMQQueue")
private Destination destinationQueue;
@Autowired
private JmsMessagingTemplate template;
// template 和 jmsTemplate 本质没区别,jmsTemplate 可做更细化操作
@RequestMapping("sendTopic")
public void sendTopicMessage() {
String message = "TOPIC消息内容:"+System.currentTimeMillis();
System.out.println("发送消息:"+message);
template.convertAndSend(destinationTopic,message);
}
@RequestMapping("sendQueue")
public void sendQueueMessage(Message msg) {
String message = "QUEUE消息内容:"+System.currentTimeMillis();
System.out.println("发送消息:"+message);
jmsTemplate.convertAndSend(destinationTopic, message);
jmsTemplate.convertAndSend(destinationTopic, msg);
}
消费者模块:ActiveMQ-C
application.yml配置:启动端口要改,其余不用改
server:
port: 8090
spring:
activemq:
user: admin
password: admin
broker-url: tcp://192.168.244.2:61616
pool:
enabled: true
配置类:
import javax.jms.Destination;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
@Configuration
@EnableJms
public class ConsumerConfig {
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
private static final String BROKERURL = "failover://tcp://192.168.244.2:61616";
private static final String TOPIC_NAME = "activemq-topic";
private static final String QUEUE_NAME = "activemq-queue";
@Autowired
private ActiveMQConnectionFactory factory;
@Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory factory =
new ActiveMQConnectionFactory(USERNAME,PASSWORD, BROKERURL);
return factory;
}
@Bean
public JmsListenerContainerFactory<?> JmsListenerContainerFactoryTopic(){
DefaultJmsListenerContainerFactory listener =
new DefaultJmsListenerContainerFactory();
listener.setConnectionFactory(factory);
//监听发布订阅模式TOPIC
listener.setPubSubDomain(true);
return listener;
}
@Bean
public JmsListenerContainerFactory<?> JmsListenerContainerFactoryQueue(){
DefaultJmsListenerContainerFactory listener =
new DefaultJmsListenerContainerFactory();
listener.setConnectionFactory(factory);
//监听点对点模式QUEUE
listener.setPubSubDomain(false);
return listener;
}
@Bean("activeMQTopic")
public Destination ActiveMQTopic() {
Destination queue = new ActiveMQTopic(TOPIC_NAME);
return queue;
}
@Bean("activeMQQueue")
public Destination ActiveMQQueue() {
Destination queue = new ActiveMQQueue(QUEUE_NAME);
return queue;
}
接收消息:
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Component
public class ConsumerService {
private static final String TOPIC_NAME = "activemq-topic";
private static final String QUEUE_NAME = "activemq-queue";
@JmsListener(destination=ConsumerService.TOPIC_NAME,
containerFactory="JmsListenerContai nerFactoryTopic")
public void reviceTopic(String text) {
System.out.println("接收消息:"+text);
}
@JmsListener(destination=ConsumerService.QUEUE_NAME,
containerFactory="JmsListenerContai nerFactoryQueue")
public void reviceQueue(String text) {
System.out.println("接收消息:"+text);
}
}
启动项目,访问 localhost:8080/activemq/sendTopic,则以topic模式发送消息
localhost:8080/activemq/sendQueue,以queue模式发送消息
控制台输出接收到的消息。