java中RabbitMQ高级应用

目录
  • 1、消息可靠性投递
    • 1.1、确认模式
    • 1.2、退回模式
    • 1.3、确认机制
  • 2、消费端限流
  • 3、消息过期时间
  • 4、死信队列
    • 4.1、死信概念
    • 4.2、延迟队列

1、消息可靠性投递

 在使用 RabbitMQ 的时候,生产者在进行消息投递的时候如果想知道消息是否成功的投递到对应的交换机和队列中,有两种方式可以用来控制消息投递的可靠性模式 。

 由上图的整个消息的投递过程来看,生产者的消息进入到中间件中会首先到达交换机,然后再从交换机传递到队列中去,也就是分为两步走战略。那么消息的丢失情况也就是会出现在这两个阶段中,RabbitMQ 贴心的为我们提供了针对于这两个部分的可靠新传递模式:

  • confirm 模式
  • return 模式

 利用这两个回调模式来确保消息的传递可靠。

1.1、确认模式

 消息从生产者到交换机之间传递会返回一个 confirmCallback 的回调。可以直接在 rabbitTemplate 实例中进行确认逻辑的设置。如果是使用 XML 配置的话需要在工厂配置开启 publisher-confirms="true"YAML 的配置就直接 publisher-confirm-type: correlated,他默认是 NONE ,需要手动开启。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class Producer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void producer() throws InterruptedException {
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println();
                if (!b) {
                    //	消息重发之类的处理
                    System.out.println(s);
                } else {
                    System.out.println("交换机成功接收消息");
                }
            }
        });
        rabbitTemplate.convertAndSend("default_exchange", "default_queue",
                "hello world & beordie");
        TimeUnit.SECONDS.sleep(5);
    }
}

 上面的确认是由一个 confirm 的函数执行的,里面携带了三个参数,第一个是配置的相关信息,第二个表示交换机是否成功的接收到消息,第三个参数是指没有成功接收消息的原因。

1.2、退回模式

 从交换机到消息队列投递失败会返回一个 returnCallback 。在工厂配置中开启回退模式 publisher-returns="true" ,设置交换机处理消息失败的模式(默认 false 直接将消息进行丢弃),添加退回处理的逻辑。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class Producer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void producer() throws InterruptedException {
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                //  重发逻辑处理
                System.out.println(message.getBody() + " 投递消息队列失败");
            }
        });
        rabbitTemplate.convertAndSend("default_exchange", "default_queue",
                "hello world & beordie");
        TimeUnit.SECONDS.sleep(5);
    }
}

returnedMessage 中携带五个参数、分别指的是消息对象、错误码、错误信息、交换机、路由键。

1.3、确认机制

 在消费者抓取消息队列中的数据取消费之后会有一个确认机制进行消息的确认,防止因为抓取消息之后但没有消费成功而导致的消息丢失。有三种确认方式:

  • 自动确认acknowledge="none"
  • 手动确认acknowledge="manual"
  • 根据异常情况确认acknowledge="auto"

 其中自动确认是指一旦消息被消费者抓取就自动默认成功,并将消息从消息队列中进行移除,如果这个时候消费端消费出现问题,那么也会是默认消息消费成功,但是实际上是没有消费成功的,也就是当前的消息丢失了。默认的情况就是自动确认机制。

 如果设置手动确认的方式,就需要在正常消费消息之后进行回调确认 channel.basicAck(),手动签收。如果业务处理过程中发生了异常则调用 channel.basicNack() 重新发送消息。

 首先需要在队列绑定时进行确认机制的配置,设置为手动签收。

<!-- 绑定队列 -->
<rabbit:listener-container connection-factory="rabbitFactory" auto-declare="true" acknowledge="manual">
    <rabbit:listener ref="rabbirConsumer" queue-names="default_queue"/>
</rabbit:listener-container>

 生产者一端不用更改,只需要改变消费者的实现进行消息自动签收就可以了,正常执行业务则签收消息,业务发生错误则选择消息拒签,消息重发或者丢弃。

public class ConsumerAck implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        //  消息唯一ID
        long tag = message.getMessageProperties().getDeliveryTag();
        try {
            String msg = new String(message.getBody(), "utf-8");
            channel.basicAck(tag, true);
            System.out.println("接收消息: " + msg);
        } catch (Exception e) {
            System.out.println("接收消息异常");
            channel.basicNack(tag, true, true);
            e.printStackTrace();
        }
    }
}

 里面涉及三个简单的签收函数,一是正确签收的 basicAck ,二是单条拒签的 basicReject ,三是批量拒签的 basicNack

  • basicAck 第一个参数表示消息在通道中的唯一ID,只针对当前的 Channel;第二个参数表示是否批量同意,如果是 false 的话只会同意签收当前ID的一条消息,将其从消息队列中进行删除,而如果是 true 的话将会把此ID之前的消息一起给同意签收了。
  • basicReject 第一个参数依旧表示消息的唯一ID,第二个参数表示是否重新回队发送,false 表示直接丢弃该条消息或者有死信队列可以接收, true 则表示重新回队进行消息发送,所有操作只针对当前的消息。
  • basicNack 比第二个多了一个参数,也就是处于中间位置的布尔值,表示是否批量进行。

2、消费端限流

 在用户请求和DB服务处理之间增加消息中间件的隔离,使得突发流量全部让消息队列来抗,降低服务端被冲垮的可能性。让所有的请求都往队列中存,消费端只需要匀速的取出消息进行消费,这样就能保证运行效率,也不会因为后台的阻塞而导致客户端得不到正常的响应(当然指的是一些不需要同步回显的任务)。

 只需要在消费者绑定消息队列时指定取出消息的速率即可,需要使用手动签收的方式,每进行一次的签收才会从队列中再取出下一条数据。

<!-- 绑定队列 -->
<rabbit:listener-container connection-factory="rabbitFactory" auto-declare="true"
                           acknowledge="manual" prefetch="1">
    <rabbit:listener ref="rabbirConsumer" queue-names="default_queue"/>
</rabbit:listener-container>

3、消息过期时间

 消息队列提供了存储在队列中消息的过期时间,分为两个方向的实现,一个是针对于整个队列中的所有消息,也就是队列的过期时间,另一个是针对当前消息的过期时间,也就是针对于单条消息单独设置。

 队列的过期时间设置很简单,只需要在创建队列时进行过期时间的指定即可,也可以通过控制台直接创建指定过期时间。一旦队列过期时间到了,队列中还未被消费的消息都将过期,进行队列的过期处理。

<rabbit:queue id="default_queue" name="default_queue" auto-declare="true">
    <rabbit:queue-arguments>
        <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"/>
    </rabbit:queue-arguments>
</rabbit:queue>

 单条消息的过期时间需要在发送的时候进行单独的指定,发送的时候指定配置的额外信息,配置的编写由配置类完成。

 如果一条消息的过期时间到了,但是他此时处于队列的中间,那么他将不会被处理,只有当之后处理到时候才会进行判断是否过期。

MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
    @Override
    public Message postProcessMessage(Message message) throws
        AmqpException {
        //	设置 message 的过期时间
        message.getMessageProperties().setExpiration("5000");
        //	返回该消息
        return message;
    }
};
rabbitTemplate.convertAndSend("exchange", "route", "msg", messagePostProcessor);

 如果说同时设置了消息的过期时间和队列的过期时间,那么最终的过期时间由最短的时间进行决定,也就是说如果当前消息的过期时间没到,但是整个队列的过期时间到了,那么队列中的所有消息也自然就过期了,执行过期的处理策略。

4、死信队列

4.1、死信概念

死信队列指的是死信交换机,当一条消息成为死信之后可以重新发送到另一个交换机进行处理,而进行处理的这个交换机就叫做死信交换机。

  • 消息成为死信消息有几种情况

    队列的消息长度达到限制

    消费者拒接消息的时候不把消息重新放入队列中

    队列存在消息过期设置,消息超时未被消费

    消息存在过期时间,在投递给消费者时发现过期

 在创建队列时可以在配置中指定相关的信息,例如死信交换机、队列长度等等,之后的一系列工作就不由程序员进行操作了,MQ 会自己完成配置过的事件响应。

<rabbit:queue id="default_queue" name="default_queue" auto-declare="true">
    <rabbit:queue-arguments>
        <!-- 死信交换机 -->
        <entry key="x-dead-letter-exchange" value-type="dlx_exchane"/>
        <!-- 路由 -->
        <entry key="x-dead-letter-routing-key" value-type="dlx_routing"/>
        <!-- 队列过期时间 -->
        <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"/>
        <!-- 队列长度 -->
        <entry key="x-max-length" value-type="java.lang.Integer" value="10"/>
    </rabbit:queue-arguments>
</rabbit:queue>

4.2、延迟队列

 延迟队列指的是消息在进入队列后不会立即被消费,只有到达指定时间之后才会被消费,也就是需要有一个时间的判断条件。

 消息队列实际上是没有提供对延迟队列的实现的,但是可以通过 TTL + 死信队列 的方式完成,设置一个队列,不被任何的消费者所消费,所有的消息进入都会被保存在里面,设置队列的过期时间,一旦队列过期将所有的消息过渡到绑定的死信队列中。

 再由具体的消费者来消费死信队列中的消息,这样就实现了延迟队列的功能。

 例如实现一个下单超时支付取消订单的功能:

到此这篇关于java中RabbitMQ高级应用的文章就介绍到这了,更多相关java RabbitMQ内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java RabbitMQ的工作队列与消息应答详解

    目录 WorkQueues 1.轮询分发消息 1.1抽取工具类 1.2编写两个工作线程 1.3编写生产者 1.4运行测试 1.5异常情况 2.消息应答 2.1自动应答 2.2手动应答 2.3消息自动重新入队 2.4手动应答测试 2.4.1生产者代码 2.4.2消费者代码 2.4.3测试 总结 Work Queues 工作队列(任务队列)主要思想是避免立即执行资源密集型任务,而不得不等待它完成,相反我们安排任务在之后执行.我们把任务封装为消息并将其发送到队列.在后台运行的工作进程将弹出任务并最终执

  • Java面试高频问题之RabbitMQ系列全面解析

    1.RabbitMQ是什么? RabbitMQ是一款开源的,Erlang编写的,基于AMQP(高级消息队列协议)协议的消息中间件. 2.为什么要使用消息队列? 从本质上来说是因为互联网的快速发展,业务不断扩张,促使技术架构需要不断的演进. 从以前的单体架构到现在的微服务架构,成百上千的服务之间相互调用和依赖.从互联网初期一个服务器上有 100 个在线用户已经很了不得,到现在坐拥10亿日活的微信.此时,我们需要有一个「工具」来解耦服务之间的关系.控制资源合理合时的使用以及缓冲流量洪峰等等.因此,消

  • Java搭建RabbitMq消息中间件过程详解

    这篇文章主要介绍了Java搭建RabbitMq消息中间件过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 当系统中出现"生产"和"消费"的速度或稳定性等因素不一致的时候,就需要消息队列. 名词 exchange: 交换机 routingkey: 路由key queue:队列 控制台端口:15672 exchange和queue是需要绑定在一起的,然后消息发送到exchange再由exchange通过ro

  • JAVA获取rabbitmq消息总数过程详解

    公司使用的是rabbitMQ,需要做监控预警的job去监控rabbitMQ里面的堆积消息个数,如何使用rabbitMQ获取监控的队列里面的队列消息个数呢? 首先需要创建一个连接,配置文件注入相关的值,然后设置连接的相关信息,创建链接. 导入的包是使用: import com.rabbitmq.client @Value("${spring.rabbitmq.host}") private String host; @Value("${spring.rabbitmq.port}

  • Java RabbitMQ的持久化和发布确认详解

    目录 1.持久化 1.1实现持久化 1.2不公平分发 1.3测试不公平分发 1.4预取值 1.4.1代码测试 2.发布确认 2.1单个确认发布 2.2批量确认发布 2.3异步确认发布 2.4处理未确认的消息 总结 1. 持久化 当RabbitMQ服务停掉以后消息生产者发送过的消息不丢失.默认情况下RabbitMQ退出或者崩溃时,会忽视掉队列和消息.为了保证消息不丢失需要将队列和消息都标记为持久化. 1.1 实现持久化 1.队列持久化:在创建队列时将channel.queueDeclare();第

  • Java编程rabbitMQ实现消息的收发

    java实现rAMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计.消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然. AMQP的主要特征是面向消息.队列.路由(包括点对点和发布/订阅).可靠性.安全. RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python.Ruby..NET.Java.JMS.C.PHP.Actio

  • java远程连接调用Rabbitmq的实例代码

    本文介绍了java远程连接调用Rabbitmq,分享给大家,希望此文章对各位有所帮助. 打开IDEA创建一个maven工程(Java就可以了). pom.xml文件如下 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apac

  • java中RabbitMQ高级应用

    目录 1.消息可靠性投递 1.1.确认模式 1.2.退回模式 1.3.确认机制 2.消费端限流 3.消息过期时间 4.死信队列 4.1.死信概念 4.2.延迟队列 1.消息可靠性投递  在使用 RabbitMQ 的时候,生产者在进行消息投递的时候如果想知道消息是否成功的投递到对应的交换机和队列中,有两种方式可以用来控制消息投递的可靠性模式 .  由上图的整个消息的投递过程来看,生产者的消息进入到中间件中会首先到达交换机,然后再从交换机传递到队列中去,也就是分为两步走战略.那么消息的丢失情况也就是

  • Java RabbitMQ高级特性详细分析

    目录 消息的可靠投递 确认模式 退回模式 Consumer Ack 消费端限流 TTL(Time To Live) 设置某个队列为过期队列 设置单独某个消息过期 死信队列 延迟队列 消息的可靠投递 在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景.RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式. confirm 确认模式 return 退回模式 rabbitmq整个消息投递的路径为: producer—>rabbitmq broker—>

  • Java中ModelMapper 的高级使用

    目录 ModelMapper高级使用 使用实例 实体类 ModelMapper配置类 ModelMapperService类 测试类 测试结果 ModelMapper 高级使用   ModelMapper 是一个 Object To Object 的工具,类似于 MapStruct又不同于 MapStruct.主要原因是 ModelMapper 是利用反射的原理实现的 Object To Object.   ModelMapper 官方API : http://modelmapper.org/u

  • JAVA中正则表达式小总结(整理)

    ]js正则表达式基本语法(精粹):  http://www.jb51.net/article/72044.htm 许多语言,包括Perl.PHP.Python.JavaScript和JScript,都支持用正则表达式处理文本,一些文本编辑器用正则表达式实现高级"搜索-替换"功能.所以JAVA语言也不例外.正则表达式已经超出了某种语言或某个系统的局限,成为被人们广为使用的工具,我们完全可以用它来解决实际开发中碰到的一些实际的问题. 在JDK1.3及之前的JDK版本中并没有包含正则表达式的

  • Java中Spring获取bean方法小结

    Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢? Bean工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级IoC的配置机制.BeanFactory使管理不同类型的Java对象成为可能,应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory基础之上,提供

  • 详谈Java中的Object、T(泛型)、?区别

    因为最近重新看了泛型,又看了些反射,导致我对Object.T(以下代指泛型).?产生了疑惑. 我们先来试着理解一下Object类,学习Java的应该都知道Object是所有类的父类,注意:那么这就意味着它的范围非常广!首先记住这点,如果你的参数类型时Object,那么的参数类型将非常广! <Thinking in Java>中说很多原因促成了泛型的出现,最引人注目的一个原因就是为了创造容器类.这个要怎么来理解呢?我的理解是,可以抛开这个为了创造容器类这个,而是回到泛型的目的是限定某种类型上来.

  • Java中的动态和静态编译实例详解

    Java中的动态和静态编译实例详解 首先,我们来说说动态和静态编译的问题. Q: java和javascript有什么区别?    总结了一下:有以下几点吧: 1.首先从运行环境来说java代码是在JVM上编译成class文件,而javascript则直接在浏览器上加载运行. 2.由第一点可看出,java代码需要编译,而javascript不需要编译. 3.从语言性质来说,java是一种高级编程语言,对变量检查要求严格,javascript只是一个简单的解释性的脚本语言,对变量检查及要求很弱.

  • 全面了解Java中的内部类和匿名类

    Java内部类(Inner Class),类似的概念在C++里也有,那就是嵌套类(Nested Class),乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了解,你会发现Java的设计者在内部类身上的确是用心良苦.学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构.下面从以下几个方面来介绍: 第一次见面 public interface Contents { int value(); } public interface

  • 谈谈Java中整数类型(short int long)的存储方式

    在java中的整数类型有四种,分别是 byte  short int long 其中byte只有一个字节 0或1,在此不详细讲解. 其他的三种类型如下: 1. 基本类型:short 二进制位数:16 包装类:java.lang.Short 最小值:Short.MIN_VALUE=-32768 (-2的15此方) 最大值:Short.MAX_VALUE=32767 (2的15次方-1) 2. 基本类型:int 二进制位数:32 包装类:java.lang.Integer 最小值:Integer.M

随机推荐