Spring boot集成Rabbit MQ使用初体验

1.rabbit mq基本特性

首先介绍一下rabbitMQ的几个特性

Asynchronous Messaging

Supports multiple messaging protocols, message queuing, delivery acknowledgement, flexible routing to queues, multiple exchange type.

异步消息

支持多种消息传递协议,消息排队,传递确认,灵活路由规则,多种交换类型。这些应该是rabbitmq最核心的特性了。

Developer Experience

Deploy with BOSH, Chef, Docker and Puppet. Develop cross-language messaging with favorite programming languages such as: Java, .NET, PHP, Python, JavaScript, Ruby, Go, and many others.

部署体验?

与BOSH,Chef,Docker和Puppet一起部署。使用喜欢的编程语言来开发跨语言消息传递,例如Java,.NET,PHP,Python,JavaScript,Ruby,Go等。

Distributed Deployment

Deploy as clusters for high availability and throughput; federate across multiple availability zones and regions.

分布式部署

部署为集群以实现高可用性和吞吐量;跨多个可用区域和区域联合。

Enterprise & Cloud Ready

Pluggable authentication, authorisation, supports TLS and LDAP. Lightweight and easy to deploy in public and private clouds.

企业和云就绪

可插拔身份验证,授权,支持TLS和LDAP。轻巧且易于在公共和私有云中进行部署。

Tools & Plugins

Diverse array of tools and plugins supporting continuous integration, operational metrics, and integration to other enterprise systems. Flexible plug-in approach for extending RabbitMQ functionality.

工具&插件

工具和插件的种类繁多,支持持续集成,运营指标以及与其他企业系统的集成。灵活的插件方法,用于扩展RabbitMQ功能。

Management & Monitoring

HTTP-API, command line tool, and UI for managing and monitoring RabbitMQ.

管理和监控

HTTP-API支持,命令行工具,管理和监控界面。

2.rabbit mq核心概念

①Message

消息,消息就是数据的载体,由消息头和消息体组成。消息体是不透明的,而消息头由一系列的可选属性组成,这些属性包括routing-key(路由键,也就是消息是如何分发给队列的),priority(相对于其它消息的优先权),delivery-mode(指定是否需要持久化存储)

②Publisher

消息的生产者,向交换机发布消息的客户端应用程序。

③Exchange

交换机用来接收生产者发送的消息并将这些消息按照路由规则或者交换机类型路由到指定的队列。交换机有4种类型:direct(默认),fanout,topic,以及headers,这四种类型支持不同的路由策略。

④Queue

消息队列,用于保存消息直到发送给消费者,是消息的容器。一个消息可以存入一个或多个队列,一直到消费者消费这个消息,才会从队列中删除。

⑤Binding

绑定,指定交换机和队列的绑定规则,可以理解为一个过滤器,当路由键符合这个绑定规则时,就会将消息发送给队列。交换机和队列之间的绑定可以是多对多的关系

⑥Connection

一个TCP连接

⑦Channel

信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。

⑧Consumer

消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

⑨Virtual Host

虚拟主机,表示一批交换机、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。

⑩Broker

表示消息队列服务器实体。

更详细的说明请参考官方文档:https://www.rabbitmq.com/tutorials/amqp-concepts.html

3.交换机类型和消息路由

  • Direct Exchange

消息中的路由键(routing key)如果和 Binding中的 binding key 一致,交换器就将消息发到对应的队列中。路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为“dog”,则只转发 routing key 标记为“dog”的消息,不会转发“dog.puppy”,也不会转发“dog.guard”等等。它是完全匹配、单播的模式。

  • Fanout Exchange

每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键【路由键被忽略】,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。

  • Topic Exchange

topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号“#”和符号“

注意#匹配0个或者多个单词,*匹配一个单词

4.开始使用

我们先看spring boot的官方文档是怎么说的吧。

首先,添加这些配置

spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /test

rabbitmq默认用户名密码为guest:guest

先上配置类,RabbitTemplate使用自动配置好的,自动注入进来就可以了,我们还需要配置一个RabbitAdmin对象,RabbitAdmin有两个构造方法

/**
* Construct an instance using the provided {@link ConnectionFactory}.
* @param connectionFactory the connection factory - must not be null.
*/
public RabbitAdmin(ConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
this.connectionFactory = connectionFactory;
this.rabbitTemplate = new RabbitTemplate(connectionFactory);
} /**
* Construct an instance using the provided {@link RabbitTemplate}. Use this
* constructor when, for example, you want the admin operations to be performed within
* the scope of the provided template's {@code invoke()} method.
* @param rabbitTemplate the template - must not be null and must have a connection
* factory.
* @since 2.0
*/
public RabbitAdmin(RabbitTemplate rabbitTemplate) {
Assert.notNull(rabbitTemplate, "RabbitTemplate must not be null");
Assert.notNull(rabbitTemplate.getConnectionFactory(), "RabbitTemplate's ConnectionFactory must not be null");
this.connectionFactory = rabbitTemplate.getConnectionFactory();
this.rabbitTemplate = rabbitTemplate;
}

但实际看他们的构造函数,发现如果我们不需要自己定制RabbitTemplate,直接使用第一个构造方法即可。

@Configuration
public class RabbitMqConfig {
@Autowired
RabbitTemplate rabbitTemplate; @Bean
public RabbitAdmin rabbitAdmin() {
return new RabbitAdmin(rabbitTemplate);
}
}

类似这样,就配置好了。

接下来写一个测试类,测试声明交换机,队列,以及发送消息和接收消息等操作。

首先是声明交换机类型,四种交换机对应的构造方法如下

//参数列表分别是:1.交换器名称,2.是否持久化,3.是否自动删除【指的是当最后一个与它绑定的队列删除时,是否自动删除该交换机】
TopicExchange topicExchange = new TopicExchange("default.topic", true, false);
DirectExchange directExchange = new DirectExchange("default.direct", true, false);
FanoutExchange fanoutExchange = new FanoutExchange("default.fanout", true, false);
HeadersExchange headersExchange = new HeadersExchange("default.headers", true, false);
rabbitAdmin.declareExchange(topicExchange);
rabbitAdmin.declareExchange(directExchange);
rabbitAdmin.declareExchange(fanoutExchange);
rabbitAdmin.declareExchange(headersExchange);

然后是声明队列

//1.队列名称,2.声明一个持久队列,3.声明一个独立队列,4.是否自动删除队列
Queue queue1 = new Queue("queue1", true, false, false);
Queue queue2 = new Queue("queue2", true, false, false);
Queue queue3 = new Queue("queue3", true, false, false);
Queue queue4 = new Queue("queue4", true, false, false);
rabbitAdmin.declareQueue(queue1);
rabbitAdmin.declareQueue(queue2);
rabbitAdmin.declareQueue(queue3);
rabbitAdmin.declareQueue(queue4);

然后把队列和交换机相互绑定

//1.queue:绑定的队列,2.topicExchange:绑定到那个交换器,3.test.send.topic:绑定的路由名称[routing key]
rabbitAdmin.declareBinding(BindingBuilder.bind(queue1).to(fanoutExchange));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue2).to(fanoutExchange));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue3).to(topicExchange).with("mq.*"));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue4).to(directExchange).with("mq.direct"));

因为fanout类型的交换机忽略routing key属性,所以不需要设置。

完整测试代码如下

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class RabbitMqTest {
@Autowired
private RabbitTemplate rabbitTemplate; @Autowired
private RabbitAdmin rabbitAdmin; @Test
public void testDeclare() {
//参数列表分别是:1.交换器名称,2.是否持久化,3.是否自动删除【指的是当最后一个与它绑定的队列删除时,是否自动删除该交换机】
TopicExchange topicExchange = new TopicExchange("default.topic", true, false);
DirectExchange directExchange = new DirectExchange("default.direct", true, false);
FanoutExchange fanoutExchange = new FanoutExchange("default.fanout", true, false);
HeadersExchange headersExchange = new HeadersExchange("default.headers", true, false);
rabbitAdmin.declareExchange(topicExchange);
rabbitAdmin.declareExchange(directExchange);
rabbitAdmin.declareExchange(fanoutExchange);
rabbitAdmin.declareExchange(headersExchange); //1.队列名称,2.声明一个持久队列,3.声明一个独立队列,4.是否自动删除队列
Queue queue1 = new Queue("queue1", true, false, false);
Queue queue2 = new Queue("queue2", true, false, false);
Queue queue3 = new Queue("queue3", true, false, false);
Queue queue4 = new Queue("queue4", true, false, false);
rabbitAdmin.declareQueue(queue1);
rabbitAdmin.declareQueue(queue2);
rabbitAdmin.declareQueue(queue3);
rabbitAdmin.declareQueue(queue4); //1.queue:绑定的队列,2.topicExchange:绑定到那个交换器,3.test.send.topic:绑定的路由名称[routing key]
rabbitAdmin.declareBinding(BindingBuilder.bind(queue1).to(fanoutExchange));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue2).to(fanoutExchange));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue3).to(topicExchange).with("mq.*"));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue4).to(directExchange).with("mq.direct"));
}
}

运行结果如下:

再看一下绑定情况:

Direct交换机

Fanout交换机

Topic交换机

全都测试成功,接下来就可以开始发送消息了。

发送消息有多个API可用,这里选择高亮的那个API,实际还有send方法可用,不过需要自己来构建消息

@Test
public void testSendMessage() {
//1.交换机,2.路由键,3.发送的消息体【这里的消息体会自动转换为消息,也可以自己构建消息对象】
rabbitTemplate.convertAndSend("default.topic","mq.whatever.this.is",new Student(1,"mmp","male",234));
}

测试结果如下:

一定要注意topic类型的交换机的路由键的匹配规则,#匹配0个或者多个单词,*匹配一个单词

那如果不想每次都是在测试类里面创建交换机和队列,可以怎么做呢?可以在程序入口类里面实现CommandLineRunner接口,代码如下,这样的话,每次启动都会声明一次,当然重复声明不会报错,但会覆盖之前的声明,比如说之前声明的时候定义的routing key可能就会被覆盖。

@SpringBootApplication
@EnableRabbit
public class AmqpApplication implements CommandLineRunner {
@Autowired
private RabbitTemplate rabbitTemplate; @Autowired
private RabbitAdmin rabbitAdmin; public static void main(String[] args) {
SpringApplication.run(AmqpApplication.class, args);
} @Override
public void run(String... args) throws Exception {
//参数列表分别是:1.交换器名称,2.是否持久化,3.是否自动删除【指的是当最后一个与它绑定的队列删除时,是否自动删除该交换机】
TopicExchange topicExchange = new TopicExchange("default.topic", true, false);
DirectExchange directExchange = new DirectExchange("default.direct", true, false);
FanoutExchange fanoutExchange = new FanoutExchange("default.fanout", true, false);
HeadersExchange headersExchange = new HeadersExchange("default.headers", true, false);
rabbitAdmin.declareExchange(topicExchange);
rabbitAdmin.declareExchange(directExchange);
rabbitAdmin.declareExchange(fanoutExchange);
rabbitAdmin.declareExchange(headersExchange); //1.队列名称,2.声明一个持久队列,3.声明一个独立队列,4.是否自动删除队列
Queue queue1 = new Queue("queue1", true, false, false);
Queue queue2 = new Queue("queue2", true, false, false);
Queue queue3 = new Queue("queue3", true, false, false);
Queue queue4 = new Queue("queue4", true, false, false);
rabbitAdmin.declareQueue(queue1);
rabbitAdmin.declareQueue(queue2);
rabbitAdmin.declareQueue(queue3);
rabbitAdmin.declareQueue(queue4); //1.queue:绑定的队列,2.topicExchange:绑定到那个交换器,3.test.send.topic:绑定的路由名称[routing key]
rabbitAdmin.declareBinding(BindingBuilder.bind(queue1).to(fanoutExchange));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue2).to(fanoutExchange));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue3).to(topicExchange).with("mq.*"));
rabbitAdmin.declareBinding(BindingBuilder.bind(queue4).to(directExchange).with("mq.direct"));
}
}

但其实这样做还是比较复杂的,而且完全没有必要,更加简单方便的做法是,把那些配置声明的对象直接添加到IOC容器中,让spring自动的去调用相应的声明方法,真是纵享丝滑呀,类似下面这样子:

@Bean
public Queue Queue() {
return new Queue("hello");
}

继续测试接收消息,有一个注解很方便。

@Service
public class ReceiverService {
@RabbitListener(queues = {"queue3"})
public void receive(Student student) {
System.out.println("接收到消息并打印:"+student);
}
}

测试结果如下:

接收到消息并打印:student{id=1, name='mmp', gender='male', age=234}

也可以直接使用方法接收消息

@Test
public void testReceive() {
Student student = (Student) rabbitTemplate.receiveAndConvert("queue3");
System.out.println(student);
}

测试结果如下:

student{id=1, name='mmp', gender='male', age=234}

如果想让发送的学生对象使用JSON格式怎么办呢?

需要定制一下:

@Configuration
public class RabbitMqConfig {
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
} @Bean
public RabbitAdmin rabbitAdmin(RabbitTemplate rabbitTemplate) {
return new RabbitAdmin(rabbitTemplate);
}
}

测试一下:

源码地址:https://github.com/lingEric/springboot-integration-hello

更多官方tutorials请移步https://github.com/rabbitmq/rabbitmq-tutorials

Spring boot集成Rabbit MQ使用初体验的更多相关文章

  1. spring boot 中 rabbit mq基础例子

    1.先安装ELANG,再按照RabbitMQ 2.打开RabbitMQ控制台:rabbit command prompt 1.设置elang的路径:set ERLANG_HOME=D:\work_pr ...

  2. 小马哥-Java 微服务实践 - Spring Boot 系列-01Java 微服务实践 - Spring Boot 系列(一)初体验

    课程github地址 https://github.com/mercyblitz/segmentfault-lessons 传统的web应用架构.微服务是一种架构.不限定什么语言 单体应用和微服务的对 ...

  3. Spring Boot and Rabbit MQ 异常的时候消息的状态

    我们有一个处理消息的方法. 在处理消息的时候出现了异常,那出现异常后这个消息会怎么处理呢. 根据我们的实际情况的观察,如果出现了异常. 但是你没有捕获或者处理异常,这个消息会一直存在,并且你的系统会持 ...

  4. Spring Boot 集成 RabbitMQ 实战

    Spring Boot 集成 RabbitMQ 实战 特别说明: 本文主要参考了程序员 DD 的博客文章<Spring Boot中使用RabbitMQ>,在此向原作者表示感谢. Mac 上 ...

  5. Spring boot集成RabbitMQ(山东数漫江湖)

    RabbitMQ简介 RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统 MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出 ...

  6. 【spring boot】【redis】spring boot 集成redis的发布订阅机制

    一.简单介绍 1.redis的发布订阅功能,很简单. 消息发布者和消息订阅者互相不认得,也不关心对方有谁. 消息发布者,将消息发送给频道(channel). 然后是由 频道(channel)将消息发送 ...

  7. Spring Boot集成Jasypt安全框架

    Jasypt安全框架提供了Spring的集成,主要是实现 PlaceholderConfigurerSupport类或者其子类. 在Sring 3.1之后,则推荐使用PropertySourcesPl ...

  8. Spring boot集成swagger2

    一.Swagger2是什么? Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件. Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格 ...

  9. Spring Boot 集成 Swagger,生成接口文档就这么简单!

    之前的文章介绍了<推荐一款接口 API 设计神器!>,今天栈长给大家介绍下如何与优秀的 Spring Boot 框架进行集成,简直不能太简单. 你所需具备的基础 告诉你,Spring Bo ...

随机推荐

  1. 以后可得记住了--Python笔试面试题小结

    1.字符串处理 将字符串中的数字替换成其两倍的值,例如: 修改前:"AS7G123m (d)F77k" 修改后:"AS14G246m (d)F154k"   个 ...

  2. SQL SERVER数据库中DDL语句

    一 修改表列名 EXEC sp_rename 'table_name.[字段旧名]', '字段新名' , 'COLUMN'; 二 修改列类型 ALTER TABLE table_name  ALTER ...

  3. JUC包Lock机制的支持--AQS

    在上一次总结中,提到了JUC包下使用Lock接口实现同步的方法,以及和Synchronized关键字的一些比较,那么使用Lock完成锁机制的底层支持又是什么呢?总结如下: 1 AQS是什么 AQS是一 ...

  4. NLP(十二)指代消解

    代词是用来代替重复出现的名词 例句: 1.Ravi is a boy. He often donates money to the poor. 先出现主语,后出现代词,所以流动的方向从左到右,这类句子 ...

  5. 【系统解读】SystemUI篇(一)SystemUI启动流程

    前言 SystemUI是系统启动中第一个用户肉眼可见的应用,其功能包罗万象,比如开机后看到的锁屏界面,充电时充电界面,状态栏,导航栏,多任务栏等,都是与Android手机用户息息相关的功能.所以不止S ...

  6. POJ-1213 How Many Tables( 并查集 )

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213 Problem Description Today is Ignatius' birthday. ...

  7. CodeForces 982 C Cut 'em all!

    Cut 'em all! 题意:求删除了边之后,剩下的每一块联通块他的点数都为偶数,求删除的边最多能是多少. 题解:如果n为奇数,直接返回-1,因为不可能成立.如果n为偶数,随意找一个点DFS建树记录 ...

  8. Spring boot 全局配置 properties或者yml文件报错

    主要问题是没有扫描到配置文件 在pom文件里面<build>    </build>中加上以下代码就可以保证能扫描到了 <resources> <resour ...

  9. 个推TechDay参会感悟

    上周六去参加了个推和FCC联合在梦想小镇举办的TechDay,当然是作为台下听讲选手参与的,想上去讲可惜实力他不允许啊,吹牛逼我在行,讲技术可就有点虚了,老老实实的坐在台下听大佬们的分享,当然由于买了 ...

  10. Object和Objects

    Object 在Java,Object类是超级父类,是所有类的父类. public boolean equals(Object obj) { return (this == obj); } publi ...