springboot2.x +rabbitmq使用和源码分析二(生产者配置)

springboot2.x +rabbitmq使用和源码分析四(消费者-Listener使用) 序言: 当数据通过生产者产生到发送到exchange交换器,再通过设定的路由规则,经过routingKey,最终会落地到queue 中。这个时候引出了Listener消费者了。在第一章中有对RabbitAnnotationDrivenConfiguration该类中源码做过基本分析,该类最大的作用就是通过配置文件的设定用于构建RabbitListenerContainerFactory(该接口的实现,工厂设计模式用以创建核心的MessageListenerContainer 容器类,而该类Message 阅读详情

1:手动构建RabbitmqQueueExchangeAutoConfiguration

该类用于初始化 queue exchange 并进行Binding绑定

package com.fc.rabbitmq_demo.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import static com.fc.rabbitmq_demo.config.CommonConstant.*;

/**
 *初始化 queue exchange 并进行Binding绑定
 * @author fanyuan
 */
@Configuration
public class RabbitmqQueueExchangeAutoConfiguration {


    /**
     * 默认有四种bean 对于rabbitmq四种交换器
     * DirectExchange: 直连型 和queue一一对应
     * FanoutExchange: 广播型 广播exchange下多有queue
     * TopicExchange: 通过routeKey和 Binding Key 进行模糊匹配 进行路由发送queue
     * HeadersExchange:而根据消息中的 Headers 和创建绑定关系时指定的 Arguments 来匹配决定路由到哪些 Queue基本不用
     */

    /*
     * 设置交换器 exchange
     */
    @Bean
    public Exchange directExchange(){
        return new DirectExchange(exchange_direct);
    }

    @Bean
    public Exchange fanoutExchange(){

        return new FanoutExchange(exchange_fanout);
    }

    @Bean
    public Exchange topicExchange(){

        return new TopicExchange(exchange_topic);
    }

    /**
     * Queue创建有4个属性
     * @param name 队列名称
     * @param durable 设置是否为持久性队列 如果为持久队列,则为true(该队列将在服务器重新启动后继续有效,否则重启后失效) 默认为true
     * @param exclusive 设置是否为独占队列(该队列将仅由声明者的连接使用) 如果声明独占队列,则为true 默认为false
     * @param autoDelete 设置是否自动删除 当该队列不再使用了是否自动删除
     */

    /*创建queue*/

    /**
     * 声明创建一个queue
     * @return
     */
    @Bean
    public Queue directQueue1(){
        return  new Queue(queue_direct1,true,false,false);
    }

    @Bean
    public Queue directQueue2(){
        return  new Queue(queue_direct2,true,false,false);
    }

    @Bean
    public Queue fanoutQueue1(){
        return  new Queue(queue_fanoutQueue1,true,false,false);
    }

    @Bean
    public Queue fanoutQueue2(){
        return  new Queue(queue_fanoutQueue2,true,false,false);
    }

    @Bean
    public Queue topicQueue1(){
        return  new Queue(queue_topic1,true,false,false);
    }

    @Bean
    public Queue topicQueue2(){
        return  new Queue(queue_topic2,true,false,false);
    }


    /* 将exchange与topic进行绑定*/

    @Bean
    public Binding directBinding1(){

        return BindingBuilder.bind(directQueue1()).to(directExchange()).with(routingKey_direct1).noargs();
    }

    @Bean
    public Binding directBinding2(){

        return BindingBuilder.bind(directQueue2()).to(directExchange()).with(routingKey_direct2).noargs();
    }

    @Bean
    public Binding fanoutBinding1(){

        return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange()).with("").noargs();
    }

    @Bean
    public Binding fanoutBinding2(){

        return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange()).with("").noargs();
    }

    @Bean
    public Binding topicBinding1(){
        return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with(routingKey_topic1).noargs();
    }

    @Bean
    public Binding topicBinding2(){

        return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with(routingKey_topic2).noargs();
    }


}

在项目中将需要创建的exchange和queue进行 binding完成之后 ,此时我们去构建生产者相关配置,

2:RabbitmqProducerAutoConfiguration

该类用于对生产者进行配置,包含对公共的异步ack消息处理

/**
 * 对生产者进行设置
 * @author fangyuan
 */
@Configuration
@Slf4j
public class RabbitmqProducerAutoConfiguration {

    /**
     *
     * 若需要 使用到returnCallback 与confirmCallback 生产者确认机制
     * 其中confirmCallback:消息从生产者到达exchange时返回ack,消息未到达exchange返回nack(ack为false);
     * returnCallBack:消息进入exchange但未进入queue时会被调用  这个时候说明路由失败了 需要进行 设置
     * 需要在配置中使用以下配置:
     * publisher-returns: true
     * publisher-confirm-type: correlated
     *
     */

    /**
     * 设置生产者的生产消息的ack信息回调(公共处理)
     * @return
     */
    @Bean
    public RabbitTemplate.ConfirmCallback confirmCallback(){

        //返回
        return (correlationData, ack, cause)->{
            //我们可以通过correlationData原始数据 来对消息进行后续处理,当时这是有个要求在于发送必须使用CorrelationData类
            //成功
            if(ack){
                log.info("消息发送成功!!!!!,消息data:{},时间:{}",correlationData,System.currentTimeMillis());
            }else {
                log.error("消息发送失败!!!!,原因是:{}",cause);
            }

        };
    }

    /**
     * 发送者失败通知
     */
    @Bean
    public RabbitTemplate.ReturnCallback returnCallback(){

        //构建一个
        return (Message message, int replyCode, String replyText, String exchange, String routingKey)->{
            log.error("发送者路由失败,请检查路由 Returned replyCode:{} Returned replyText:{} Returned routingKey:{} Returned message:{}"
                    ,  replyCode,replyText,routingKey,new String(message.getBody()));
        };
    }

    //设置RabbitTemplate
    @Bean
    public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate();
        configurer.configure(template, connectionFactory);
        //若同步阻塞该ConfirmCallback 不用设置
//        template.setConfirmCallback(confirmCallback());
        template.setReturnCallback(returnCallback());
        return template;
    }

}

 

3: application.yml 基础设置

spring:
  rabbitmq:
    addresses: localhost:5672
    username: admin
    password: admin
    virtual-host: my_vhost
    ##通过returnCallback
    publisher-returns: true
    ##通过confirmCallback
    publisher-confirm-type: simple
    template:
      mandatory: true

4: 生产者API测试

4.1:测试生产者同步与异步ACK确认机制

同步阻塞ACK确认发送,需要将    publisher-confirm-type: simple 并且template中不需要设置ConfirmCallback。

 //测试发送阻塞消息
    @Test
    public void testSendMessageSync(){

        RabbitTemplate rabbitTemplate = rabbitMessagingTemplate.getRabbitTemplate();

        Boolean flag = rabbitTemplate.invoke(operations -> {
            rabbitTemplate.convertAndSend(CommonConstant.exchange_direct, CommonConstant.routingKey_direct2, "测试数据direct2");
            log.info("发送message==============》");
            //同步阻塞
            return rabbitTemplate.waitForConfirms(3 * 1000);
        });
        //同步ack确认
        if(flag){
            log.info("消息发送成功");
        }else {
            //消息消费失败 后续处理 重发(消费者需要进行幂等控制)或其它
            log.error("消息发送失败");
        }
    }

可以在rabbitmq控制台可以看到该条消息被创建但未消费

异步ACK确认发送, publisher-confirm-type: correlated 并且在设置ConfirmCallback,可以统一设置ConfirmCallback,也每种发送定制一种ConfirmCallback。

公共设置可参考RabbitmqProducerAutoConfiguration中。send代码如下:

 //定制
@Test
    public void testSendMessageASyncCustomized(){

        RabbitTemplate rabbitTemplate = rabbitMessagingTemplate.getRabbitTemplate();

        rabbitTemplate.convertAndSend(CommonConstant.exchange_direct, CommonConstant.routingKey_direct2, "测试数据direct2");

}

运行测试代码可以查看到日志,得到:

从上述可以发现correlationData为null,我们需要在send时创建correlationData被写入。代码如下所示:

@Test
    public void testSendMessageASyncCustomized(){

        RabbitTemplate rabbitTemplate = rabbitMessagingTemplate.getRabbitTemplate();
        
        CorrelationData correlationData = new CorrelationData();
        correlationData.setId("1");

        rabbitTemplate.convertAndSend(CommonConstant.exchange_direct, CommonConstant.routingKey_direct2, "测试数据direct2",correlationData);
}

最终结果如下所示,correlationData不会为空,并且携带的id包含在呢。

查看CorrelationData源码发现,携带的数据很少,若回调ack中携带更加丰富消息,例如exchange,queue,retry次数,我们可以重写correlationData。


//这里需要对CorrelationData进行序列化 后续说到消息转换器说到
//默认支持 string 实现了Serializable的java类 byte[]
@Data
public class CorrelationData extends org.springframework.amqp.rabbit.connection.CorrelationData implements Serializable {

    //消息体
    private volatile Object message;
    //交换机
    private String exchange;
    //路由键
    private String routingKey;
    //重试次数
    private int retryCount = 0;

}

再次发送,最终的结果如下所示:

 

定制发送可以通过在send时候指定如下所示:

4.3:测试ReturnCallback作用

指定发送一个消息到不存在的queue中,发现执行了以下代码

日志打印结果:

发送者路由失败,请检查路由 Returned replyCode:312 Returned replyText:NO_ROUTE Returned routingKey:exchange:com.fc.exchange.direct Returned message:routingKey:com.fc.routingKey.direct1

发现 ConfirmCallback中得到的结果ack结果为成功,所以这也证明了rabbitmq的一个机制,数据先到exchange,exchange这时候发送一个ack机制,但并不代表这条消息就真的能落地了,若exchange路由queue发生了异常,若我们不设置ReturnCallback机制,就会出现丢数据的情况。在实际工作中,若我们面对的是消息一致性要求高,不要丢失数据,我们需要对ReturnCallback与ConfirmCallback要同时设置并就行补偿流程设置

4.3:测试不同的exchange与queue的实际效果

测试代码如下:

 @Test
    public void testSendMessageBatchASync(){

        //convertAndSend 使用此方法,交换机会马上把所有的信息都交给所有的消费者,消费者再自行处理,不会因为消费者处理慢而阻塞线程。
        //convertSendAndReceive 使用此方法,当确认了所有的消费者都接收成功之后,才触发另一个convertSendAndReceive
        //发送direct类型数据
        log.info("开始发送!!!!");
        RabbitTemplate rabbitTemplate = rabbitMessagingTemplate.getRabbitTemplate();

        //发送direct
        rabbitTemplate.convertAndSend(CommonConstant.exchange_direct, CommonConstant.routingKey_direct1,"测试数据direct1");
        rabbitTemplate.convertAndSend(CommonConstant.exchange_direct, CommonConstant.routingKey_direct2, "测试数据direct2");
        //发送fanout类型数据
        rabbitTemplate.convertAndSend(CommonConstant.exchange_fanout, "", "测试数据fanout1");
        rabbitTemplate.convertAndSend(CommonConstant.exchange_fanout, "", "测试数据fanout2");
        //发送topic类型数据
        rabbitTemplate.convertAndSend(CommonConstant.exchange_topic, CommonConstant.routingKey_c_topic1, "测试数据topic1");
        rabbitTemplate.convertAndSend(CommonConstant.exchange_topic, CommonConstant.routingKey_c_topic2, "测试数据topic2");


    }

我们通过对exchange 3种常用进行测试,期盼的上述顺序队列种的数据条数为为1,1,2,2,1,1

注:在执行多条写入的过程中会出现这样的情况,后面几条中ack为false,异常信息为clean channel shutdown; protocol method: #method<channel.close>(reply-code=200, reply-text=OK, class-id=0, method-id=0),但是发现queue队列中却写入成功了。这是因为上述代表运行在单元测试环境中,有因为是异步回调,所以存在主方法执行完成,rabbitmq通道被关闭,最终ack正确消息无法被回调,就出现如下情况。我们只需要对主方法sleep即可

最终执行结果为:

5:批量发送

AMQP在协议上规定每次只能传送一条数据,因此做批量数据操作,需要在应用层上定义,Spring 目前通BatchingRabbitTemplate在上层应用中提供了批量使用( 所谓批量, 就是spring 将多条message重新组成一条message, 发送到mq, 从mq接受到这条message后,在重新解析成多条message),BatchingRabbitTemplate定义如下所示:

@Bean
    public BatchingRabbitTemplate batchingRabbitTemplate(ConnectionFactory connectionFactory){

        //可自定义对该pool设置
        ThreadPoolTaskScheduler taskScheduler=new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(5);
        taskScheduler.setDaemon(true);
        taskScheduler.setThreadNamePrefix("rabbitmq-");
        taskScheduler.setAwaitTerminationSeconds(60);
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化
        taskScheduler.initialize();
        //一次批量的数量
        int batchSize=10;
        // 缓存大小限制,单位字节,
        // simpleBatchingStrategy的策略,是判断message数量是否超过batchSize限制或者message的大小是否超过缓存限制,
        // 缓存限制,主要用于限制"组装后的一条消息的大小"
        // 如果主要通过数量来做批量("打包"成一条消息), 缓存设置大点
        // 详细逻辑请看simpleBatchingStrategy#addToBatch()
        int bufferLimit=1024; //1 K
        long timeout=10000;

        //注意,该策略只支持一个exchange/routingKey
        BatchingStrategy batchingStrategy=new SimpleBatchingStrategy(batchSize,bufferLimit,timeout);

        return  new BatchingRabbitTemplate(connectionFactory,batchingStrategy,taskScheduler);
    }

测试发送代码:

 @Test
    public void testBatchSendMessage(){
        // 除了send(String exchange, String routingKey, Message message, CorrelationData correlationData)方法是发送单条数据
        // 其他send都是批量
        String msg;
        Message message;
        MessageProperties messageProperties=new MessageProperties();
        for(int i=0;i<1000;i++){
            msg="测试批量数据"+i;
            message=new Message(msg.getBytes(), messageProperties);
            batchingRabbitTemplate.send(CommonConstant.exchange_direct,CommonConstant.routingKey_direct2,message);
        }

        System.out.println("发送数据完毕");
    }

//ack确认与非ack确认最终实现效果一致 写入mq都为100条
@Test
    public void testBatchSendMessageAck(){
        // 除了send(String exchange, String routingKey, Message message, CorrelationData correlationData)方法是发送单条数据
        // 其他send都是批量
        String msg;
        Message message;
        MessageProperties messageProperties=new MessageProperties();
        for(int i=0;i<1000;i++){
            msg="测试批量数据"+i;
            message=new Message(msg.getBytes(), messageProperties);
//            batchingRabbitTemplate.send(CommonConstant.exchange_direct,CommonConstant.routingKey_direct2,message);
            Message finalMessage = message;
            Boolean flag = batchingRabbitTemplate.invoke(operations -> {
                batchingRabbitTemplate.convertAndSend(CommonConstant.exchange_direct, CommonConstant.routingKey_direct2, finalMessage);
                log.info("发送message==============》");
                //同步阻塞
                return batchingRabbitTemplate.waitForConfirms(3 * 1000);
            });
            //同步ack确认
            if(flag){
                log.info("消息发送成功");
            }else {
                //消息消费失败 后续处理 重发(消费者需要进行幂等控制)或其它
                log.error("消息发送失败");
            }

        }

        System.out.println("发送数据完毕");
    }

我们共发送了1000条数据, 通过控制台查看最终结果:

 

从上面数据存储来看,spring将10(batchSize设置)条 数据压缩成一条发送给mq(为何1000变化为100),一般我们通过batch发送的数据(为何这么处理原因在于 batchlisteren看到headers头中包含amqp_batchSize,会讲数据进行解压还原为10条),关于批量消费详情查看后续对消费者描述

6:设置消息的TTL

在某些mq的应用场景中,消息是有过期时间,例如:我们一般对于发送验证码短信这个动作都会通过mq来进行异步推送,而每一个验证码都有属于自己的过期时间(短的1min长的有半个小时),如果我们mq因为某些原因阻塞了,本该马上发出的消息,却在1min之后发出,那么这时候发出的短信毫无意义。所以我们需要进行设置消息的TTL,一旦超时,就写入到死信队列(关于此可以参考后文的消费者),这时候监控消息队列的cousumer就会获取该动作,触发重新获取验证码的动作。

目前rabbitmq有两个地方可以设置ttl:

  1. 第一种方式是通过队列属性设置,队列中所有消息都有相同的过期时间
  2. 第二种方式是对消息本身进行单独的设置,每条消息的TTL可以不同,在发送时指定

测试代码演示:

第一种:构建一个ttl的队列并设置ttl=10s,并绑定死信队列,在发送20条message,在超时时间内不进行消费

构建队列代码: 

 @Bean
    public Queue ttlQueue(){

        Map<String, Object> arguments  = new HashMap<>();

        arguments.put("x-message-ttl",10*1000);

        //声明当前死信的 Exchange 10s超时

        arguments.put("x-dead-letter-exchange",exchange_deadLetter_direct);

        arguments.put("x-dead-letter-routing-key",routingKey_deadLetter);

        return  new Queue(queue_ttl,true,false,false,arguments);
    }

    @Bean
    public Binding ttlBinding(){

        return BindingBuilder.bind(ttlQueue()).to(directExchange()).with(routingKey_ttl).noargs();
    }

发送端:

 @Test
    public void testSendMessageTTL(){

        for (int i=0;i<20;i++){
            rabbitTemplate.convertAndSend(CommonConstant.exchange_direct,CommonConstant.routingKey_ttl,"这是一条测试数据"+i);
        }


    
        try {
            Thread.sleep(60*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

发送之前:

ttl队列: 

死信队列: 

 第二种 指定message的过期时间,不用设置队列TTL

@Test
    public void testSendMessageTTL(){
        //

        for (int i=0;i<20;i++){
            rabbitTemplate.convertAndSend(CommonConstant.exchange_direct,CommonConstant.routingKey_ttl,"这是一条测试数据"+i);
        }


        for (int i=0;i<20;i++){
            rabbitTemplate.convertAndSend(CommonConstant.exchange_direct,CommonConstant.routingKey_ttl,"这是一条测试数据"+i,(message->{
                //设置过期时间时间
                message.getMessageProperties().setExpiration("10000");
                return message;
            }));
        }

        try {
            Thread.sleep(60*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

这里20条不设置ttl,20条设置10sttl

最终结果:

ttl开始包含40条

经过10s后,有20条消息进入死信队列中,剩下20条仍在队列中

 

通过上述测试发现,在无消费者消费的情况,一旦设置超过了过期时间数据被清除,如果设置了死信则转储死信队列。

6:设置消息优先级

还是拿短信发送来描述该功能的作用,在实际中我们会将短信发送进行异步处理,而短信发送来源很多,例如有登陆验证码短信,注册验证码短信,交易通知短信,营销类的短信。这些短信我们不可能根据类型来构建不同的queue,通常都会存放到一个队列中,那么在大量消息到来之后,消费者消费能力不足就会产生消息堆积。那么对于交易通知短信这种很重要的短信就会因为营销类短信先到而无法发送。如果队列能提供一种level的概念,那么对于等级高的,在消费能力不足的情况下,等级高的先被消费,等级低的后消费。Rabbitmq就通过对队列设置x-max-priority(设置当前队列最大的level等级)参数,对消息设置Priority参数来做到。

代码如下所示:

queue构建:

  @Bean
    public Exchange priorityQExchange(){
        return new DirectExchange(exchange_priority);
    }

    /**
     *
     * @return
     */
    @Bean
    public Queue priorityQueue(){

        Map<String, Object> arguments  = new HashMap<>();
        arguments.put("x-max-priority",200);

        return  new Queue(queue_priority,true,false,false,arguments);
    }


    @Bean
    public Binding priorityBinding(){
        return BindingBuilder.bind(priorityQueue()).to(priorityQExchange()).with(routingKey_priority).noargs();
    }

发送者:

  @Test
    public void testSendMessagePriority(){

        for (int i=1;i<200;i++){
            final  int j = i;
            rabbitTemplate.convertAndSend(CommonConstant.exchange_priority,CommonConstant.routingKey_priority,"这是一条测试数据"+i,message -> {
                //设置
                message.getMessageProperties().setPriority(j);
                return message;
            });
        }
        
        try {
            Thread.sleep(600*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

最终结果:

在Rabbitmq控制台优先级队列中都有Pri标示

 消费者:

注意:spring.ribbitmq.simple.prefetch的设置,该值代表每个customer会在MQ预取一些消息放入内存的LinkedBlockingQueue中,这个值越高,消息传递的越快,但非顺序处理消息的风险更高,如果ack模式为none,则忽略。如有必要,将增加此值以匹配txSize或messagePerAck。从2.0开始默认为250;设置为1将还原为以前的行为。prefetch默认值以前是1,会导致高效cousumer的利用率不足(频繁和服务端进行交互)。从spring-amqp 2.0版开始,默认的prefetch值是250,这将使消费者在大多数常见场景中保持忙碌,从而提高吞吐量。

不过在有些情况下,尤其是处理速度比较慢的大消息,消息可能在内存中大量堆积,消耗大量内存;以及对于一些严格要求顺序的消息,prefetch的值应当设置为1,上述中如果要使用到消息优先级改值的也不易设置过大。

对于低容量消息和多个消费者的情况(也包括单listener容器的concurrency配置)希望在多个使用者之间实现更均匀的消息分布,建议在手动ack下并设置prefetch=1

对于消费端更加详细的介绍。可阅读springboot官方文档:https://docs.spring.io/spring-amqp/reference/html/#amqp

Demo地址:https://github.com/fangyuan94/rabbitmq_demo.git 

SpringBoot学习路径:从入门到精通的三个阶段 SpringBoot作为Java企业级开发的主流框架,其核心价值在于简化配置和提升开发效率。通过自动装配机制和Starter依赖管理,开发者可以快速构建应用。理解其底层原理如条件化配置和Environment体系,能实现深度定制。在生产环境中,性能调优、安全防护和云原生适配是关键。本文以SpringBoot学习路径为主线,剖析基础应用、深度定制和生产级精通三个阶段的核心技术点,涉及自动装配、Redis缓存、RabbitMQ消息队列等热词技术,为开发者提供系统化的学习指南。 阅读详情

相关推荐

springboot2.x整合rabbitmq

首先请确保你的rabbitmq服务已经打开,或者百度搜索安装 Exchange 类型 Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers 。只说前三种模式。 1.Direct模式 消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中。路由键与队列名完全匹配 2.Topic模式 topic 交换器通过模式匹配分配消息的路由键属性,将路由键.

272

BatchingRabbitTemplate 允许开发者将多个消息组合成一个批次

是 Spring AMQP 提供的一个强大工具,用于提高消息发送的性能和效率。通过批处理消息,它可以减少网络开销和连接次数,从而在高并发或大规模消息发送的场景下提供显著的性能提升。然而,在使用时,开发者需要注意合理配置批处理大小和发送延迟,并妥善处理可能出现的异常和资源释放问题。

wddblog的博客 892

RabbitMQ之生产批量发送

timeout :超过收集的时间的最大等待时长,单位:毫秒。不过要注意,这里的超时开始计时的时间,是以最后一次发送时间为起点。但是 RabbitMQ 并没有提供了批量发送消息的 API 接口,使用 spring-amqp 的 BatchingRabbitTemplate 实现批量能力。不过值得注意的是,我们一次发送十条消息到 RabbitMQ Broker 中去,在 RabbitMQ Broker 显示的也是 1 个消息。bufferLimit :超过收集的消息占用的最大内存。

优快云_LiMingfly的博客 2130

安卓 实现灯光闪过效果

有个小需求,给按钮加一个灯光划过的效果,这样的需求一般有UI,肯定就找UI了要个gif图不就完事了,可是这次没有,好久自己做一个嘛。Web端的大佬实现很快,到安卓这边呢,百度一搜发现诶?怎么没有,好吧自己来一个 一切动效相关的不给图看都是耍流氓 所以俺不能这么干啊,OK!直接上效果图,再来解释下怎么实现的 效果就是这样子,怎么实现呢? 从图分析下,就是一个光写着重左边移动要右边循环嘛,这有啥难的?? 来看一看我的第一版,第二版 这是个什么?????一点都不像啊? 赶紧再问了问Web端大佬,噢,加了一个

SummerCloudXT的博客 1378

BEVFusion(mit)最强环境安装,部署复现

BEVFusion作为一项经典的工作,值得深入探索。然而由于配置环境比较麻烦,耽误了一些时间。经过几次成功配置之后,有了一些总结,在这里记录下来,供大家参考避坑,也方便自己下次查阅。

qq_45783225的博客 1万+

rabbit是否支持批量发送?

Rabbit是否支持批量发送消息 该问题笔者翻阅官方文档与三方博客也没有找到答案,后也是自己去翻阅源码后才大概找到一个不敢确定的答案:

qq_35040959的博客 1287

图像处理(12)--图像各种噪声及消除方法

文章目录结构1.什么是图像噪声2. 图像噪声来源3. 噪声分类及其消除方法3.1 高斯噪声及其消除方式3.1.1 如何确定一种噪声是高斯噪声3.1.2 高斯滤波消除高斯噪声3.1.3 高斯函数的重要性质3.2 瑞利噪声及其消除方式3.3 伽马(爱尔兰)噪声及其消除方式3.4 指数分布噪声及其消除方式3.5 均匀噪声及其消除方式3.6 脉冲(椒盐)噪声及其消除方式 1.什么是图像噪声 图像噪声是指...

ShaneHolmes 11万+

消息队列 - Spring Boot 对rabbitmq批量处理数据的支持

消息队列 - Spring Boot 对rabbitmq批量处理数据的支持 一丶前言   在生产中,存在一些场景,需要对数据进行批量操作。如,可以先将数据存放到redis,然后将数据进行批量写进数据库。但是使用redis,不得不面对一个数据容易丢失的问题。也可以考虑使用消息队列进行替换,在数据持久化,数据不丢失方面,消息队列确实比redis好一点,毕竟设计不一样。是不是使用消息队列,就一...

u013262689的专栏 7582

当传统CFD遇上AI:PINN如何帮我们省下90%的仿真计算时间?

本文探讨了物理信息神经网络(PINN)在计算流体力学(CFD)中的应用,如何通过无网格方法节省90%的仿真计算时间。PINN将物理定律编码到神经网络中,显著减少传统CFD中的网格生成和计算时间,同时保持高精度。文章还对比了PINN与传统方法的性能,并讨论了其独特优势和当前局限。

weixin_32678995的博客 320

霍尔式流量计背后的技术和算法

例如,在一项实验中,当流量低于最小操作范围(0.250 L/min)时,误差比其他流量大,但仍然在95%的精度范围内可接受。例如,在Honeywell电子学的测试中,使用30英寸水柱的压力计和热电偶测量气体特性,记录并计算实际和理论质量流量,以确定修正系数。霍尔式流量计的校准系数是通过一系列精确的测量和计算步骤确定的,以确保其测量结果的准确性和可靠性。霍尔式流量计的校准系数是通过严格的环境条件、精确的测量步骤和实验数据确定的。其中,Qs是标尺介质的流量,K是修正系数,Qm是用户介质的流量。

百态·数智思维 | 聚焦大数据、量化与未来AI 1234

springboot2.x +rabbitmq使用和源码分析一(自动装配)

序言: 之前描述过springboot对kafka对支持,这里主要描述springboot对于rabbitmq的支持。这里建立在对rabbitmq有一定了解,若是一个新手建议参考官网结合一些博客来自我学习。 springboot对于rabbitmq各核心组件初始化都在:autoconfigure包下 (这里用的springboot版本较新2.3.1) 上面描述的这些类中,主要例举一个核心类并对其介绍: 1: RabbitProperties 该类从命名上也知道它是一个属性配置文件类,映..

F_Hello_World的博客 1096

19、多项式基算术:原理与运算详解

本文深入探讨了有限域 $GF(2^m)$ 中的多项式基算术,详细介绍了加法、减法、乘法和平方运算的原理与实现方法。重点分析了多项式基表示形式、模约简技术以及乘法优化策略,如高基数乘法、并行与顺序乘法器设计,并提到了Karatsuba-Ofman算法在减少乘法次数方面的优势。此外,文章还讨论了多项式基算术在密码学、编码理论等领域的应用前景,并展望了未来在高效算法设计与硬件加速方面的研究方向。

yellow的专栏 150

Spring Boot2.X整合消息中间件RabbitMQ原理简浅探析

RabbitMQ是一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。消息中间件最主要的作用还是解耦,中间件最标准的用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理,生产者不用关心是谁来消费,消费者不用关心谁在生产消息,从而达到解耦的目的。在分布式的系统中,消息队列也会被用在很多其它的方面,比如:分布式事务的支持,RPC的调用等等。 文章目录 1、简单概述RabbitMQ重要作用 1.1、异步

宜春 6447

【机器学习与数据挖掘实战 | 商务】案例03:基于k近邻算法的非侵入式电力负荷监测与分解的电力分析

本案例结合非侵入式电力负荷监测与分解的案例,重点介绍了在数据可视化的辅助下k近邻算法的应用。首先利用数据可视化寻找数据的特征,构建特征集合。然后构建k近邻模型,利用构建的特征集合训练模型,接着利用该模型对单一设备所属类别进行判定,最后计算实时用电量。

Morse_Chen的博客 2303

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

RabbitMQ以及整合SpringBoot2.x实战

RabbitMQ以及整合SpringBoot2.x实战 1 参考url ①https://www.bilibili.com/video/BV1dE411K7MG?from=search&seid=6293595653131678657 ②https://www.bilibili.com/video/BV1KW411F7oX?p=1 2 MQ的引言和不同MQ的特点 2.1 什么是MQ MQ(Message Quene),翻译为消息队列,别名为消息中间件,是构成生产者和消费者模型的基础。生产者不断向消息

chenweijunhy的博客 713

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

springboot2.x +rabbitmq使用和源码分析三(消息转换器)

序言: 在第一篇和第二篇中,描述了自动装配的过程以及如何发送消息到mq。这里会涉及到将数据转化的工作,也就是如何将我们string或者java对象转化到二进制数据,传输到rabbitmq服务器中(在网络中只能传输二进制数据),而又是如何将到rabbitmq服务器传输的二进制数据转化为监听者需要的实体类型,这里就用到了消息转换器(也就是MessageConverter接口,在RabbitAnnotationDrivenConfiguration我们可以看到有该类的定义并将该类与监听工厂类绑定,在Rabbit

F_Hello_World的博客 861

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

MQ消息中间件之RabbitMQ以及整合SpringBoot2.x实战教程

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录系列文章目录前言一、MQ引言1.1 什么是MQ1.2 MQ有哪些1.3 不同MQ特点1. ActiveMQ2. Kafka3. RocketMQ4. RabbitMQ二、使用步骤1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器

m0_50932526的博客 1420

SpringBoot2.x系列教程63--SpringBoot整合消息队列之RabbitMQ详解

SpringBoot2.x系列教程63--SpringBoot整合消息队列之RabbitMQ详解 作者:一一哥 一. RabbitMQ 简介 1. RabbitMQ 背景 RabbitMQ起源于金融系统,主要用于分布式系统的内部各子系统之间的数据存储转发,这是系统解耦方面的一种运用. 2. RabbitMQ 概述 RabbitMQ是一种基于erlang语言开发的流行的开源消息中间件,或...

一一哥 1166

Springboot 2.x ——RabbitTemplate为什么会默认消息持久化?

文章目录前言springboot测试测试现象源码分析联想 前言 之前在Java直接测试mq消息持久化时,采取如下的配置实现消息的持久化: //消息持久化测试 Builder builder = new Builder(); builder.deliveryMode(2); BasicProperties properties = builder.build(); channel.basicPublish("", queue_name, properties, string.getBytes()); 其中针

专注写bug 2402

Netty4.x: Server端 设置 option 警告 Unknown channel option ‘xxxx‘ for channel 分析及解决 (附源码分析)

一、问题背景: 最近某springboot项目想嵌入一个用户聊天功能,打算使用 Rabbitmq + Netty4.x + Redis 来开发高性能聊天功能。花费三天时间所有功能都已实现。启动时却警告 :Unknown channel option 'xxxx' for channel '[id: 0xba18fac1]', 虽然是个告警但是必须解决,否则会影响正在准备建立连接的握手以及激活心跳机制->TCP自动发送一个活动探测数据报文 netty 使用的版本是 4.1.6 二、源码分析 1

Specif1c的博客 6541

最新版RabbitMQ教程完结,高薪必学必会~跟着新哥一块儿自学

袁庭新的最新版【RabbitMQ教程-V2.0】已编写完成啦~新版是基于Spring Boot 3.x + JDK21创作,并扩展了很多新内容。

2301_82300081的博客 903

老公不睡觉都要看系列:华为18级工程师自爆千页SpringBoot学习笔记

前言 SpringBoot是现在每个企业必不可少的,在Java面试中也是躲不过去的。对于大部分普通家庭的孩子,IT业是一个单靠个人或者团队能力就能实现“最快实现人生小目标”的行业,所以每门技术都是不能落下和缺少的,要跟上时代的进步和发展,充实自己的技术栈,完善自己储备的知识体系,都是必不可少的。 无论哪个行业都是需要强大的理论知识储备+实践,工欲善其事,必先利其器,都会结合一些开发工具的使用和非常使用的技能,来帮助我们最大化的实现高效开发价值。 市面上有很多关于SpringBoot的学习资料,都比

如果频繁就私信一下呢 474

阿里高工内产的 SpringBoot 保姆级笔记,面面俱到,太全了

对于大部分普通家庭的孩子来说, IT行业是一个单靠个人或者团队能力就能“最快实现人生小目标”的行业。所以, 如果这本文可以帮助读者学会精通 Spring Boot ,并且能够进入一线大厂不断学习丰富自己的人生,那本文的价值就很大了。本文基于Spring Boot 2.X版本,采用“知识点+实例”的形式编写,通过“58个基于知识的实例+2个综合性的项目”,全面深入地讲解了Spring Boot的主要技术原理和应用。不仅如此,本文还对比讲解了多种同类技术的使用和区别,读者可以根据自己的喜好进行选择。

m0_67645544的博客 224

Spring-rabbit重试消费源码分析

本文深入解析了RabbitMQ与Spring Boot 3.1.x集成时RetryOperationsInterceptor的工作原理。该组件通过拦截消息消费异常实现重试机制,支持配置最大尝试次数(如3次)、退避策略(初始间隔1秒,倍增因子2.0)和最终恢复处理(转发至死信队列)。源码层面揭示了其与RetryTemplate的协作流程:当消费失败时自动重试,若达到上限则调用RepublishMessageRecoverer将消息路由至死信队列。文章还展示了如何通过@RabbitListener与自定义容器工

知秋的博客 926

SpringBoot: 整合RabbitMQ发送对象类型的消息时的源码分析

boot

amadeus_liu2的博客 605
上一篇: springboot2.x +rabbitmq使用和源码分析一(自动装配)
下一篇: springboot2.x +rabbitmq使用和源码分析三(消息转换器)
F_Hello_World
博客等级 码龄11年 38粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值