Spring Boot中通过RabbitTemplate主动pull(get)消息的例子
import java.util.Properties;
import java.util.function.Consumer; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.ChannelCallback;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.GetResponse; @Service
public class RabbitAdminServices { private static final Logger logger = LoggerFactory.getLogger(RabbitAdminServices.class); @Autowired
AmqpAdmin rabbitAdmin; @Autowired
RabbitTemplate rabbitTemplate; @Autowired
MessageConverter messageConverter; private volatile MessagePropertiesConverter messagePropertiesConverter = new DefaultMessagePropertiesConverter(); public int getCount(String queueName) { Properties properties = rabbitAdmin.getQueueProperties(queueName);
return (Integer)properties.get(RabbitAdmin.QUEUE_MESSAGE_COUNT);
} public <T> void processQueue(String queueName, Integer count, Class<T> clazz, Consumer<T> consumer) { int reprocessCount = getCount(queueName);
int requestCount = reprocessCount;
if(count != null) {
requestCount = count;
}
for(int i = 0; i < reprocessCount && i < requestCount; i++) {
rabbitTemplate.execute(new ChannelCallback<T>() { @Override
public T doInRabbit(Channel channel) throws Exception {
GetResponse response = channel.basicGet(queueName, false);
T result = null;
try {
MessageProperties messageProps = messagePropertiesConverter.toMessageProperties(response.getProps(), response.getEnvelope(), "UTF-8");
if(response.getMessageCount() >= 0) {
messageProps.setMessageCount(response.getMessageCount());
}
Message message = new Message(response.getBody(), messageProps);
result = (T)messageConverter.fromMessage(message);
consumer.accept(result);
channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
}
catch(Exception e) {
channel.basicNack(response.getEnvelope().getDeliveryTag(), false, true);
}
return result;
}
}); }
}
}
实现RabbitMQ的消费者有两种模式,推模式(Push)和拉模式(Pull)。
实现推模式推荐的方式是继承 DefaultConsumer 基类,也可以使用Spring AMQP的 SimpleMessageListenerContainer 。
推模式是最常用的,但是有些情况下推模式并不适用的,比如说:
由于某些限制,消费者在某个条件成立时才能消费消息
需要批量拉取消息进行处理
实现拉模式
RabbitMQ的Channel提供了 basicGet 方法用于拉取消息。
- /**
- * Retrieve a message from a queue using {@link com.rabbitmq.client.AMQP.Basic.Get}
- * @see com.rabbitmq.client.AMQP.Basic.Get
- * @see com.rabbitmq.client.AMQP.Basic.GetOk
- * @see com.rabbitmq.client.AMQP.Basic.GetEmpty
- * @param queue the name of the queue
- * @param autoAck true if the server should consider messages
- * acknowledged once delivered; false if the server should expect
- * explicit acknowledgements
- * @return a {@link GetResponse} containing the retrieved message data
- * @throws java.io.IOException if an error is encountered
- */
- GetResponse basicGet(String queue, boolean autoAck) throws IOException;
basicGet 返回 GetResponse 类。
- public class GetResponse {
- private final Envelope envelope;
- private final BasicProperties props;
- private final byte[] body;
- private final int messageCount;
- // ...
public class GetResponse { private final Envelope envelope; private final BasicProperties props; private final byte[] body; private final int messageCount; // ...
rabbitmq-client版本4.0.3
使用 basicGet 拉取消息需要注意:
- basicGet
- DefaultConsumer
示例代码:
- private void consume(Channel channel) throws IOException, InterruptedException {
- while (true) {
- if (!isConditionSatisfied()) {
- TimeUnit.MILLISECONDS.sleep(1);
- continue;
- }
- GetResponse response = channel.basicGet(CAOSH_TEST_QUEUE, false);
- if (response == null) {
- TimeUnit.MILLISECONDS.sleep(1);
- continue;
- }
- String data = new String(response.getBody());
- logger.info("Get message <= {}", data);
- channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
- }
- }
批量拉取消息
RabbitMQ支持客户端批量拉取消息,客户端可以连续调用 basicGet 方法拉取多条消息,处理完成之后一次性ACK。需要注意:
- basicGet
- basicAck
示例代码:
- String bridgeQueueName = extractorProperties.getBridgeQueueName();
- int batchSize = extractorProperties.getBatchSize();
- List<GetResponse> responseList = Lists.newArrayListWithCapacity(batchSize);
- long tag = 0;
- while (responseList.size() < batchSize) {
- GetResponse getResponse = channel.basicGet(bridgeQueueName, false);
- if (getResponse == null) {
- break;
- }
- responseList.add(getResponse);
- tag = getResponse.getEnvelope().getDeliveryTag();
- }
- if (responseList.isEmpty()) {
- TimeUnit.MILLISECONDS.sleep(1);
- } else {
- logger.info("Get <{}> responses this batch", responseList.size());
- // handle messages
- channel.basicAck(tag, true);
- }
关于QueueingConsumer
QueueingConsumer 在客户端本地使用 BlockingQueue 缓冲消息,其nextDelivery方法也可以用于实现拉模式(其本质上是 BlockingQueue.take ),但是 QueueingConsumer 现在已经标记为Deprecated。
Spring Boot中通过RabbitTemplate主动pull(get)消息的例子的更多相关文章
- Spring Boot中一个Servlet主动断开连接的方法
主动断开连接,从而返回结果给客户端,并且能够继续执行剩余代码. 对于一个HttpServletResponse类型的对象response来说,执行如下代码: response.getWriter(). ...
- 记一次spring boot中MongoDB Prematurely reached end of stream的异常解决
在spring boot项目中使用了mongodb,当一段时间没有操作mongodb,下次操作mongodb时就会出现异常.异常如下: org.springframework.data.mongodb ...
- Spring Boot中使用缓存
Spring Boot中使用缓存 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一. 原始的使 ...
- Spring Boot中使用RabbitMQ
很久没有写Spring Boot的内容了,正好最近在写Spring Cloud Bus的内容,因为内容会有一些相关性,所以先补一篇关于AMQP的整合. Message Broker与AMQP简介 Me ...
- spring boot中使用servlet、listener和filter
spring boot中支持使用java Web三大组件(servlet.listener和filter),但是坑比较多,主要是spring boot内嵌tomcat和独立tomcat服务器有一些细节 ...
- Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题
Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...
- Spring Boot中快速操作Mongodb
Spring Boot中快速操作Mongodb 在Spring Boot中集成Mongodb非常简单,只需要加入Mongodb的Starter包即可,代码如下: <dependency> ...
- 在 Spring Boot 中使用 HikariCP 连接池
上次帮小王解决了如何在 Spring Boot 中使用 JDBC 连接 MySQL 后,我就一直在等,等他问我第三个问题,比如说如何在 Spring Boot 中使用 HikariCP 连接池.但我等 ...
- spring boot(三):Spring Boot中Redis的使用
spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...
- Spring Boot中的事务管理
原文 http://blog.didispace.com/springboottransactional/ 什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合 ...
随机推荐
- vue前端开发仿钉图系列(1)高德地图的使用详解
最近公司让参考钉图做图层模块相关的功能,很庆幸有机会细细研究地图相关的东西.因为手机端用的是高德地图,web端也使用高德地图.还是和往常一样,先贴上效果图. 步骤1.在高德开放平台注册信息,创建自己的 ...
- threejs 几何体的本质 顶点
几何体的线框模式, 一个正方平面最少可以由4个顶点组成,两个三角形组成(公用了 2个顶点,使用了索引创建顶点属性) . // 导入 threejs import * as THREE from &qu ...
- js中 操作符new 的作用和含义
作用:通过构造函数创建实例对象 :通过 new 出来的实例可以访问构造函数的属性和方法 :
- [Dest0g3 520迎新赛]funny_upload
打开靶机抓包发现过滤代码 发现.htaccess能上传后传入图片马 发现内容对<?进行过滤 我们换一种方式写后门代码 <script language="php"> ...
- 基于 Python + Vue3!一个轻量级的域名和 SSL 证书监测平台!
大家好,我是 Java陈序员. 在企业开发中,由于业务众多,涉及到很多业务域名证书,证书过期由于遗忘常常未能及时续期,导致线上访问异常,给企业带来损失! 今天,给大家介绍一个轻量级的域名和 SSL 证 ...
- CF1187E 题解
Title translation 给定一棵 \(n\) 个点的树,初始全是白点. 要做 \(n\) 步操作,每一次选定一个与一个黑点相隔一条边的白点,将它染成黑点,然后获得该白点被染色前所在的白色联 ...
- freeswitch的话单处理
概述 freeswitch是一款简单好用的VOIP开源软交换平台. 如果对cdr话单要求不高,可以直接使用fs的原始话单文件,使用脚本做一些简单的统计. 环境 CentOS 7.9 freeswitc ...
- ASP.Net Core使用Jenkins配合pm2自动化部署项目
一. 新建一个自由风格的软件项目 二. General配置(参数化构建) 1. 用来选择部署的服务器(我这里只添加了一个,如果需要添加多个,一行一个就可以了) 2. 选择不同的环境变量 三.源码管理 ...
- 终于搞全了:GPIO/ADC/LED/I2C/SPI/USB…
合宙低功耗4G模组经典型号Air780E,支持两种软件开发方式: 一种是传统的AT指令:一种是基于模组做Open开发. 传统AT指令的开发方式,合宙模组与行业内其它模组品牌在软件上区别不大,在硬件功耗 ...
- IPC-7093A-CN 中文 2020底部端子元器件(BTCs)设计和组装工艺的实施
IPC-7093A 标准为实施底部端子元器件(BTCs)提供了基本的设计和组装指南.具体而言,IPC-7093A 提供了与 BTCs 相关的关键设计.材料.组装.检查.维修.质量和可靠性问题的指南. ...