RabbitMQ基础教程之Spring使用篇

相关博文,推荐查看:

  1. RabbitMq基础教程之安装与测试
  2. RabbitMq基础教程之基本概念
  3. RabbitMQ基础教程之基本使用篇
  4. RabbitMQ基础教程之使用进阶篇

在实际的应用场景中,将RabbitMQ和Spring结合起来使用的时候可能更加频繁,网上关于Spring结合的博文中,大多都是xml的方式,这篇博文,则主要介绍下利用JavaConfig的结合,又会是怎样的

I. Spring中RabbitMQ的基本使用姿势

1. 准备

开始之前,首先添加上必要的依赖,主要利用 spring-rabbit 来实现,这个依赖中,内部又依赖的Spring相关的模块,下面统一改成5.0.4版本

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.7.3.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.4.RELEASE</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

流程分析

实现主要分为两块,一个是投递服务,一个是消费服务,结合前面RabbitMQ的基本使用姿势中的流程,即便是使用Spring,我们也避免不了下面几步

  • 建立连接
  • 声明Exchange ,声明Queue
  • 建立Queue和Exchange之间的绑定关系
  • 发送消息
  • 消费消息(ack/nak)

2. 基本case

首先借助Spring,来实现一个最基本的最简单的实现方式

/**
* Created by yihui in 19:53 18/5/30.
*/
public class SimpleProducer {
public static void main(String[] args) throws InterruptedException {
CachingConnectionFactory factory = new CachingConnectionFactory("127.0.0.1", 5672);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/"); RabbitAdmin admin = new RabbitAdmin(factory); // 创建队列
Queue queue = new Queue("hello", true, false, false, null);
admin.declareQueue(queue); //创建topic类型的交换机
TopicExchange exchange = new TopicExchange("topic.exchange");
admin.declareExchange(exchange); //交换机和队列绑定,路由规则为匹配"foo."开头的路由键
admin.declareBinding(BindingBuilder.bind(queue).to(exchange).with("foo.*")); //设置监听
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(factory);
Object listener = new Object() {
public void handleMessage(String foo) {
System.out.println(" [x] Received '" + foo + "'");
}
};
MessageListenerAdapter adapter = new MessageListenerAdapter(listener);
container.setMessageListener(adapter);
container.setQueues(queue);
container.start(); //发送消息
RabbitTemplate template = new RabbitTemplate(factory);
template.convertAndSend("topic.exchange", "foo.bar", "Hello, world!");
Thread.sleep(1000); // 关闭
container.stop();
}
}

3. 逻辑分析

上面这一段代码中,包含了消息投递和消费两块,从实现而言,基本上逻辑和前面的基础使用没有什么太大的区别,步骤如下:

  1. 建立连接: new CachingConnectionFactory("127.0.0.1", 5672)
  2. 声明Queue: new Queue("hello", true, false, false, null)
  3. 声明Exchange: new TopicExchange("topic.exchange")
  4. 绑定Queue和Exchange: admin.declareBinding(BindingBuilder.bind(queue).to(exchange).with("foo.*"));
  5. 投递消息: template.convertAndSend("topic.exchange", "foo.bar", "Hello, world!");
  6. 消费消息: 设置MessageListenerAdapter

这里面有几个类需要额外注意:

  • RabbitTemplate: Spring实现的发送消息的模板,可以直接发送消息
  • SimpleMessageListenerContainer: 注册接收消息的容器

II. Spring结合JavaConfig使用RabbitMQ使用姿势

1. 公共配置

主要是将公共的ConnectionFactory 和 RabbitAdmin 抽取出来

@Configuration
@ComponentScan("com.git.hui.rabbit.spring")
public class SpringConfig { private Environment environment; @Autowired
public void setEnvironment(Environment environment) {
this.environment = environment;
System.out.println("then env: " + environment);
} @Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
return factory;
} @Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
}

2. 消息投递

发送消息的组件就比较简单了,直接利用 AmqpTemplate 即可

@Component
public class AmqpProducer { private AmqpTemplate amqpTemplate; @Autowired
public void amqpTemplate(ConnectionFactory connectionFactory) {
amqpTemplate = new RabbitTemplate(connectionFactory);
} /**
* 将消息发送到指定的交换器上
*
* @param exchange
* @param msg
*/
public void publishMsg(String exchange, String routingKey, Object msg) {
amqpTemplate.convertAndSend(exchange, routingKey, msg);
}
}

3. DirectExchange消息消费

根据不同的Exchange类型,分别实现如下

DirectExchange方式

@Configuration
public class DirectConsumerConfig {
@Autowired
private ConnectionFactory connectionFactory; @Autowired
private RabbitAdmin rabbitAdmin; @Bean
public DirectExchange directExchange() {
DirectExchange directExchange = new DirectExchange("direct.exchange");
directExchange.setAdminsThatShouldDeclare(rabbitAdmin);
return directExchange;
} @Bean
public Queue directQueue() {
Queue queue = new Queue("aaa");
queue.setAdminsThatShouldDeclare(rabbitAdmin);
return queue;
} @Bean
public Binding directQueueBinding() {
Binding binding = BindingBuilder.bind(directQueue()).to(directExchange()).with("test1");
binding.setAdminsThatShouldDeclare(rabbitAdmin);
return binding;
} @Bean
public ChannelAwareMessageListener directConsumer() {
return new BasicConsumer("direct");
} @Bean(name = "directMessageListenerContainer")
public MessageListenerContainer messageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setRabbitAdmin(rabbitAdmin);
container.setQueues(directQueue());
container.setPrefetchCount(20);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setMessageListener(directConsumer());
return container;
}
}

从上面的实现,基本上都是重新定义了一个Queue, Exchange, Binding, MessageListenerContainer(用来监听消息),并将消息的消费抽出了一个公共类

@Slf4j
public class BasicConsumer implements ChannelAwareMessageListener {
private String name; public BasicConsumer(String name) {
this.name = name;
} @Override
public void onMessage(Message message, Channel channel) throws Exception {
try {
byte[] bytes = message.getBody();
String data = new String(bytes, "utf-8");
System.out.println(name + " data: " + data + " tagId: " + message.getMessageProperties().getDeliveryTag());
} catch (Exception e) {
log.error("local cache rabbit mq localQueue error! e: {}", e);
}
}
}

4. 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class SprintUnit {
@Autowired
private AmqpProducer amqpProducer; @Test
public void testDirectConsumer() throws InterruptedException {
String[] routingKey = new String[]{"hello.world", "world", "test1"};
for (int i = 0; i < 10; i++) {
amqpProducer
.publishMsg("direct.exchange", routingKey[i % 3], ">>> hello " + routingKey[i % 3] + ">>> " + i);
}
System.out.println("-------over---------"); Thread.sleep(1000 * 60 * 10);
}
}

这个测试类中,虽然主要是往MQ中投递消息,但在Spring容器启动之后,接收MQ消息并消费的实际任务,是通过前面的MessageListenerContainer托付给Spring容器了,上面测试执行之后,输出为

direct data: >>> hello test1>>> 2 tagId: 1
direct data: >>> hello test1>>> 5 tagId: 2
direct data: >>> hello test1>>> 8 tagId: 3

5. Topic & Fanout策略

上面的一个写出来之后,再看这两个就比较相似了

@Configuration
public class TopicConsumerConfig {
@Autowired
private ConnectionFactory connectionFactory; @Autowired
private RabbitAdmin rabbitAdmin; @Bean
public TopicExchange topicExchange() {
TopicExchange topicExchange = new TopicExchange("topic.exchange");
topicExchange.setAdminsThatShouldDeclare(rabbitAdmin);
return topicExchange;
} @Bean
public Queue topicQueue() {
Queue queue = new Queue("bbb");
queue.setAdminsThatShouldDeclare(rabbitAdmin);
return queue;
} @Bean
public Binding topicQueueBinding() {
Binding binding = BindingBuilder.bind(topicQueue()).to(topicExchange()).with("*.queue");
binding.setAdminsThatShouldDeclare(rabbitAdmin);
return binding;
} @Bean
public ChannelAwareMessageListener topicConsumer() {
return new BasicConsumer("topic");
} @Bean(name = "topicMessageListenerContainer")
public MessageListenerContainer messageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setRabbitAdmin(rabbitAdmin);
container.setQueues(topicQueue());
container.setPrefetchCount(20);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setMessageListener(topicConsumer());
return container;
}
}

对应的测试case

@Test
public void testTopicConsumer() throws InterruptedException {
String[] routingKey = new String[]{"d.queue", "a.queue", "cqueue"};
for (int i = 0; i < 20; i++) {
amqpProducer.publishMsg("topic.exchange", routingKey[i % 3], ">>> hello " + routingKey[i % 3] + ">>> " + i);
}
System.out.println("-------over---------"); Thread.sleep(1000 * 60 * 10);
}

广播方式

@Configuration
public class FanoutConsumerConfig { @Autowired
private ConnectionFactory connectionFactory; @Autowired
private RabbitAdmin rabbitAdmin; @Bean
public FanoutExchange fanoutExchange() {
FanoutExchange fanoutExchange = new FanoutExchange("fanout.exchange");
fanoutExchange.setAdminsThatShouldDeclare(rabbitAdmin);
return fanoutExchange;
} @Bean
public Queue fanoutQueue() {
Queue queue = new Queue("ccc");
queue.setAdminsThatShouldDeclare(rabbitAdmin);
return queue;
} @Bean
public Binding fanoutQueueBinding() {
Binding binding = BindingBuilder.bind(fanoutQueue()).to(fanoutExchange());
binding.setAdminsThatShouldDeclare(rabbitAdmin);
return binding;
} @Bean
public ChannelAwareMessageListener fanoutConsumer() {
return new BasicConsumer("fanout");
} @Bean(name = "FanoutMessageListenerContainer")
public MessageListenerContainer messageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setRabbitAdmin(rabbitAdmin);
container.setQueues(fanoutQueue());
container.setPrefetchCount(20);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setMessageListener(fanoutConsumer());
return container;
}
}

对应的测试case

@Test
public void testFanoutConsumer() throws InterruptedException {
String[] routingKey = new String[]{"d.queue", "a.queue", "cqueue", "hello.world", "world", "test1"};
for (int i = 0; i < 20; i++) {
amqpProducer
.publishMsg("fanout.exchange", routingKey[i % 6], ">>> hello " + routingKey[i % 6] + ">>> " + i);
}
System.out.println("-------over---------"); Thread.sleep(1000 * 60 * 10);
}

II. 其他

项目地址

一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

扫描关注

 
QrCode

RabbitMQ基础教程之Spring&JavaConfig使用篇的更多相关文章

  1. OpenVAS漏洞扫描基础教程之OpenVAS概述及安装及配置OpenVAS服务

    OpenVAS漏洞扫描基础教程之OpenVAS概述及安装及配置OpenVAS服务   1.  OpenVAS基础知识 OpenVAS(Open Vulnerability Assessment Sys ...

  2. Python基础教程之List对象 转

    Python基础教程之List对象 时间:2014-01-19    来源:服务器之家    投稿:root   1.PyListObject对象typedef struct {    PyObjec ...

  3. Python基础教程之udp和tcp协议介绍

    Python基础教程之udp和tcp协议介绍 UDP介绍 UDP --- 用户数据报协议,是一个无连接的简单的面向数据报的运输层协议.UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但 ...

  4. RabbitMQ系列教程之二:工作队列(Work Queues)(转载)

    RabbitMQ系列教程之二:工作队列(Work Queues)     今天开始RabbitMQ教程的第二讲,废话不多说,直接进入话题.   (使用.NET 客户端 进行事例演示)          ...

  5. Java基础-SSM之Spring MVC入门篇

    Java基础-SSM之Spring MVC入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Spring MVC简介 1>.什么是Spring MVC 答:Sprin ...

  6. Java基础-SSM之Spring快速入门篇

    Java基础-SSM之Spring快速入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.    Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java ...

  7. 转 RabbitMQ 基础概念及 Spring 的配置和使用 推荐好文 举例讲解

    从不知道到了解—RabbitMQ 基础概念及 Spring 的配置和使用 原理同上 请求地址:http://localhost:8080/home?type=3&routing_key=myO ...

  8. Linux入门基础教程之Linux下软件安装

    Linux入门基础教程之Linux下软件安装 一.在线安装: sudo apt-get install 即可安装 如果在安装完后无法用Tab键补全命令,可以执行: source ~/.zshrc AP ...

  9. [第一篇]——Docker 教程之Spring Cloud直播商城 b2b2c电子商务技术总结

    Docker 教程 Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然 ...

随机推荐

  1. Linux学习总结(十三)文本编辑器 vim

    vim是vi的升级版,会根据文本属性带色彩显示,具体用法如下: 一般模式 : 1.光标定位: 左右移动一个字符, h l上下移动一个字符, k j左右下上 ,左右在两边,下上在中间这样记光标定位行首 ...

  2. npm run build 打包后,如何运行在本地查看效果

    目前,使用vue-cli脚手架写了一个前端项目,之前一直是使用npm run dev 在8080端口上进行本地调试.项目已经进行一半了,今天有时间突然想使用npm run build进行上线打包,试试 ...

  3. [转]order by 1是什么意思?

    ORDER BY 1 表示 所select 的字段按第一个字段排序 ORDER BY ASC应该没有这样写法,ORDER BY 后面不是字段就是数字, 可以ORDER BY 1 ASC 或者ORDER ...

  4. [转]HTTP报文接口及客户端和服务器端交互原理

    1. 协议 a. TCP/IP整体构架概述 TCP/IP协议并不完全符合OSI的七层参考模型.传统的开放式系统互连参考模型,是一种通信协议的7层抽象的参考模型,其中每一层执行某一特定任务.该模型的目的 ...

  5. 自定义UICollectionViewLayout并添加UIDynamic

    大家也可以到这里查看. UICollectionView是iOS6引入的控件,而UIDynamicAnimator是iOS7上新添加的框架.本文主要涵盖3部分: 一是简单概括UICollectionV ...

  6. redis参数AOF参数的bug

    问题:redis 不想开启AOF了.但是还老是出现BGREWRITEAOF .(本redis版本为4.0.6) 涉及持久化参数设置如下: 排查及结果: 该redis以前开启过 AOF ,后来停止AOF ...

  7. CentOS中的 yum upgrade 和 yum update 的区别

    通过 man yum 的帮助信息了解 yum update 和 yum upgrade: update If run without any packages, update will update ...

  8. angular常用属性大全

    Angular元素属性大全 addClass()-为每个匹配的元素添加指定的样式类名 after()-在匹配元素集合中的每个元素后面插入参数所指定的内容,作为其兄弟节点 append()-在每个匹配元 ...

  9. 原生JS实现移动端的轮播效果

    首先 我们想实现的效果是在手指按下拖动的时候图片能够跟随移动(无动画效果)然后松开手指后判断图片移动的位置 和某一个值进行比较 在这里我们默认定为盒子的1/3宽度 当x轴的移动位置大于1/3的时候图片 ...

  10. TinyMCE插件:RESPONSIVE filemanager 9 图片自动添加水印

    跟踪function() 搜索(filemanager/upload.php) 在代码中发现,上传成功后,会传回JSON信息数据,于是最后找到方法是 $upload_handler = new Upl ...