RabbitMQ 篇-深入了解延迟消息、MQ 可靠性(生产者可靠性、MQ 可靠性、消费者可靠性)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 RabbitMQ 的可靠性

        2.0 发送者的可靠性

        2.1 生产者重试机制

        2.2 生产者确认机制

        2.2.1 开启生产者确认机制

        2.2.2 定义 ReturnCallback 机制

        2.2.3 定义 ConfirmCallback 机制

        3.0 MQ 可靠性

        3.1 交换机持久化

        3.2 队列持久化

        3.3 消息持久化

        3.4 LazyQueue 懒惰队列

        3.4.1 声明懒惰队列的方式

        4.0 消费者的可靠性

        4.1 消费者确认机制

        4.2 失败重试机制

        4.3 失败处理策略

        5.0 使用 DelayExchange 插件

        5.1 安装 DelayExchange 插件

        5.2 声明延迟交换机

        5.3 发送延迟消息


        1.0 RabbitMQ 的可靠性

        消息从生产者到消费者的每一步都可能导致消息丢失:

        1)发送消息时丢失:

                - 生产者发送消息时连接 MQ 失败。

                - 生产者发送消息到达 MQ 后未找到 Exchange 。

                - 生产者发送消息到达 MQ 的 Exchange 后,未找到合适的 Queue 。

                - 消息到达 MQ 后,处理消息的进程发生异常。

        2)MQ 导致消息丢失:

                - 消息到达 MQ,保存到队列后,尚未消费就突然宕机了。

        3)消费者处理消息时:

                - 消息接收后尚未处理突然宕机。

                - 消息接收后处理过程中抛出异常。

        综上,要解决消息丢失问题,保证 MQ 可靠性,就必须从三个方面入手:

                - 确保生产者一定把消息发送到 MQ 。

                - 确保 MQ 不会将消息弄丢。

                - 确保消费者一定要处理消息。

        2.0 发送者的可靠性

        通过生产者重试机制、确认机制来确保发送者的可靠性。

        2.1 生产者重试机制

        首先第一种情况,就是生产者发送消息时,出现了网络故障,导致与MQ的连接中断。为了解决这个问题,SpringAMQP 提供的消息发送时的重试机制。即:当 RabbitTemplate 与 MQ 连接超时后,多次重试。

        修改 application.yml 文件,添加下面的内容:

spring:rabbitmq:connection-timeout: 1s # 设置MQ的连接超时时间template:retry:enabled: true # 开启超时重试机制initial-interval: 1000ms # 失败后的初始等待时间multiplier: 1 # 失败后下次的等待时长倍数,下次等待时长 = initial-interval * multipliermax-attempts: 3 # 最大重试次数

        利用命令停掉 RabbitMQ 服务:

docker stop mq

        然后测试发送一条消息,会发现会每隔 1 秒重试 1 次,总共重试了 3 次。消息发送的超时重试机制配置成功了。

        注意,当网络不稳定的时候,利用重试机制可以有效提高消息发送的成功率。不过 SpringAMQP 提供的重试机制时阻塞式的重试,也就是说多次重试等待的过程中,当前线程式被阻塞的。

        如果对业务性能有要求,建议禁用重试机制,如果一定要使用,请合理配置等待时长和重试次数,当然也可以考虑使用异步线程来执行发送消息的代码。

        2.2 生产者确认机制

        一般情况下,只要生产者与 MQ 之间的网路连接顺畅,基本不会出现发送消息丢失的情况,因此大多数情况下我们无需考虑这种问题。

        不过,在少数情况下,也会出现消息发送到 MQ 之后丢失的现象,比如:

                - MQ 内部处理消息的进程发生了异常。

                - 生产者发送消息到达 MQ 后未找到 Exchange 。

                - 生产者发送消息到达 MQ 的 Exchange 后,未找到合适的 Queue,因此无法路由。

        当消息投递到 MQ,但是路由失败时,通过 Return 返回异常信息,同时返回 ack 确认信息,代表投递成功。

        1)临时消息投递到了 MQ,并且入队成功,返回 ACK,告知投递成功。

        2)持久消息投递到了 MQ,并且入队完成持久化,返回 ACK,告知投递成功。

        3)其他情况都会返回 NACK,告知投递失败。

        其中 ACK 和 NACK 属于 Publisher Confirm 机制,ACK 是投递成功;NACK 是投递失败。而 return 则属于Publisher Return机制。

        2.2.1 开启生产者确认机制

        默认两种机制都是关闭状态,需要通过配置文件来开启。

        在 application.yml 中添加配置:

spring:rabbitmq:publisher-confirm-type: correlated # 开启publisher confirm机制,并设置confirm类型publisher-returns: true # 开启publisher return机制

        这里 publisher-confirm-type 有三种模式可选:

        1)none:关闭 confirm 机制

        2)simple:同步阻塞等待 MQ 的回执

        3)correlated:MQ 异步回调返回回执

        一般我们推荐使用 correlated,回调机制。

        2.2.2 定义 ReturnCallback 机制

        每个 RabbitTemplate 只能配置一个 ReturnCallback,因此可以在配置类中统一设置。

代码如下:

package com.itheima.publisher.config;import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;@Slf4j
@AllArgsConstructor
@Configuration
public class MqConfig {private final RabbitTemplate rabbitTemplate;@PostConstructpublic void init(){rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {@Overridepublic void returnedMessage(ReturnedMessage returned) {log.error("触发return callback,");log.debug("exchange: {}", returned.getExchange());log.debug("routingKey: {}", returned.getRoutingKey());log.debug("message: {}", returned.getMessage());log.debug("replyCode: {}", returned.getReplyCode());log.debug("replyText: {}", returned.getReplyText());}});}
}

测试:

        当发送消息的时候,设置一个不存在的路由:

        在交换机中,不存在 "xbss" 路由关键字,则会执行 returnedMessage 方法。

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息textObject();}@Testpublic void textObject(){User user = new User(1,"xbs","123456",null,null);rabbitTemplate.convertAndSend("textExchange", "xbss", user, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setMessageId("123456");return message;}});}

执行结果:

        2.2.3 定义 ConfirmCallback 机制

        由于每个消息发送时的处理逻辑不一定相同,因此 ConfirmCallback 需要在每次发送消息时定义。具体来说,是在调用 RabbitTemplate 中的 convertAndSend 方法时,多传递一个参数:

代码如下:

    @Testvoid testPublisherConfirm() {// 1.创建CorrelationDataCorrelationData cd = new CorrelationData();// 2.给Future添加ConfirmCallbackcd.getFuture().thenAccept(result -> {if (result.isAck()) { // result.isAck(),boolean类型,true代表ack回执,false 代表 nack回执log.info("发送消息成功,收到 ack!");} else { // result.getReason(),String类型,返回nack时的异常描述log.info("发送消息失败,收到 nack, reason : {}", result.getReason());}}).exceptionally(ex -> {// 2.1.Future发生异常时的处理逻辑,基本不会触发log.error("send message fail", ex);return null;});// 3.发送消息rabbitTemplate.convertAndSend("xbs.direct", "red", "hello", cd);}

执行结果:

        执行成功之后,日志输出:

        接收消息:

注意

开启生产者确认比较消耗 MQ 性能,一般不建议开启。而且大家思考一下触发确认的几种情况:

  • 路由失败:一般是因为 RoutingKey 错误导致,往往是编程导致

  • 交换机名称错误:同样是编程错误导致

  • MQ 内部故障:这种需要处理,但概率往往较低。因此只有对消息可靠性要求非常高的业务才需要开启,而且仅仅需要开启 ConfirmCallback 处理 nack 就可以了。

        3.0 MQ 可靠性

        消息到达MQ以后,如果 MQ 不能及时保存,也会导致消息丢失,所以 MQ 的可靠性也非常重要。

为了提升性能,默认情况下 MQ 的数据都是在内存存储的临时数据,重启后就会消失。为了保证数据的可靠性,必须配置数据持久化,包括:

        1)交换机持久化

        2)队列持久化

        3)消息持久化

        3.1 交换机持久化

        在控制台的 Exchange 页面,添加交换机时可以配置交换机的 Durability 参数:

        设置为 Durable 就是持久化模式,Transient 就是临时模式。

        3.2 队列持久化

        在控制台的 Queues 页面,添加队列时,同样可以配置队列的 Durability 参数:

        设置为 Durable 就是持久化模式,Transient 就是临时模式。

        3.3 消息持久化

        在控制台发送消息的时候,可以添加很多参数,而消息的持久化是要配置一个Delivery mode:设置为 Persistent 消息持久化。

说明

        在开启持久化机制以后,如果同时还开启了生产者确认,那么 MQ 会在消息持久化以后才发送 ACK 回执,进一步确保消息的可靠性。

        不过出于性能考虑,为了减少 IO 次数,发送到 MQ 的消息并不是逐条持久化到数据库的,而是每隔一段时间批量持久化。一般间隔在 100 毫秒左右,这就会导致 ACK 有一定的延迟,因此建议生产者确认全部采用异步方式。

        3.4 LazyQueue 懒惰队列

        1)在默认情况下,RabbitMQ 会将接收到的信息保存在内存中以降低消息收发的延迟。但在某些特殊情况下,这会导致消息积压,比如:

  • 消费者宕机或出现网络故障

  • 消息发送量激增,超过了消费者处理速度

  • 消费者处理业务发生阻塞

        2)一旦出现消息堆积问题,RabbitMQ 的内存占用就会越来越高,直到触发内存预警上限。此时 RabbitMQ 会将内存消息刷到磁盘上,这个行为成为 PageOut 。PageOut 会耗费一段时间,并且会阻塞队列进程。因此在这个过程中 RabbitMQ 不会再处理新的消息,生产者的所有请求都会被阻塞。

        为了解决这个问题,从 RabbitMQ 的 3.6.0 版本开始,就增加了 Lazy Queues 的模式,也就是惰性队列。惰性队列的特征如下:

  • 接收到消息后直接存入磁盘而非内存

  • 消费者要消费消息时才会从磁盘中读取并加载到内存(也就是懒加载)

  • 支持数百万条的消息存储

        而在 3.12 版本之后,LazyQueue 已经成为所有队列的默认格式。因此官方推荐升级 MQ 为 3.12 版本或者所有队列都设置为 LazyQueue 模式。

        3.4.1 声明懒惰队列的方式

        1)在添加队列的时候,添加 x-queue-mode 参数即可设置队列为 Lazy 模式:

        2)在利用 SpringAMQP 声明队列的时候,添加 x-queue-mode 参数也可设置队列为 Lazy 模式:

    @Beanpublic Queue queue3(){return QueueBuilder.durable("queue7") //队列名称.lazy() //开启懒惰模式.build();}

        3)基于注解来声明队列并设置为 Lazy 模式:

@RabbitListener(queuesToDeclare = @Queue(name = "lazy.queue",durable = "true",arguments = @Argument(name = "x-queue-mode", value = "lazy")
))
public void listenLazyQueue(String msg){log.info("接收到 lazy.queue的消息:{}", msg);
}

        4.0 消费者的可靠性

        当 RabbitMQ 向消费者投递消息以后,需要知道消费者的处理状态如何。因为消息投递给消费者并不代表就一定被正确消费了,可能出现的故障有很多。

        一旦发生上述情况,消息也会丢失。因此,RabbitMQ 必须知道消费者的处理状态,一旦消息处理失败才能重新投递消息。

        4.1 消费者确认机制

        1)为了确认消费者是否成功处理消息,RabbitMQ 提供了消费者确认机制(Consumer Acknowledgement)。即:当消费者处理消息结束后,应该向 RabbitMQ 发送一个回执,告知 RabbitMQ 自己消息处理状态。回执有三种可选值:

  • ack:成功处理消息,RabbitMQ 从队列中删除该消息。

  • nack:消息处理失败,RabbitMQ 需要再次投递消息。

  • reject:消息处理失败并拒绝该消息,RabbitMQ 从队列中删除该消息。

        2)一般 reject 方式用的较少,除非是消息格式有问题,那就是开发问题了。因此大多数情况下我们需要将消息处理的代码通过 try catch 机制捕获,消息处理成功时返回 ack,处理失败时返回 nack 。

        由于消息回执的处理代码比较统一,因此 SpringAMQP 帮我们实现了消息确认。并允许我们通过配置文件设置 ACK 处理方式,有三种模式:

  • none:不处理。即消息投递给消费者后立刻 ack,消息会立刻从 MQ 删除。非常不安全,不建议使用

  • manual:手动模式。需要自己在业务代码中调用 api,发送 ack 或 reject,存在业务入侵,但更灵活

  • auto:自动模式。SpringAMQP 利用 AOP 对我们的消息处理逻辑做了环绕增强,当业务正常执行时则自动返回 ack. 当业务出现异常时,根据异常判断返回不同结果:

    • 如果是业务异常,会自动返回 nack;

    • 如果是消息处理或校验异常,自动返回 reject;

        因此,推荐使用 aotu 自动模式。

application 配置如下:

spring:rabbitmq:listener:simple:acknowledge-mode: auto # 自动ack

        在开启消费者确认机制时,手动抛出异常模拟接收信息失败时:

        1)发送消息:

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息testSendMessage();}@Testpublic void testSendMessage(){//发送消息rabbitTemplate.convertAndSend("yt","xbs","hello rabbitmq");}

        2)接收消息:

    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "wyt",durable = "true",arguments = @Argument(name = "x-queue-mode", value = "lazy")),exchange = @Exchange(name = "yt",type = ExchangeTypes.DIRECT),key = "xbs"))public void receiveMessage(String massage) throws InterruptedException {log.info("sendMessage发送的消息为: " + massage);//模拟接送消息失败throw new RuntimeException("模拟接收消息失败");}

执行结果:

        由于抛出了异常,则返回给 MQ 为 nack,就会重复发送消息给客户端:

        在队列中,由于消息没有被正确处理,则消息会一直在队列中且不断发送消息给客户端:

        4.2 失败重试机制

        当消费者出现异常后,消息会不断 requeue(重入队)到队列,再重新发送给消费者。如果消费者再次执行依然出错,消息会再次 requeue 到队列,再次投递,直到消息处理成功为止。

        极端情况就是消费者一直无法执行成功,那么消息 requeue 就会无限循环,导致mq的消息处理飙升,带来不必要的压力:

        应对上述情况 Spring 又提供了消费者失败重试机制:在消费者出现异常时利用本地重试,而不是无限制的 requeue 到 mq 队列。

修改 application.yml 文件,添加内容:

spring:rabbitmq:listener:simple:retry:enabled: true # 开启消费者失败重试initial-interval: 1000ms # 初识的失败等待时长为1秒multiplier: 1 # 失败的等待时长倍数,下次等待时长 = multiplier * last-intervalmax-attempts: 3 # 最大重试次数stateless: true # true无状态;false有状态。如果业务中包含事务,这里改为false

        再来尝试手动抛出异常模拟接收消息失败:

结果如下:

        MQ 向接收者发送了三次信息,接着进程结束。

        此时队列中的信息不存在了,丢失了:

结论:

  • 开启本地重试时,消息处理过程中抛出异常,不会 requeue 到队列,而是在消费者本地重试

  • 重试达到最大次数后,Spring 会返回 reject,消息会被丢弃。

        4.3 失败处理策略

        在之前的测试中,本地测试达到最大重试次数后,消息会被丢弃。这在某些对于消息可靠性要求较高的业务场景下,显然不太合适了。

        因此 Spring 允许我们自定义重试次数耗尽后的消息处理策略,这个策略是由 MessageRecovery 接口来定义的,它有3个不同实现:

  • RejectAndDontRequeueRecoverer:重试耗尽后,直接 reject,丢弃消息。默认就是这种方式

  • ImmediateRequeueMessageRecoverer:重试耗尽后,返回 nack,消息重新入队

  • RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机

        比较优雅的一种处理方案是 RepublishMessageRecoverer,失败后将消息投递到一个指定的,专门存放异常消息的队列,后续由人工集中处理。

代码实现:

        1)定义处理失败消息的交换机和队列:

@Bean
public DirectExchange errorMessageExchange(){return new DirectExchange("error.direct");
}
@Bean
public Queue errorQueue(){return new Queue("error.queue", true);
}
@Bean
public Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with("error");
}

        2)定义一个 RepublishMessageRecoverer,关联队列和交换机

@Bean
public MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){return new RepublishMessageRecoverer(rabbitTemplate, "error.direct", "error");
}

完整代码:

package com.itheima.consumer.config;import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.context.annotation.Bean;@Configuration
@ConditionalOnProperty(name = "spring.rabbitmq.listener.simple.retry.enabled", havingValue = "true")
public class ErrorMessageConfig {@Beanpublic DirectExchange errorMessageExchange(){return new DirectExchange("error.direct");}@Beanpublic Queue errorQueue(){return new Queue("error.queue", true);}@Beanpublic Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with("error");}@Beanpublic MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){return new RepublishMessageRecoverer(rabbitTemplate, "error.direct", "error");}
}

        当重试失败之后,不会直接将消息丢失而是交给专门接收失败信息的交换机,再由专门的消费者进行消费,比如说监听到队列中有接收失败的信息,将其写入日志中等等处理的方法。

测试:

        1)发送消息:

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息testSendMessage();}@Testpublic void testSendMessage(){//发送消息rabbitTemplate.convertAndSend("yt","xbs","hello rabbitmq");}

        2)接收消息:

        接收消息之后,手动抛出异常模拟接收消息失败。

    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "wyt",durable = "true",arguments = @Argument(name = "x-queue-mode", value = "lazy")),exchange = @Exchange(name = "yt",type = ExchangeTypes.DIRECT),key = "xbs"))public void receiveMessage(String massage) throws InterruptedException {log.info("sendMessage发送的消息为: " + massage);//模拟接送消息失败throw new RuntimeException("模拟接收消息失败");}

执行结果:

        在本地重试三次发送失败之后,则将该消息交给 "errorMessageExchange" 交换机,再路由给 "errorMessageQueue" 队列,后续可以监听该队列进行接收消息再做处理。

        5.0 使用 DelayExchange 插件

        RabbitMQ 官方推出了一个插件,原生支持延迟消息功能。该插件的原理是设计了一种支持延迟消息功能的交换机,当消息投递到交换机后可以暂存一定时间,到期后再投递到队列中。

        5.1 安装 DelayExchange 插件

        基于 Docker 安装,所以需要先查看 RabbitMQ 的插件目录对应的数据卷。

docker volume inspect mq-plugins

结果如下:

[{"CreatedAt": "2024-06-19T09:22:59+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/mq-plugins/_data","Name": "mq-plugins","Options": null,"Scope": "local"}
]

        插件目录被挂载到了 docker volume inspect mq-plugins 这个目录,我们上传插件到该目录下。

        DelayExchange 插件下载地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange

        接着文件放到 docker volume inspect mq-plugins 这个目录中。

接下来执行命令,安装插件:

docker exec -it mq rabbitmq-plugins enable rabbitmq_delayed_message_exchange

安装结果: 

        5.2 声明延迟交换机

        1)基于注解方式:

@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "delay.queue", durable = "true"),exchange = @Exchange(name = "delay.direct", delayed = "true"),key = "delay"
))
public void listenDelayMessage(String msg){log.info("接收到delay.queue的延迟消息:{}", msg);
}

        2)基于 @Bean 的方式:

package com.itheima.consumer.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Slf4j
@Configuration
public class DelayExchangeConfig {@Beanpublic DirectExchange delayExchange(){return ExchangeBuilder.directExchange("delay.direct") // 指定交换机类型和名称.delayed() // 设置delay的属性为true.durable(true) // 持久化.build();}@Beanpublic Queue delayedQueue(){return new Queue("delay.queue");}@Beanpublic Binding delayQueueBinding(){return BindingBuilder.bind(delayedQueue()).to(delayExchange()).with("delay");}
}

        

        5.3 发送延迟消息

        发送消息时,必须通过 x-delay 属性设定延迟时间:

@Test
void testPublisherDelayMessage() {// 1.创建消息String message = "hello, delayed message";// 2.发送消息,利用消息后置处理器添加消息头rabbitTemplate.convertAndSend("delay.direct", "delay", message, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {// 添加延迟消息属性message.getMessageProperties().setDelay(5000);return message;}});
}

测试:

        1)接收消息:

    @AutowiredMessageConverter messageConverter;@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "delay.queue",durable = "true",arguments = @Argument(name = "x-queue-mode", value = "lazy")),exchange = @Exchange(name = "delay.exchange",type = ExchangeTypes.DIRECT,delayed = "true"),key = "xbs"))public void receiveMessage(Message massage) throws InterruptedException {log.info("sendMessage发送的消息ID为: " + massage.getMessageProperties().getMessageId());log.info("sendMessage发送的消息延迟时间为: " + massage.getMessageProperties().getDelayLong());String str = (String) messageConverter.fromMessage(massage);log.info("sendMessage发送的消息内容为: " + str);}

        2)发送消息:

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息testSendDelayMessage();}@Testvoid testSendDelayMessage(){//发送消息rabbitTemplate.convertAndSend("delay.exchange", "xbs", "hello rabbitmq delay message", new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {//先设置唯一IDmessage.getMessageProperties().setMessageId("123456");//设置延迟发送时间message.getMessageProperties().setDelayLong(5000L);return message;}});}

执行结果:

注意:

        延迟消息插件内部会维护一个本地数据库表,同时使用 Elang Timers 功能实现计时。如果消息的延迟时间设置较长,可能会导致堆积的延迟消息非常多,会带来较大的 CPU 开销,同时延迟消息的时间会存在误差。

        因此,不建议设置延迟时间过长的延迟消息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/471696.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】AVL树

引言:在实际情况中,数据不仅仅要存储起来,还要进行对数据进行搜索,为了方便进行高效搜索(在此之前的数据结构的搜索基本都是暴力搜索)二叉搜索树应运而生。但是在极端情况下(我们按照有序的方式进行插入),二叉搜索树就…

CSS的综合应用例子(网页制作)

这是html的一些最基本内容的代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <t…

MySQL查询某个数据库中特定表的空间占用大小

如果您也想要查询某个数据库中特定表的空间占用大小&#xff0c;包括数据和索引的大小&#xff0c;那么您可以使用以下SQL查询。这个查询将显示特定表在数据库中的数据大小、索引大小以及总大小。 SELECT table_name AS Table,ROUND(((data_length index_length) / 1024 / 10…

Towards Reasoning in Large Language Models: A Survey

文章目录 题目摘要引言什么是推理?走向大型语言模型中的推理测量大型语言模型中的推理发现与启示反思、讨论和未来方向 为什么要推理?结论题目 大型语言模型中的推理:一项调查 论文地址:https://arxiv.org/abs/2212.10403 项目地址: https://github.com/jeffhj/LM-reason…

进入未来城:第五周游戏指南

欢迎来到 Alpha 第 4 季第五周&#xff01; 走进霓虹闪烁的未来城街道&#xff0c;这是一座科技至上的赛博朋克大都市。鳞次栉比的摩天大楼熠熠生辉&#xff0c;拥挤的街道下则是阴森恐怖的地下世界。在这里&#xff0c;像激光鹰队长这样的超级战士正在巡逻&#xff0c;而 Ago…

斯坦福泡茶机器人DexCap源码解析:涵盖收集数据、处理数据、模型训练三大阶段

前言 因为我司「七月在线」关于dexcap的复现/优化接近尾声了(每月逐步提高复现的效果)&#xff0c;故准备把dexcap的源码也分析下&#xff0c;11月​下旬则分析下iDP3的源码——为队伍「iDP3人形的复现/优化」助力 最开始&#xff0c;dexcap的源码分析属于此文《DexCap——斯…

Python中的HTML

文章目录 一. HTML1. html的定义2. html的作用3. 基本结构4. 常用的html标签5. 列表标签① 无序列表② 有序列表 6. 表格标签7. 表单标签8. 表单提交① 表单属性设置② 表单元素属性设置 一. HTML 1. html的定义 HTML 的全称为&#xff1a;HyperText Mark-up Language, 指的是…

PdServer:调用MidjourneyAPI完成静夜思图文生成

欢迎沟通讨论&#xff0c;WX: cdszsz。公号&#xff1a;AIGC中文站。 今天我们将使用PdServer&#xff0c;通过Qwen大模型完成古诗的解析与prompt的生成&#xff0c;然后调用MidjourneyAPI完成图片的生成。有了文案和图片&#xff0c;我们就可以将其生成为一个古诗讲读视频。从…

论文 | The Capacity for Moral Self-Correction in LargeLanguage Models

概述 论文探讨了大规模语言模型是否具备“道德自我校正”的能力&#xff0c;即在收到相应指令时避免产生有害或偏见输出的能力。研究发现&#xff0c;当模型参数达到一定规模&#xff08;至少22B参数&#xff09;并经过人类反馈强化学习&#xff08;RLHF&#xff09;训练后&…

认证鉴权框架SpringSecurity-1--概念和原理篇

1、基本概念 Spring Security 是一个强大且高度可定制的框架&#xff0c;用于构建安全的 Java 应用程序。它是 Spring 生态系统的一部分&#xff0c;提供了全面的安全解决方案&#xff0c;包括认证、授权、CSRF防护、会话管理等功能。 2、认证、授权和鉴权 &#xff08;1&am…

删库跑路,启动!

起因&#xff1a;这是一个悲伤的故事&#xff0c;在抓logcat时 device待机自动回根目录了&#xff0c;而题主对当前路径的印象还停留在文件夹下&#xff0c;不小心在根目录执行了rm -rf * … 所以&#xff0c;这是个悲伤的故事&#xff0c;东西全没了…device也黑屏了&#xff…

unity单例模式的不同声明(待完善

总结&#xff1a; 这段代码实现了一个泛型单例模式&#xff08;Singleton Pattern&#xff09;&#xff0c;用于确保某个类&#xff08;由泛型参数 T 指定&#xff09;在整个应用程序中只有一个实例&#xff0c;并且在第一次访问时才创建该实例。该模式保证了该实例的全局唯一…

低代码牵手 AI 接口:开启智能化开发新征程

一、低代码与 AI 接口的结合趋势 低代码开发平台近年来在软件开发领域迅速崛起。随着企业数字化转型的需求不断增长&#xff0c;低代码开发平台以其快速构建应用程序的优势&#xff0c;满足了企业对高效开发的需求。例如&#xff0c;启效云低代码平台通过范式化和高颗粒度的可配…

3. Sharding-Jdbc核⼼流 程+多种分⽚策略

1. Sharding-Jdbc 分库分表执⾏核⼼流程 Sharding-JDBC执行流程 1. SQL解析 -> SQL优化 -> SQL路由 -> SQL改写 -> SQL执⾏-> 结果归并 ->返回结果简写为&#xff1a;解析->路由->改写->执⾏->结果归并1.1 SQL解析 1. SQL解析过程分为词法解析…

解读Nature:Larger and more instructable language models become less reliable

目录 Larger and more instructable language models become less reliable 核心描述 核心原理 创新点 举例说明 大模型训练,微调建议 Larger and more instructable language models become less reliable 这篇论文的核心在于对大型语言模型(LLMs)的可靠性进行了深入…

A3超级计算机虚拟机,为大型语言模型LLM和AIGC提供强大算力支持

热门大语言模型项目地址&#xff1a;www.suanjiayun.com/mirrorDetails?id66ac7d478099315577961758 近几个月来&#xff0c;我们目睹了大型语言模型&#xff08;LLMs&#xff09;和生成式人工智能强势闯入我们的视野&#xff0c;显然&#xff0c;这些模型在训练和运行时需要…

跟着尚硅谷学vue2—基础篇4.0

11. 收集表单数据 收集表单数据&#xff1a; 若&#xff1a;<input type"text"/>&#xff0c;则v-model收集的是value值&#xff0c;用户输入的就是value值。 若&#xff1a;<input type"radio"/>&#xff0c;则v-model收集的是value值&…

「人眼视觉不再是视频消费的唯一形式」丨智能编解码和 AI 视频生成专场回顾@RTE2024

你是否想过&#xff0c;未来你看到的电影预告片、广告&#xff0c;甚至新闻报道&#xff0c;都可能完全由 AI 生成&#xff1f; 在人工智能迅猛发展的今天&#xff0c;视频技术正经历着一场前所未有的变革。从智能编解码到虚拟数字人&#xff0c;再到 AI 驱动的视频生成&#…

【LeetCode】每日一题 2024_11_14 统计好节点的数目(图/树的 DFS)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;统计好节点的数目 代码与解题思路 先读题&#xff1a;题目要求我们找出好节点的数量&#xff0c;什么是好节点&#xff1f;“好节点的所有子节点的数量都是相同的”&#xff0c;拿示例一…

js中typeOf无法区分数组对象

[TOC]&#xff08;js中typeOf无法区分数组对象) 前提&#xff1a;很多时候我们在JS中用typeOf来判断值类型&#xff0c;如&#xff1a;typeOf ‘abc’//string ,typeOf 123 //number; 但当判断对象为数组时返回的仍是’object’ 这时候我们可以使用Object.prototype.toString.c…