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 方法用于拉取消息。

  1.  
    /**
  2.  
    * Retrieve a message from a queue using {@link com.rabbitmq.client.AMQP.Basic.Get}
  3.  
    * @see com.rabbitmq.client.AMQP.Basic.Get
  4.  
    * @see com.rabbitmq.client.AMQP.Basic.GetOk
  5.  
    * @see com.rabbitmq.client.AMQP.Basic.GetEmpty
  6.  
    * @param queue the name of the queue
  7.  
    * @param autoAck true if the server should consider messages
  8.  
    * acknowledged once delivered; false if the server should expect
  9.  
    * explicit acknowledgements
  10.  
    * @return a {@link GetResponse} containing the retrieved message data
  11.  
    * @throws java.io.IOException if an error is encountered
  12.  
    */
  13.  
    GetResponse basicGet(String queue, boolean autoAck) throws IOException;

basicGet 返回 GetResponse 类。

  1.  
    public class GetResponse {
  2.  
    private final Envelope envelope;
  3.  
    private final BasicProperties props;
  4.  
    private final byte[] body;
  5.  
    private final int messageCount;
  6.  
     
  7.  
    // ...
 

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 拉取消息需要注意:

  1.  
    basicGet
  2.  
    DefaultConsumer

示例代码:

  1.  
    private void consume(Channel channel) throws IOException, InterruptedException {
  2.  
    while (true) {
  3.  
    if (!isConditionSatisfied()) {
  4.  
    TimeUnit.MILLISECONDS.sleep(1);
  5.  
    continue;
  6.  
    }
  7.  
    GetResponse response = channel.basicGet(CAOSH_TEST_QUEUE, false);
  8.  
    if (response == null) {
  9.  
    TimeUnit.MILLISECONDS.sleep(1);
  10.  
    continue;
  11.  
    }
  12.  
    String data = new String(response.getBody());
  13.  
    logger.info("Get message <= {}", data);
  14.  
    channel.basicAck(response.getEnvelope().getDeliveryTag(), false);
  15.  
    }
  16.  
    }

批量拉取消息

RabbitMQ支持客户端批量拉取消息,客户端可以连续调用 basicGet 方法拉取多条消息,处理完成之后一次性ACK。需要注意:

  1.  
    basicGet
  2.  
    basicAck

示例代码:

  1.  
    String bridgeQueueName = extractorProperties.getBridgeQueueName();
  2.  
    int batchSize = extractorProperties.getBatchSize();
  3.  
    List<GetResponse> responseList = Lists.newArrayListWithCapacity(batchSize);
  4.  
    long tag = 0;
  5.  
    while (responseList.size() < batchSize) {
  6.  
    GetResponse getResponse = channel.basicGet(bridgeQueueName, false);
  7.  
    if (getResponse == null) {
  8.  
    break;
  9.  
    }
  10.  
    responseList.add(getResponse);
  11.  
    tag = getResponse.getEnvelope().getDeliveryTag();
  12.  
    }
  13.  
    if (responseList.isEmpty()) {
  14.  
    TimeUnit.MILLISECONDS.sleep(1);
  15.  
    } else {
  16.  
    logger.info("Get <{}> responses this batch", responseList.size());
  17.  
    // handle messages
  18.  
    channel.basicAck(tag, true);
  19.  
    }

关于QueueingConsumer

QueueingConsumer 在客户端本地使用 BlockingQueue 缓冲消息,其nextDelivery方法也可以用于实现拉模式(其本质上是 BlockingQueue.take ),但是 QueueingConsumer 现在已经标记为Deprecated。

Spring Boot中通过RabbitTemplate主动pull(get)消息的例子的更多相关文章

  1. Spring Boot中一个Servlet主动断开连接的方法

    主动断开连接,从而返回结果给客户端,并且能够继续执行剩余代码. 对于一个HttpServletResponse类型的对象response来说,执行如下代码: response.getWriter(). ...

  2. 记一次spring boot中MongoDB Prematurely reached end of stream的异常解决

    在spring boot项目中使用了mongodb,当一段时间没有操作mongodb,下次操作mongodb时就会出现异常.异常如下: org.springframework.data.mongodb ...

  3. Spring Boot中使用缓存

    Spring Boot中使用缓存 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一. 原始的使 ...

  4. Spring Boot中使用RabbitMQ

    很久没有写Spring Boot的内容了,正好最近在写Spring Cloud Bus的内容,因为内容会有一些相关性,所以先补一篇关于AMQP的整合. Message Broker与AMQP简介 Me ...

  5. spring boot中使用servlet、listener和filter

    spring boot中支持使用java Web三大组件(servlet.listener和filter),但是坑比较多,主要是spring boot内嵌tomcat和独立tomcat服务器有一些细节 ...

  6. Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题

    Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...

  7. Spring Boot中快速操作Mongodb

    Spring Boot中快速操作Mongodb 在Spring Boot中集成Mongodb非常简单,只需要加入Mongodb的Starter包即可,代码如下: <dependency> ...

  8. 在 Spring Boot 中使用 HikariCP 连接池

    上次帮小王解决了如何在 Spring Boot 中使用 JDBC 连接 MySQL 后,我就一直在等,等他问我第三个问题,比如说如何在 Spring Boot 中使用 HikariCP 连接池.但我等 ...

  9. spring boot(三):Spring Boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  10. Spring Boot中的事务管理

    原文  http://blog.didispace.com/springboottransactional/ 什么是事务? 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合 ...

随机推荐

  1. iOS之动画(transform和UIView动画)学习

    1.transform 形变 这个是UIView的属性,继承UIView的控件都具有这个属性 UIImageView *imageview=[[UIImageView alloc]init]; ima ...

  2. forEach filter some map every 的区别

    forEach 遍历数组,不会改变原数组,没有返回值 : filter 过滤数组 相同点:都不改变原数组,都是数组的实例方法 :

  3. Android复习(二)应用资源——>更多类型

    更多资源类型 本页面定义了更多类型的可具体化的资源,包括: Bool 带有布尔值的 XML 资源. 颜色 带有颜色值(十六进制颜色)的 XML 资源. 尺寸 带有尺寸值(包含度量单位)的 XML 资源 ...

  4. Oracle ASM 常用巡检脚本

    1.查看磁盘组 sqlplus "/ as sysasm" set line 200 set pagesize 200 select group_number,name,state ...

  5. 一些新奇的玩意【php篇--持续更新】

    人不进步就等于退步! 接触越多的人以及事就能学到更多的东西. 以下仅为本人记录的一些新奇的东西,不喜勿喷! 1.??运算符号,在新的项目中突然发现很多红线报错,还以为是错误!看了下,是??运算的问题, ...

  6. 分享一个大模型在请求api接口上的巧用

    前言 自从Chatgpt横空出世以来,各种智能工具层出不穷,聊天.绘画.视频等各种工具帮助很多人高效的工作.作为一个开发者,目前常用应用包括代码自动填充,聊天助手等. 这些是工具层面的使用,有没有将大 ...

  7. Web渗透10_CSRF SSRF

    1 CSRF漏洞 CSRF 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CS ...

  8. OpenCV3 图像处理笔记

    此笔记针对 Python 版本的 opencv3,c++ 版本的函数和 python 版本的函数参数几乎一样,只是矩阵格式从 ndarray 类型变成适合 c++ 的 mat 模板类型.注意,因为 p ...

  9. antdesign vue 步骤条a-step按审核人员节点排序显示逻辑

    一.需求内容 目前审核人员角色有:学术.法务.售后,串行执行审核流程. 审核流程:发起/修改审核->审核节点 审核节点规则:学术->法务->售后,每个节点均可以审核或修改. 审核状态 ...

  10. openresty操作mongodb

    最近项目中使用openresty,需要通过openresty连接mongo,经过几番折腾终于有了一个结果,现将其记录下来,也感谢模块提供者 使用openresty操作mongo 1.引入第三方的模块 ...