转: StringBoot集成Rabbit,根据业务返回ACK


为了维护消息的有效性,当消费消息时候处理失败时候,不进行消费,需要我们根据业务区返回ACK,本项目我使用Redis和ack机制双重保险,保障消息一定能够正确的消费
  • 首先,接着上部分内容,使用Topic,机制(不明白的,可以回顾上部分内容)

  • 上部分内容,我们使用SpringBoot注解,去实现,但是控制权不完全账务,当进行大规模项目时候,不太建议使用


 @RabbitListener(queues = TopicRabbitConfig.USER_QUEUE)
    @RabbitHandler
    public void processUser(String message) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                logger.info("用户侧流水:{}",message);
            }
        });
    }
  • 根据源码分析,当然这里不分析源码,有兴趣的可以多失败几次就ok明白了

  • 在配置类中定义监听器,监听这个序列(AcknowledgeMode.MANUAL是必须的哦)


    /**
     * 接受消息的监听,这个监听客户交易流水的消息
     * 针对消费者配置
     * @return
     */
    @Bean
    public SimpleMessageListenerContainer messageContainer1(ConnectionFactory connectionFactory, TransactionConsumeImpl transactionConsume) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queueMessage());
        container.setExposeListenerChannel(true);
        container.setMaxConcurrentConsumers(1);
        container.setConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); //设置确认模式手工确认
        container.setMessageListener(transactionConsume);
        return container;
    }

这个 TransactionConsumeImpl 要继承ChannelAwareMessageListener,主要说的手动返回ACK就是channel。调用


@Component
public class TransactionConsumeImpl implements ChannelAwareMessageListener {
    private static final Logger logger = LoggerFactory.getLogger(TransactionConsumeImpl.class);
    private static final Gson gson = new Gson();
    @Autowired
    JedisShardInfo jedisShardInfo;
    @Autowired
    ExecutorService threadPool;
    @Autowired
    BoluomeFlowService boluomeFlowService;
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String boby = new String(message.getBody(), "utf-8");//转换消息,我们是使用json数据格式
        threadPool.execute(new Runnable() {   //多线程处理
            @Override
            public void run() {
                Jedis jedis = jedisShardInfo.createResource();
                jedis.sadd(TopicRabbitConfig.TRANSACTION_QUEUE, boby);//添加到key为当前消息类型的集合里面,防止丢失消息
                BoluomeFlow flow = gson.fromJson(boby, BoluomeFlow.class);
                String json = gson.toJson(flow);
                if (boluomeFlowService.insert(flow)) {  //当添加成功时候返回成功
                    logger.info("客户交易流水添加1条记录:{}", json);
                    jedis.srem(TopicRabbitConfig.TRANSACTION_QUEUE, boby);//从当前消息类型集合中移除已经消费过的消息
                    try {
                        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);//手工返回ACK,通知此消息已经争取消费
                    } catch (IOException ie) {
                        logger.error("消费成功回调成功,io操作异常");
                    }
                } else {
                    logger.info("客户交易流水添加失败记录:{}", json);
                }
            }
        });
    }
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); // 消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); // ack返回false,并重新回到队列,api里面解释得很清楚
  • channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); // 拒绝消息

    • true 发送给下一个消费者
    • false 谁都不接受,从队列中删除


Rabbitmq进阶
© 著作权归作者所有
)))); factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);      return factory; }

   //消费者配置  @RabbitListener 和  @Bean SimpleMessageListenerContainer 方式

 /**    * 针对消费者配置     * 方式一 每一个Queue 对应一个SimpleMessageListenerContainer    *                  指定消息接受监听器 MessageListener implements  ChannelAwareMessageListener 接口 自己实现onMessage方法    *    *       作用:       接受消息的监听,这个监听指定Queue(payQueue)客户交易流水的消息    * @return */ /*   @Bean   public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory) {      SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);      container.setQueues(payQueue());      container.setExposeListenerChannel(true);      container.setMaxConcurrentConsumers(1);      container.setConcurrentConsumers(1);      //设置确认模式手工确认      //设置ack方式为手动,增加对应队列的监听器。acknowledge="manual" 则开启ack机制      container.setAcknowledgeMode(AcknowledgeMode.MANUAL);      container.setMessageListener(transactionConsumer);      return container;   }   */ /**    * 方式二  @RabbitListener    * SimpleRabbitListenerContainerFactory 可以生成 RabbitListenerContainer    */ //factory.setAcknowledgeMode(AcknowledgeMode.AUTO);   // 自动响应时 简单配置即可 不需要手动确认是否消费 @RabbitListener(queues = "payQueue")    public void displayMail(Mail mail) throws Exception {      System.out.println("队列监听器1号收到消息"+mail.toString()); }

   //factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);   //需要手动确认消息是否消费时 这样处理 @RabbitListener(queues = "payQueue")   public void process(@Payload Mail mail, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel)throws Exception{      logger.info("rabbit receiver message:{}",mail);      try {         channel.basicAck(deliveryTag, false); }catch (Exception e){         logger.error("process message error: {}",e); }   }

}

方式二 的消息接受MessageListener  实现类 TransactionConsumerImpl


import com.alibaba.fastjson.JSONObject;import com.rabbitmq.client.Channel;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.amqp.core.Message;import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;import org.springframework.amqp.support.converter.SimpleMessageConverter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import po.Mail;

import java.io.IOException;import java.util.concurrent.ExecutorService;

@Service("transactionConsumerImpl")public class TransactionConsumerImpl implements ChannelAwareMessageListener {    private static final Logger logger = LoggerFactory.getLogger(TransactionConsumerImpl.class);

    private SimpleMessageConverter converter = new SimpleMessageConverter();    @Autowired    ExecutorService threadPool;  //  @Autowired    //BoluomeFlowService boluomeFlowService;

    //只有在消息处理成功后发送ack确认,或失败后发送nack使信息重新投递    public void onMessage(final Message message, final Channel channel) throws Exception {        final String boby = new String(message.getBody(), "utf-8");//转换消息,我们是使用json数据格式        Object msg = converter.fromMessage(message);        // todo  mail chuli        System.out.println(JSONObject.toJSONString(msg));        try {            //手工返回ACK,通知此消息已经争取消费            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);        } catch (IOException e1) {            e1.printStackTrace();            System.out.println("消息已重复处理失败,拒绝再次接收  失败...");        }

       /* threadPool.execute(new Runnable() {   //多线程处理            public void run() {                Jedis jedis = jedisShardInfo.createResource();                jedis.sadd(TopicRabbitConfig.TRANSACTION_QUEUE, boby);//添加到key为当前消息类型的集合里面,防止丢失消息                BoluomeFlow flow = gson.fromJson(boby, BoluomeFlow.class);                String json = gson.toJson(flow);

                if (boluomeFlowService.insert(flow)) {  //当添加成功时候返回成功                    logger.info("客户交易流水添加1条记录:{}", json);                    jedis.srem(TopicRabbitConfig.TRANSACTION_QUEUE, boby);//从当前消息类型集合中移除已经消费过的消息                    try {                        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);//手工返回ACK,通知此消息已经争取消费                    } catch (IOException ie) {                        logger.error("消费成功回调成功,io操作异常");                    }                } else {                    logger.info("客户交易流水添加失败记录:{}", json);                }

                try                {                    System.out.println("consumer--:" + message.getMessageProperties() + ":" + new String(message.getBody()));

                    // deliveryTag是消息传送的次数,我这里是为了让消息队列的第一个消息到达的时候抛出异常,处理异常让消息重新回到队列,然后再次抛出异常,处理异常拒绝让消息重回队列                    if (message.getMessageProperties().getDeliveryTag() == 1 || message.getMessageProperties().getDeliveryTag() == 2)                    {                        throw new Exception();                    }                    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); // false只确认当前一个消息收到,true确认所有consumer获得的消息                }                catch (Exception e){                    e.printStackTrace();

                    if (message.getMessageProperties().getRedelivered())                    {                        System.out.println("消息已重复处理失败,拒绝再次接收...");                        try {                            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); // 拒绝消息                        } catch (IOException e1) {                            e1.printStackTrace();                            System.out.println("消息已重复处理失败,拒绝再次接收  失败...");                        }                    }                    else                    {                        System.out.println("消息即将再次返回队列处理...");                        try {                            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); // requeue为是否重新回到队列                        } catch (IOException e1) {                            e1.printStackTrace();                            System.out.println("requeue为是否重新回到队列   失败...");                        }                    }                }            }        });*/    }

}

本地测试代码地址: xxx

StringBoot集成Rabbit Redis和ack机制双重保险,保障消息一定能够正确的消费的更多相关文章

  1. (35)Spring Boot集成Redis实现缓存机制【从零开始学Spring Boot】

    [本文章是否对你有用以及是否有好的建议,请留言] 本文章牵涉到的技术点比较多:Spring Data JPA.Redis.Spring MVC,Spirng Cache,所以在看这篇文章的时候,需要对 ...

  2. Spring Boot从入门到精通(六)集成Redis实现缓存机制

    Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言 ...

  3. RabbitMq + Spring 实现ACK机制

    概念性解读(Ack的灵活) 首先啊,有的人不是太理解这个Ack是什么,讲的接地气一点,其实就是一个通知,怎么说呢,当我监听消费者,正常情况下,不会出异常,但是如果是出现了异常,甚至是没有获取的异常,那 ...

  4. Storm的ack机制在项目应用中的坑

    正在学习storm的大兄弟们,我又来传道授业解惑了,是不是觉得自己会用ack了.好吧,那就让我开始啪啪打你们脸吧. 先说一下ACK机制: 为了保证数据能正确的被处理, 对于spout产生的每一个tup ...

  5. Spring boot集成Rabbit MQ使用初体验

    Spring boot集成Rabbit MQ使用初体验 1.rabbit mq基本特性 首先介绍一下rabbitMQ的几个特性 Asynchronous Messaging Supports mult ...

  6. RabbitMQ的消息确认ACK机制

    1.什么是消息确认ACK. 答:如果在处理消息的过程中,消费者的服务器在处理消息的时候出现异常,那么可能这条正在处理的消息就没有完成消息消费,数据就会丢失.为了确保数据不会丢失,RabbitMQ支持消 ...

  7. Redis键通知机制

    Redis键通知机制 一.概念 自从redis2.8.0以后出了一个新特性,Keyspace Notifications 称为“键空间通知”. 这个特性大概是,凡是实现了Redis的Pub/Sub的客 ...

  8. Redis的主从复制与Redis Sentinel哨兵机制

    1    Redis的主从复制 1.1   什么是主从复制 持久化保证了即使redis服务重启也不会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损 ...

  9. kafkaspot在ack机制下如何保证内存不溢

    新浪微博:intsmaze刘洋洋哥.   storm框架中的kafkaspout类实现的是BaseRichSpout,它里面已经重写了fail和ack方法,所以我们的bolt必须实现ack机制,就可以 ...

随机推荐

  1. editcap的使用

    editcap.exe -h Editcap (Wireshark) (v2.-gf42a0d2b6c) Edit and/or translate the format of capture fil ...

  2. http协议版本历史

    1.http 0.9 2.http 1.0 3. http 1.1 4.http 2.0 推送:主动发送js.css推送到浏览器. 二进制流:可以并行发送数据. 2019.3.18补充: (1)htt ...

  3. Java并发容器——CopyOnWriteArrayList

    CopyOnWriteArrayList是“读写分离”的容器,在写的时候是先将底层源数组复制到新数组中,然后在新数组中写,写完后更新源数组.而读只是在源数组上读.也就是,读和写是分离的.由于,写的时候 ...

  4. sqlserver 建表语句,获取建表语句的存储过程,包括排序规则,索引,字段说明,支持同时生成多个表

    先创建一个分割表名的分割函数 --表值函数用以截取字符串 --如果为其添加一列主键id,则其顺序就会固定了 create FUNCTION [Split](@text NVARCHAR(max)) ) ...

  5. Knockout学习之表单绑定器(下)

    “hasFocus”绑定 hasFocus绑定器会将DOM元素的焦点状态与视图模型中的属性相关联,当你设置视图模型中关联的属性为true或false后,将能够设置关键的DOM元素是否获得焦点. 比如下 ...

  6. 使用PHP做移动端 api接口开发方法(适用于TP框架)

    1. [代码]使用TP框架时 放在common文件夹下文件名就叫function.php ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...

  7. (原)tensorflow中提示CUDA_ERROR_LAUNCH_FAILED

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/6606092.html 参考网址: https://github.com/tensorflow/tens ...

  8. swift类型转换之Could not cast value of type xxx to xxx错误的一种特殊情况记录

    之前swift项目打包成Framework静态库,提供给OC项目套入使用时,有时会抱这样一个错误: 这个错误发生的概率比较随机,有时会,有时不会,而且这句话在swift中的使用,是做model类型强制 ...

  9. centos7用xshell可以连接, xftp连接失败!(墙裂推荐)

    环境: vultr vps搭建的是centos7  毛病: xshell可以连接, 打开xftp配置好连接却连接失败如下图: 然后开始设置ssh的配置文件vim /etc/ssh/sshd_confi ...

  10. Solr删除managedschema

    一.创建solr核心 solr createcore 二.删除managedschema managedschema是solr自动生成的,里面包含大量无用配置. solr是检查用户定义的schema. ...