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/ 什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合 ...
随机推荐
- Java日期时间API系列31-----Jdk8中java.time包中的新的日期时间API类,时间戳的获取方式对比、转换和使用。
时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到.Java中本来已经有相关获取时间戳的方法,Java8后增加新的类In ...
- PRT预计算辐射传输方法
PRT(Precomputed Radiance Transfer)技术是一种用于实时渲染全局光照的方法.它通过预计算光照传输来节省时间,并能够实时重现面积光源下3D模型的全局光照效果. 由于PRT方 ...
- 组件传参v-model语法糖只能写一次的解决办法
v-model 的使用 解决只能使用一次v-model的问题:使用 sync 修饰符
- kotlin协程——>组合挂起函数
默认顺序调用 假设我们在不同的地⽅定义了两个进⾏某种调⽤远程服务或者进⾏计算的挂起函数.我们只假设它们都是有⽤的,但是实际上它们在这个⽰例中只是为了该⽬的⽽延迟了⼀秒钟: suspend fun do ...
- JVM栈帧
Java 的源码文件经过编译器编译后会生成字节码文件,然后由 JVM 的类加载器进行加载,再交给执行引擎执行.在执行过程中,JVM 会划出一块内存空间来存储程序执行期间所需要用到的数据,这块空间一般被 ...
- 噢!JavaScript (2):对数组要小心使用delete
最近在重写我自己的静态博客生成器,虽然遇到的小问题挺多,但今早这个问题令我印象深刻. 发现问题 博客的文章基础数据储存在main.json中,其中专门有数组dateindex来储存经过排列后的文章顺序 ...
- .NET使用Moq开源模拟库简化单元测试
前言 今天大姚给大家推荐一个.NET开源.流行.使用简单的.NET模拟库:Moq. Moq类库介绍 Moq是一个.NET开源.流行.使用简单的 .NET 模拟库,充分利用了.NET 的 Linq 表达 ...
- 【转载】scipy.stats.norm.ppf —— 分位点函数(CDF的逆)(也被用作“标准偏差乘数”)
原文地址: https://www.cnblogs.com/jiangkejie/p/15292260.html scipy.stats.norm.ppf() 分位点函数(CDF的逆)(也被用作&qu ...
- 鸿蒙NEXT自定义组件:太极Loading
[引言](完整代码在最后面) 本文将介绍如何在鸿蒙NEXT中创建一个自定义的"太极Loading"组件,为你的应用增添独特的视觉效果. [环境准备] 电脑系统:windows 10 ...
- 【一步步开发AI运动小程序】二十、AI运动小程序如何适配相机全屏模式?
引言 受小程序camera组件预览和抽帧图像不一致的特性影响,一直未全功能支持全屏模式,详见本系列文件第四节小程序如何抽帧:随着插件在云上赛事.健身锻炼.AI体测.AR互动场景的深入应用,各开发者迫切 ...