消息队列中间件
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量 削锋等问题实现高性能,高可用,可伸缩和最终一致性架构
RabbitMQ简介
RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。 AMQP :Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。
架构图
概念
- RabbitMQ Server
也叫broker server,它是一种传输服务。 他的角色就是维护一条 从Producer到Consumer的路线,保证数据能够按照指定的方式进行传输。
- Producer
消息生产者,消息生产者连接RabbitMQ服 务器然后将消息投递到Exchange。
- Consumer
消息消费者,消息消费者订阅队列, RabbitMQ将Queue中的消息发送到消息消费者。
- Exchange
生产者将消息发送到Exchange(交换器),由Exchange将消息路由到一个 或多个Queue中(或者丢弃)。Exchange并不存储消息。RabbitMQ中的Exchange有 direct、fanout、topic、headers四种类型,每种类型对应不同的路由规则。
- Queue
(队列)是RabbitMQ的内部对象,用于存储消息。消息消费者就是通过订阅 队列来获取消息的,RabbitMQ中的消息都只能存储在Queue中,生产者生产消息并最终 投递到Queue中,消费者可以从Queue中获取消息并消费。多个消费者可以订阅同一个 Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者 都收到所有的消息并处理。
- RoutingKey
生产者在将消息发送给Exchange的时候,一般会指定一个routing key, 来指定这个消息的路由规则,而这个routing key需要与Exchange Type及binding key联 合使用才能最终生效。在Exchange Type与binding key固定的情况下(在正常使用时一 般这些内容都是固定配置好的),我们的生产者就可以在发送消息给Exchange时,通过 指定routing key来决定消息流向哪里。RabbitMQ为routing key设定的长度限制为255 bytes。
特性
- 可靠性(Reliability)
RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
- 灵活的路由(Flexible Routing)
在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
- 消息集群(Clustering)
多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker 。
- 高可用(Highly Available Queues)
队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。 5.多种协议(Multi-protocol) RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT 等等。
- 多语言客户端(Many Clients)
RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby 等等。
- 管理界面(Management UI)
RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方 面。
- 跟踪机制(Tracing)
如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
- 插件机制(Plugin System)
RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。
RabbitMQ安装(Windows)
因为RabbitMQ 是基于Erlang 语言开发的,要先安装Erlang 像安装JDK一样。
Erlang 下载地址:http://erlang.org/download/ 根据操作系统选择相应版本,下载完成安装直接下一步即可。
RabbitMQ 下载地址:http://www.rabbitmq.com/download.html 下载完成直接安装即可,避开中文安装目录
安装管理界面:
进入RabbitMQ安装目录的sbin目录,输入命令:rabbitmq‐plugins enable rabbitmq_management
找到Windows服务中的RabbitMQ服务重启即可,浏览器访问地址:http://localhost:15672/ 进入管理界面,用户名和密码为guest。
直接模式(Direct)
我们需要将消息发给唯一一个节点时使用这种模式,这是最简单的一种形式(默认使用)。
代码测试:
1、先在图形化界面创建一个队列mqtest:
Durability:是否做持久化 Durable(持久) transient(临时) Auto delete : 是否自动删除
2、创建一个Spring Boot项目rabbitmq_demo 引入如下依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wdg</groupId>
<artifactId>rabbitmq_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rabbitmq_demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties文件配置mq:
spring.rabbitmq.host=127.0.0.1
3、消息生产者实现:
package com.wdg.rabbitmq;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqTest {
//注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void doMQTest() {
//直接模式可以看成没有通过交换器Exchange直接与Queue绑定,发送消息只需要带上参数routingKey, object
rabbitTemplate.convertAndSend("mqtest","MQ测试");
}
}
运行测试类:发送消息
4、消息消费者实现:
package com.wdg.rabbitmq;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @author WDG
* @date 2019-2-15
*/
@Component
@RabbitListener(queues="mqtest")//绑定队列
public class MQConsumer1 {
@RabbitHandler
public void doHandler(String message){
System.out.println("接收到消息:"+message);
}
}
运行启动类,MQConsumer1监听到mqtest队列的消息后消费:
分列模式(Fanout)
当我们需要将消息一次发给多个队列时,需要使用这种模式。
任何发送到Fanout Exchange的消息都会被转发到与该Exchange绑定(Binding)的所有 Queue上。
1.可以理解为路由表的模式
2.这种模式不需要RouteKey
3.这种模式需要提前将Exchange与Queue进行绑定,一个Exchange可以绑定多个 Queue,一个Queue可以同多个Exchange进行绑定。
4.如果接受到消息的Exchange没有与任何Queue绑定,则消息会被抛弃。
代码实现:
1、创建两个队列,man、women
2、创建一个交换器,type选择fanout类型:person
3、将man、women两个队列绑定到交换器person上:
4、消息生产者测试方法:
@Test
public void doFanoutTest(){
rabbitTemplate.convertAndSend("person","","分裂模式测试");
}
运行测试方法:
5、消息消费者 consumer2:
package com.wdg.rabbitmq;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @author WDG
* @date 2019-2-15
*/
@Component
@RabbitListener(queues="man")
public class MQConsumer2 {
@RabbitHandler
public void manQueues(String message){
System.out.println("man接收到消息:"+message);
}
}
6、消息消费者consumer3:
package com.wdg.rabbitmq;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @author WDG
* @date 2019-2-15
*/
@Component
@RabbitListener(queues="women")
public class MQConsumer3 {
@RabbitHandler
public void womenQueues(String message){
System.out.println("women接收到消息:"+message);
}
}
运行启动类:
主题模式(Topic)
任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue 上
1.这种模式较为复杂,简单来说,就是每个队列都有其关心的主题,所有的消息都带有一 个“标题”(RouteKey),Exchange会将消息转发到所有关注主题能与RouteKey模糊匹配的 队列。
2.这种模式需要RouteKey,也许要提前绑定Exchange与Queue。
3.在进行绑定时,要提供一个该队列关心的主题,如“#.hello.#”表示该队列关心所有涉及 hello的消息(一个RouteKey为”MQ.hello.Success”的消息会被转发到该队列)。
4.“#”表示0个或若干个关键字,“”表示一个关键字。如“hello.”能与“hello.world”匹配,无法 与“hello.world.ok”匹配;但是“hello.#”能与上述两者匹配。
5.同样,如果Exchange没有发现能够与RouteKey匹配的Queue,则会抛弃此消息
1、创建一个交换器topictest,type类型选择topic类型:
2、将man、women、mqtest队列与topictest交换器绑定 ,定义RouteKey规则:
3、主题模式消息生产者1测试:
@Test
public void testSendTopic1(){
rabbitTemplate.convertAndSend("topictest","hello.world","主题模式测试");
}
控制台(三个队列都匹配消费了消息):
4、主题模式消息生产者2测试:
@Test
public void testSendTopic2(){
rabbitTemplate.convertAndSend("topictest","hello.ok","主题模式测试");
}
控制台(只有women队列匹配成功并消费了消息):
5、主题模式消息生产者3测试:
@Test
public void testSendTopic3(){
rabbitTemplate.convertAndSend("topictest","error.world","主题模式测试");
}
控制台(只有man队列匹配成功并消费了消息):