文章目录
1、基本介绍
1.1 什么是MQ?
MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。
RabbitMQ官方地址:http://www.rabbitmq.com/
开发中消息队列通常有如下应用场景:
1、任务异步处理:将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理,提高了应用程序的响应时间。
2、应用程序解耦合:MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。
还有其他常用的消息队列,比如:ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ、Redis等。
1.2 什么是AMQP?
AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。
总结: AMQP是一套公开的消息队列协议,最早在2003年被提出,它旨在从协议层定义消息通信数据的标准格式,为的就是解决MQ市场上协议不统一的问题。RabbitMQ就是遵循AMQP标准协议开发的MQ服务。
AMQP官方网站:http://www.amqp.org/
1.3 什么是JMS?
JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
JMS是java提供的一套消息服务API标准,其目的是为所有的java应用程序提供统一的消息通信的标准,类似java的jdbc,只要遵循jms标准的应用程序之间都可以进行消息通信。它和AMQP有什么 不同,jms是java语言专属的消息服务标准,它是在api层定义标准,并且只能用于java应用;而AMQP是在协议层定义的标准,是跨语言的 。
2、RabbitMQ的安装
2.1 说明
RabbitMQ由Erlang语言开发,Erlang语言用于并发及分布式系统的开发,在电信领域应用广泛,OTP(Open Telecom Platform)作为Erlang语言的一部分,包含了很多基于Erlang开发的中间件及工具库,安装RabbitMQ需要安装Erlang/OTP,并保持版本匹配。
https://www.rabbitmq.com/which-erlang.html#compatibility-matrix这个链接可以查询Erlang和RabbitMQ的版本对应关系,本篇入门选择的是:RabbitMQ3.8.3 + Erlang21.3。
Erlang下载地址:https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.3/rabbitmq-server-3.8.3.exe
RabbitMQ下载地址:http://erlang.org/download/otp_win64_21.3.exe
2.2 启动相关服务
cmd进入到RabbitMQ的安装目录下的/sbit,可以执行以下命令:
1、安装服务:rabbitmq-service.bat install
2、停止服务:rabbitmq-service.bat stop
3、启动服务:rabbitmq-service.bat start
RabbitMQ还提供了web端的管理页面,只需要输入命令rabbitmq-plugins.bat enable rabbitmq_management
即可开启。
2.3 管理页面
浏览器输入:http://localhost:15672
账号:guest
密码:guest
登陆后可以看到:
3、RabbitMQ工作原理
3.1 基本结构
3.2 组成说明
Broker:最终将粉红色的框就是Rabbit的核心服务进程,包括两个部分:Exchange和Queue。
Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。
Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方。
Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ。
Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。
3.3 消息发布和接收流程
发送消息:
1、生产者和Broker建立TCP连接。
2、生产者和Broker建立通道。
3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。
4、Exchange将消息转发到指定的Queue(队列)。
接收消息:
1、消费者和Broker建立TCP连接
2、消费者和Broker建立通道
3、消费者监听指定的Queue(队列)
4、当有消息到达Queue时Broker默认将消息推送给消费者。
5、消费者接收到消息。
4、HelloWorld
创建一个入门案例,生产者发送一条消息到RabbitMQ,然后消费者从RabbitMQ获取这条消息。
4.1 环境依赖
创建两个maven普通java工程,分别为生产者和消费者,同时导入以下依赖:
<dependencies>
<!--RabbitMQ客户端-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.9.0</version>
</dependency>
<!--日志组件-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
4.2 生产者代码
package cn.klb.rabbitmq.producer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @Author: Konglibin
* @Description:
* @Date: Create in 2020/5/19 17:49
* @Modified By:
*/
public class ProducerDemo01 {
private static final String QUEUE = "queueOfKlb";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
// 1.获取连接
Connection connection = factory.newConnection();
// 2.创建通道
Channel channel = connection.createChannel();
// 3.声明队列
channel.queueDeclare(QUEUE, true, false, false, null);
String msg = "helloworld立斌" + System.currentTimeMillis();
// 4.发布消息
channel.basicPublish("", QUEUE, null, msg.getBytes());
System.out.println("发送的消息是:" + msg);
}
}
4.3 消费者代码
package cn.klb.rabbitmq.consumer;
import java.io.IOException;
import com.rabbitmq.client.*;
/**
* @Author: Konglibin
* @Description:
* @Date: Create in 2020/5/19 18:58
* @Modified By:
*/
public class ConsumerDemo01 {
private static final String QUEUE = "queueOfKlb";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
// 1.获取连接
Connection connection = factory.newConnection();
// 2.创建通道
Channel channel = connection.createChannel();
// 3.声明队列
channel.queueDeclare(QUEUE, true, false, false, null);
// 定义消费方法
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "utf-8");
System.out.println("收到的消息是:" + msg);
}
};
channel.basicConsume(QUEUE, true, consumer);
}
}
4.4 测试
运行生产者,可以从RabbitMQ的web端看到:
1、有一个连接存在:
有一个通道存在:
2、有一个队列,里面有一条消息等待发送:
启动消费者代码后,可以看到:
1、消费者控制台把收到的信息打印出来:
2、队列里面的消息已经发送了:
5、RabbitMQ的工作模式
代码示例:https://github.com/Meumax/RabbitMQDemo.git
5.1 Work queues
工作队列模式,流程图如下所示:
即一个生产者生产消息发给一个队列,这个队列连接两个消费者,当生产者发送消息时,只有其中一个消费者能收到消息。对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
当生产者发送多条消息时,RabbitMQ采用轮循的方式把消息平均发送给消费者,消费者在处理完某条消息后,才会收到下一条消息。
5.2 Publish/subscribe
发布订阅模式,流程图如下所示:
每个消费者都监听自己的队列,生产者把消息发送给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都会接收到消息。
5.3 Routing
路由模式,流程图如下所示:
1、每个消费者监听自己的队列,并且设置routingkey。
2、生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。
5.4 Topics
通配符模式,流程图如下所示:
1、每个消费者监听自己的队列,并且设置带统配符的routingkey。
2、生产者将消息发给broker,由交换机根据routingkey来转发消息到指定的队列。
特别说明:#
表示匹配一个或多个词,由.
分隔,比如inform.#.email.#
可以匹配inform.email
、inform.sms.email
;*
只能匹配一个词,比如inform.*
可以匹配inform.sms
,不能匹配inform.sms.email
。
5.5 Header
header模式与routing不同的地方在于,header模式取消routingkey,使用header中的 key/value(键值对)匹配队列。
// 交换机和队列绑定
Map<String,Object> headers_email = new Hashtable<String,Object>();
headers_email.put("inform_email","email");
Map<String,Object> headers_sms = new Hashtable<String,Object>();
headers_sms.put("inform_sms","sms");
channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_HEADERS_INFORM, "",headers_email);
channel.queueBind(QUEUE_INFORM_SMS, EXCHANGE_HEADERS_INFORM, "",headers_sms);
5.6 RPC
即客户端远程调用服务端模式,工作流程图如下:
1、客户端即是生产者就是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。
2、服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果
3、服务端将RPC方法 的结果发送到RPC响应队列
4、客户端(RPC调用方)监听RPC响应队列,接收到RPC调用结果。
6、SpringBoot整合RabbitMQ
1、导入starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、编写application.yaml配置文件:
server:
port: 44000
spring:
application:
name: test‐rabbitmq‐producer
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
passowrd: guest
virtualHost: /
3、编写RabbitMQ配置类:
@Configuration
public class RabbitmqConfig {
public static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
public static final String QUEUE_INFORM_SMS = "queue_inform_sms";
public static final String EXCHANGE_TOPICS_INFORM = "exchange_topics_inform";
/**
* 交换机配置
* @return
*/
@Bean(EXCHANGE_TOPICS_INFORM)
public Exchange getExchange(){
//durable(true)持久化,消息队列重启后交换机仍然存在
return ExchangeBuilder.topicExchange(EXCHANGE_TOPICS_INFORM).durable(true).build();
}
/**
* 声明 SMS 队列
* @return
*/
@Bean(QUEUE_INFORM_SMS)
public Queue getQueueSMS(){
return new Queue(QUEUE_INFORM_SMS);
}
/**
* 声明 Email 队列
* @return
*/
@Bean(QUEUE_INFORM_EMAIL)
public Queue getQueueEmail(){
return new Queue(QUEUE_INFORM_EMAIL);
}
/**
* 绑定 sms 队列到交换机
* @param queue
* @param exchange
* @return
*/
@Bean
public Binding bingSmsToExchange(@Qualifier(QUEUE_INFORM_SMS) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("inform.#.sms.#").noargs();
}
/**
* 绑定 email 队列到交换机
* @param queue
* @param exchange
* @return
*/
@Bean
public Binding bingEmailToExchange(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("inform.#.email.#").noargs();
}
}
4、生产者:
@SpringBootTest
class SpringBootRabbitmqProducerApplicationTests {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
void testSendMessage() {
for (int i = 0; i < 5; i++) {
String msg = "sms email inform to user" + i;
rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_TOPICS_INFORM,"inform.sms.email",msg);
System.out.println(msg);
}
}
}
5、消费者:
@Component
public class ReveiveHandler {
/**
* 监听 Email 队列
*
* @param msg
* @param message
* @param channel
*/
@RabbitListener(queues = {RabbitmqConfig.QUEUE_INFORM_EMAIL})
public void receive_email(String msg, Message message, Channel channel) {
System.out.println("emial queue:" + msg);
}
/**
* 监听 SMS 队列
*
* @param msg
* @param message
* @param channel
*/
@RabbitListener(queues = {RabbitmqConfig.QUEUE_INFORM_SMS})
public void receive_sms(String msg, Message message, Channel channel) {
System.out.println("sms queue:" + msg);
}
}