当前位置: 首页 > news >正文

RaabitMQ(三) - RabbitMQ队列类型、死信消息与死信队列、懒队列、集群模式、MQ常见消息问题

RabbitMQ队列类型

Classic经典队列

  • 这是RabbitMQ最为经典的队列类型。在单机环境中,拥有比较高的消息可靠性。

  • 经典队列可以选择是否持久化(Durability)以及是否自动删除(Auto delete)两个属性。

  • Durability有两个选项,Durable和Transient。 Durable表示队列会将消息保存到硬盘,这样消息的安全性更高。但是同时,由于需要有更多的IO操作,所以生产和消费消息的性能,相比Transient会比较低。

  • Auto delete属性如果选择为是,那队列将在至少一个消费者已经连接,然后所有的消费者都断开连接后删除自己。

  • 经典队列不适合积累太多的消息。如果队列中积累的消息太多了,会严重影响客户端生产消息以及消费消息的性能。因此,经典队列主要用在数据量比较小,并且生产消息和消费消息的速度比较稳定的业务场景。比如内部系统之间的服务调用

Quorum仲裁队列

  • 仲裁队列,是3.8引入的一个新队列类型;仲裁队列相比Classic经典队列,在分布式环境下对消息的可靠性保障更高。

  • Quorum是基于Raft一致性协议实现的一种新型的分布式消息队列,他实现了持久化,多备份的FIFO队列,主要就是针对RabbitMQ的镜像模式设计的。简单理解就是quorum队列中的消息需要有集群中多半节点同意确认后,才会写入到队列中。

  • Classic与Quorum对比、少了一些高级特性:
    在这里插入图片描述

  • Quorum队列更适合于 队列长期存在,并且对容错、数据安全方面的要求比低延迟、不持久等高级队列更能要求更严格的场景。例如 电商系统的订单,引入MQ后,处理速度可以慢一点,但是订单不能丢失。

  • Quorum不适合的场景如下:

    • 队列的临时性:暂时性或独占队列、高队列变动率(声明和删除率)
    • 尽可能低的延迟:由于其数据安全功能,底层共识算法固有的延迟更高
    • 当数据安全不是优先事项时(例如,应用程序不使用手动确认,不使用发布者确认)
    • 很长的队列积压(流可能更适合)

创建Quorum队列

Spring创建仲裁队列需要设置参数“-x-queue-type”为“quorum”

@Configuration
public class QuorumConfig {public final static String QUEUE_TYPE = "x-queue-type";public final static String QUEUE_TYPE_VAL = "quorum";public final static String QUEUE_NAME = "quorumQueue";@Beanpublic Queue quorumQueue() {HashMap<String, Object> params = new HashMap<>();params.put(QUEUE_TYPE,QUEUE_TYPE_VAL);Queue queue = new Queue(QUEUE_NAME, true, false, false, params);return queue;}
}

在这里插入图片描述
Rabbit Client创建Quorum队列:

Map<String,Object> params = new HashMap<>();
params.put("x-queue-type","quorum");
//声明Quorum队列的方式就是添加一个x-queue-type参数,指定为quorum。默认是classic
channel.queueDeclare(QUEUE_NAME, true, false, false, params);

​ Quorum队列的消息是必须持久化的,所以durable参数必须设定为true,如果声明为false,就会报错。同样,exclusive参数必须设置为false。这些声明,在Producer和Consumer中是要保持一致的。

Stream队列

  • Stream队列是3.9.0版本引入新队列类型。
  • 持久化到磁盘并且具备分布式备份的,更适合于消费者多,读消息非常频繁的场景。
  • Stream队列的核心是以append-only只添加的日志来记录消息,整体来说,就是消息将以append-only的方式持久化到日志文件中,然后通过调整每个消费者的消费进度offset,来实现消息的多次分发。类似kafka;

创建Stream队列

Spring AMQP目前还不支持创建Stream队列;只能使用原生API创建

  Map<String,Object> params = new HashMap<>();params.put("x-queue-type","stream");params.put("x-max-length-bytes", 20_000_000_000L); // maximum stream size: 20 GBparams.put("x-stream-max-segment-size-bytes", 100_000_000); // size of segment files: 100 MBchannel.queueDeclare(QUEUE_NAME, true, false, false, params);

Stream队列的durable参数必须声明为true,exclusive参数必须声明为false。
x-max-length-bytes 表示日志文件的最大字节数, x-stream-max-segment-size-bytes 每一个日志文件的最大大小。这两个是可选参数,通常为了防止stream日志无限制累计,都会配合stream队列一起声明。

消费者:

 	Map<String,Object> consumeParam = new HashMap<>();consumeParam.put("x-stream-offset","last");channel.basicConsume(QUEUE_NAME, false,consumeParam, myconsumer);

x-stream-offset的类型:

  • first: 从日志队列中第一个可消费的消息开始消费
  • last: 消费消息日志中最后一个消息
  • next: 相当于不指定offset,消费不到消息。
  • Offset: 一个数字型的偏移量
  • Timestamp:一个代表时间的Data类型变量,表示从这个时间点开始消费。例如 一个小时前Date timestamp = new Date(System.currentTimeMillis() - 60 * 60 * 1_000)

Stream队列产品目前不够成熟,目前用的最多的还是Classic经典队列。RabbitMQ目前主推的是Quorum队列;

死信消息

有以下三种情况,RabbitMQ会将一个正常消息转成死信

  • 消息被消费者确认拒绝。消费者把requeue参数设置为true(false),并且在消费后,向 RabbitMQ返回拒绝。channel.basicReject或者channel.basicNack。

  • 消息达到预设的TTL时限还一直没有被消费。

  • 消息由于队列已经达到最长长度限制而被丢掉

    • TTL即最长存活时间 Time-To-Live 。消息在队列中保存时间超过这个TTL,即会被认为死亡。死亡的消息会被丢入死信队列,如果没有配置死信队列的话,RabbitMQ会保证死了的消息不会再次被投递,并且在未来版本中,会主动删除掉这些死掉的消息。

    • 声明队列时、设置"x-message-ttl"值;

		Map<String, Object> args = new HashMap<String, Object>();args.put("x-message-ttl", 60000);channel.queueDeclare("myqueue", false, false, false, args);

如何判断消息是否为死信

消息被作为死信转移到死信队列后,header中还会加上第一次成为死信的三个属性,并且这三个属性在以后的传递过程中都不会更改。具体可以调试去看看;

  1. x-first-death-reason :原因
  2. x-first-death-queue : 队列
  3. x-first-death-exchange : 交换机

死信队列

  • 存在死信消息的队列;
  • RabbitMQ中有两种方式可以声明死信队列,一种是针对某个单独队列指定对应的死信队列。另一种就是以策略的方式进行批量死信队列的配置。
    流程图如下:
    在这里插入图片描述

代码:
死信交换机、队列:

@Configuration
public class DeadConfig {public final static String DEAD_EXCHANGE = "deadExchange";public final static String DEAD_QUEUE_NAME = "deadQueue";@Beanpublic FanoutExchange deadExchange() {FanoutExchange directExchange = new FanoutExchange(DEAD_EXCHANGE);return directExchange;}@Beanpublic Queue deadQueue() {Queue queue = new Queue(DEAD_QUEUE_NAME);return queue;}@Beanpublic Binding deadBinding(FanoutExchange deadExchange, Queue deadQueue) {return BindingBuilder.bind(deadQueue).to(deadExchange);}}

发送者:

@Controller
public class MessageTx {@Autowiredprivate MessageService messageService;@GetMapping("/sendDeadMsg")@ResponseBodypublic String sendMoreMsgTx(){//发送10条消息for (int i = 0; i < 10; i++) {String msg = "msg"+i;System.out.println("发送消息  msg:"+msg);// xiangjiao.exchange  交换机// xiangjiao.routingKey  队列messageService.sendMessage(MessageConfig.EXCHANGE_NAME, "", msg);//每两秒发送一次try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}return "send ok";}
}@Slf4j
@Component
public class MessageService {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendMessage(String exchange,String routingKey,Object msg) {// 暂时关闭 return 配置//rabbitTemplate.setReturnCallback(this);//发送消息rabbitTemplate.convertAndSend(exchange,routingKey,msg);}}

消费者:

public class MessageConsumer {//    @RabbitHandler : 标记的方法只能有一个参数,类型为String ,若是传Map参数、则需要传入map参数// @RabbitListener:标记的方法可以传入Channel, Message参数@RabbitListener(queues = MessageConfig.MESSAGE_QUEUE_NAME)public void listenObjectQueue(Channel channel, Message message, String msg) throws IOException {System.out.println("接收到object.queue的消息" + msg);System.out.println("消息ID : " + message.getMessageProperties().getDeliveryTag());try {channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);System.out.println("拒绝消息 , tag = " + message.getMessageProperties().getDeliveryTag());
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);}catch (IOException exception) {//拒绝确认消息channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);//拒绝消息
//            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);}}}

注入容器:

@Configuration
public class MessageConfig {public final static String EXCHANGE_NAME = "deadMessageTestExchange";public final static String MESSAGE_QUEUE_NAME = "deadMessageTestQueue";public final static String MESSAGE_ROUTE_KEY = "deadMessageTestRoutingKey";public final static String DEAD_EXCHANGE_KEY = "x-dead-letter-exchange";@Beanpublic FanoutExchange deadMessageTestExchange() {return new FanoutExchange(EXCHANGE_NAME);}@Beanpublic Queue deadMessageTestQueue() {HashMap<String, Object> params = new HashMap<>();params.put(DEAD_EXCHANGE_KEY, DeadConfig.DEAD_EXCHANGE);return new Queue(MESSAGE_QUEUE_NAME, true, false, false, params);}@Beanpublic MessageConsumer deadMessageTestConsumer() {return new MessageConsumer();}@Beanpublic Binding messageBinding(Queue deadMessageTestQueue, FanoutExchange deadMessageTestExchange) {return BindingBuilder.bind(deadMessageTestQueue).to(deadMessageTestExchange);}
}

延迟队列

RabbitMQ有提供插件使用延迟队列, 另外可借助 死信队列 实现延迟队列;
实现思路:

  1. 给普通队列设置消息过期时间(延迟时间), 不设置消费者;
  2. 当消息过期后,将消息放入死信队列, 给死信队列设置消费者;

懒队列

懒队列会尽可能早的将消息内容保存到硬盘当中,并且只有在用户请求到时,才临时从硬盘加载到RAM内存当中。 可解决部分消息积压问题、(海量消息积压,RabbitMQ存不下就得使用分布式存储消息)
适用的一些场景:

  • 消费者服务宕机了
  • 有一个突然的消息高峰,生产者生产消息超过消费者
  • 消费者消费太慢了

​ 默认情况下,RabbitMQ接收到消息时,会保存到内存以便使用,同时把消息写到硬盘。但是,
消息写入硬盘的过程是会阻塞队列的。RabbitMQ虽然做了优化,但是在长队列中表现不是很理想,所以有了懒队列、 以磁盘IO为代价解决消息积压问题;

SpringBoot懒队列声明方式:

@Configuration
public class LazyQueueConfig {@Beanpublic Queue lazyQueue() {HashMap<String, Object> params = new HashMap<>();params.put("x-queue-mode", "lazy");return new Queue("lazyQueue", true, false, false, params);}
}

原生API方式:

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-queue-mode", "lazy");
channel.queueDeclare("myqueue", false, false, false, args);

懒队列适合消息量大且长期有堆积的队列,可以减少内存使用,加快消费速度。但是这是以大量消耗集群的网络及磁盘IO为代价的。

集群模式

分布式环境下,是不允许单点故障存在,需要保证高可用, 因此需要集群环境保证高可用,另外若存在海量消息,还需要保证存放得下、即分布式存储;

普通集群模式

  • 集群的各个节点之间只会有相同的元数据,即队列结构,而消息不会进行冗余,只存在一个节点中。
  • 消费时,如果消费的不是存有数据的节点, RabbitMQ会临时在节点之间进行数据传输,将消息从存有数据的节点传输到消费的节点。
  • 此模式解决分布式存储问题、但可靠性不高,相当于多个单机服务,每个都是独立的,一个都不可以宕机。某台机器宕机、则存储的消息无法消费、若未开启持久化、则丢失消息, 若消费者正在处理消息,则机器无法收到确认信息,该消息重新入队,则重复消费;
  • 普通集群模式不支持高可用,即当某一个节点服务挂了后,需要手动重启服务,才能保证这一部分消息能正常消费。

镜像集群模式

  • 在普通集群的基础上,每次保存消息后,机器主动同步到多台机器上, 而不是消费者获取消息时,再去其他节点上获取;
  • 集群会选举主节点master, 当主节点挂了,则会重新选举;
  • 此方式实现了集群高可用,但是集群之间同步消息频繁,海量数据时、同步频率更大,导致占满带宽;

消息常见问题

RabbitMQ如何保证消息不丢失

先看看哪些情况下,会存在丢失消息?
在这里插入图片描述
1,2,4步骤是可能丢消息的,因为三个步骤都是跨网络的;

生产者保证消息正确发送到RibbitMQ

  • 对于单个数据,可以使用生产者确认机制。通过多次确认的方式,保证生产者的消息能够正确的发送到RabbitMQ中。
  • ​RabbitMQ的生产者确认机制分为同步确认和异步确认。同步确认主要是通过在生产者端使用Channel.waitForConfirmsOrDie()指定一个等待确认的完成时间。异步确认机制则是通过channel.addConfirmListener(ConfirmCallback var1, ConfirmCallback var2)在生产者端注入两个回调确认函数。第一个函数是在生产者消息发送成功时调用,第二个函数则是生产者消息发送失败时调用。两个函数需要通过sequenceNumber自行完成消息的前后对应。sequenceNumber的生成方式需要通过channel的序列获取。int sequenceNumber = channel.getNextPublishSeqNo();
  • ​ 如果发送批量消息,在RabbitMQ中,另外还有一种手动事务的方式,可以保证消息正确发送
  • 手动事务机制主要有几个关键的方法: channel.txSelect() 开启事务; channel.txCommit() 提交事务; channel.txRollback() 回滚事务; 用这几个方法来进行事务管理。但是这种方式需要手动控制事务逻辑,并且手动事务会对channel产生阻塞,造成吞吐量下降

RabbitMQ消息存盘不丢消息

消息若是只存内存中,则宕机会丢失消息, 因此队列需要开启持久化,durable参数、默认创建队列,durable都会为true; 而Quorum和Stream队列默认都是开启持久化;

RabbitMQ 主从消息同步时不丢消息

普通集群模式,消息是分散存储的,不会主动进行消息同步了,是有可能丢失消息的。而镜像模式集群,数据会主动在集群各个节点当中同步,这时丢失消息的概率不会太高。

RabbitMQ消费者不丢失消息

消费者确认,分为自动确认,手动确认;若是自动确认,则消息处理完,会返回确认ack;若是处理出现异常, 则会重新入队,再次处理, 因此存在重复消费问题;

若是手动确认,消息处理过程中使用channel#basicAck, basicNack, basicReject返回确认或拒绝;SpringBoot配置文件中通过属性spring.rabbitmq.listener.simple.acknowledge-mode需要设置mutual手动确认;

SpringBoot配置文件中通过属性spring.rabbitmq.listener.simple.acknowledge-mode 进行指定。可以设定为 AUTO 自动应答; MANUAL 手动应答;NONE 不应答;

如何保证消息幂等?

当消费者消费消息处理业务逻辑时,如果抛出异常,或者不向RabbitMQ返回响应,默认情况下,RabbitMQ会无限次数的重复进行消息消费。

处理幂等问题,要设定RabbitMQ的重试次数。在SpringBoot集成RabbitMQ时,可以在配置文件
中指定spring.rabbitmq.listener.simple.retry开头的一系列属性,来制定重试策略。

需要在业务上处理幂等问题, 处理幂等问题的关键是要给每个消息一个唯一的标识;虽然RabbitMQ会给每条消息带上MessageId (处理幂等问题的关键是要给每个消息一个唯一的标识);
SpringBoot框架集成RabbitMQ后,可以给每个消息指定一个全局唯一的MessageID,在消费者端针对MessageID做幂等性判断。

//发送者
Message message2 = MessageBuilder.withBody(message.getBytes()).setMessageId(UUID.randomUUID().toString()).build();
rabbitTemplate.send(message2);//消费者获取MessageID,自己做幂等性判断
@RabbitListener(queues = "fanout_email_queue")
public void process(Message message) throws Exception {// 获取消息IdString messageId = message.getMessageProperties().getMessageId();...
}

可为了业务上的方便,再封装一层, 专门用来放入消息ID, 否则设置ID的代码随处可见;

如何保证消息的顺序?

RabbitMQ中保证顺序的方法是 单队列+单消息推送; 若是多队列的情况下,RabbitMQ没有很好的解决方案;

个人思考:如果RabbitMQ架构上很难处理,可以通过业务设置保证顺序, 即给每条消息设置序号, 消费时、查询数据库之前的消息是否处理完,若没有查到,则等待一会, 若查得到,则处理消息,处理完后,把消息id + 序号 放入数据库代表已经处理完;

RabbitMQ的数据堆积问题

bbitMQ一直以来都有一个缺点,就是对于消息堆积问题的处理不好。当RabbitMQ中有大量消息堆积时,整体性能会严重下降。而目前新推出的Quorum队列以及Stream队列,目的就在于解决这个核心问题。目前大部分企业还是围绕Classic经典队列构建应用。因此,在使用RabbitMQ时,还是要非常注意消息堆积的问题。尽量让消息的消费速度和生产速度保持一致。

  • 对于生产者:
    最明显的方式自然是降低消息生产的速度。但是,生产者端产生消息的速度通常是跟业务息息相关的,一般情况下不太好直接优化。但是可以选择尽量多采用批量消息的方式,降低IO频率。
  • 对于服务器端
    • 可使用懒队列方式存储 部分消息积压(单机的磁盘容量还是有限)
    • 可使用Sharding分片队列(分布式存储)
  • 对于消费者
    • 检查业务代码是不是太挫了, 优化代码
    • 代码性能没问题、则要增加消费者数量,提升消费速度;
    • 若是经常存在海量消息,则可以放入数据库、慢慢消费;

相关文章:

RaabitMQ(三) - RabbitMQ队列类型、死信消息与死信队列、懒队列、集群模式、MQ常见消息问题

RabbitMQ队列类型 Classic经典队列 这是RabbitMQ最为经典的队列类型。在单机环境中&#xff0c;拥有比较高的消息可靠性。 经典队列可以选择是否持久化(Durability)以及是否自动删除(Auto delete)两个属性。 Durability有两个选项&#xff0c;Durable和Transient。 Durable表…...

Unity3D GPU Selector/Picker

Unity3D GPU Selector/Picker 一、概述 1.动机 Unity3D中通常情况下使用物理系统进行物体点击选择的基础&#xff0c;对于含大量对象的场景&#xff0c;添加Collider组件会增加内容占用&#xff0c;因此使用基于GPU的点击选择方案 2.实现思路 对于场景的每个物体&#xff0c;…...

灰度非线性变换之c++实现(qt + 不调包)

本章介绍灰度非线性变换&#xff0c;具体内容包括&#xff1a;对数变换、幂次变换、指数变换。他们的共同特点是使用非线性变换关系式进行图像变换。 1.灰度对数变换 变换公式&#xff1a;y a log(1x) / b&#xff0c;其中&#xff0c;a控制曲线的垂直移量&#xff1b;b为正…...

轻量级Web框架Flask

Flask-SQLAlchemy MySQL是免费开源软件&#xff0c;大家可以自行搜索其官网&#xff08;https://www.MySQL.com/downloads/&#xff09; 测试MySQL是否安装成功 在所有程序中&#xff0c;找到MySQL→MySQL Server 5.6下面的命令行工具&#xff0c;然后单击输入密码后回车&am…...

【gridsample】地平线如何支持gridsample算子

文章目录 1. grid_sample算子功能解析1.1 理论介绍1.2 代码分析1.2.1 x,y取值范围[-1,1]1.2.2 x,y取值范围超出[-1,1] 2. 使用grid_sample算子构建一个网络3. 走PTQ进行模型转换与编译 实操以J5 OE1.1.60对应的docker为例 1. grid_sample算子功能解析 该段主要参考&#xff1a;…...

JPA实现存储实体类型信息

本文已收录于专栏 《Java》 目录 背景介绍概念说明DiscriminatorValue 注解&#xff1a;DiscriminatorColumn 注解&#xff1a;Inheritance(strategy InheritanceType.SINGLE_TABLE) 注解&#xff1a; 实现方式父类子类执行效果 总结提升 背景介绍 在我们项目开发的过程中经常…...

阿里云快速部署开发环境 (Apache + Mysql8.0+Redis7.0.x)

本文章的内容截取于云服务器管理控制台提供的安装步骤&#xff0c;再整合前人思路而成&#xff0c;文章末端会提供原文连接 ApacheMysql 8.0部署MySQL数据库&#xff08;Linux&#xff09;步骤一&#xff1a;安装MySQL步骤二&#xff1a;配置MySQL步骤三&#xff1a;远程访问My…...

语音秘书:让录音转文字识别软件成为你的智能工作助手

每当在需要写文章的深夜&#xff0c;我的思绪经常跟不上我的笔&#xff0c;即便是说出来用录音机录下&#xff0c;再书写出来&#xff0c;也需要耗费大量时间。这个困扰了我很久的问题终于有了解决的办法&#xff0c;那就是录音转文字软件。它像个语言魔术师&#xff0c;将我所…...

【腾讯云 Cloud Studio 实战训练营】用于编写、运行和调试代码的云 IDE泰裤辣

文章目录 一、引言✉️二、什么是腾讯云 Cloud Studio&#x1f50d;三、Cloud Studio优点和功能&#x1f308;四、Cloud Studio初体验&#xff08;注册篇&#xff09;&#x1f386;五、Cloud Studio实战演练&#xff08;实战篇&#xff09;&#x1f52c;1. 初始化工作空间2. 安…...

[C#] 简单的俄罗斯方块实现

一个控制台俄罗斯方块游戏的简单实现. 已在 github.com/SlimeNull/Tetris 开源. 思路 很简单, 一个二维数组存储当前游戏的方块地图, 用 bool 即可, true 表示当前块被填充, false 表示没有. 然后, 抽一个 “形状” 类, 形状表示当前玩家正在操作的一个形状, 例如方块, 直线…...

postman官网下载安装登录详细教程

目录 一、介绍 二、官网下载 三、安装 四、注册登录postman账号&#xff08;不注册也可以&#xff09; postman注册登录和不注册登录的使用区别 五、关于汉化的说明 一、介绍 简单来说&#xff1a;是一款前后端都用来测试接口的工具。 展开来说&#xff1a;Postman 是一个…...

(贪心) 剑指 Offer 14- I. 剪绳子 ——【Leetcode每日一题】

❓剑指 Offer 14- I. 剪绳子 难度&#xff1a;中等 给你一根长度为 n 的绳子&#xff0c;请把绳子剪成整数长度的 m 段&#xff08;m、n都是整数&#xff0c;n > 1 并且 m > 1&#xff09;&#xff0c;每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m…...

如何将Linux上的cpolar内网穿透设置成 - > 开机自启动

如何将Linux上的cpolar内网穿透设置成 - > 开机自启动 文章目录 如何将Linux上的cpolar内网穿透设置成 - > 开机自启动前言一、进入命令行模式二、输入token码三、输入内网穿透命令 前言 我们将cpolar安装到了Ubuntu系统上&#xff0c;并通过web-UI界面对cpolar的功能有…...

50.两数之和(力扣)

目录 问题描述 核心代码解决 代码思想 时间复杂度和空间复杂度 问题描述 给定一个整数数组 和一个整数目标值 &#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。numstarget 你可以假设每种输入只会对应一个答案。但是&am…...

k8s基础

k8s基础 文章目录 k8s基础一、k8s组件二、k8s组件作用1.master节点2.worker node节点 三、K8S创建Pod的工作流程&#xff1f;四、K8S资源对象1.Pod2.Pod控制器3.service && ingress 五、K8S资源配置信息六、K8s部署1.K8S二进制部署2.K8S kubeadm搭建 七、K8s网络八、K8…...

【自然语言处理】大模型高效微调:PEFT 使用案例

文章目录 一、PEFT介绍二、PEFT 使用2.1 PeftConfig2.2 PeftModel2.3 保存和加载模型 三、PEFT支持任务3.1 Models support matrix3.1.1 Causal Language Modeling3.1.2 Conditional Generation3.1.3 Sequence Classification3.1.4 Token Classification3.1.5 Text-to-Image Ge…...

FFmpeg将编码后数据保存成mp4

以下测试代码实现的功能是&#xff1a;持续从内存块中获取原始数据&#xff0c;然后依次进行解码、编码、最后保存成mp4视频文件。 可保存成单个视频文件&#xff0c;也可指定每个视频文件的总帧数&#xff0c;保存多个视频文件。 为了便于查看和修改&#xff0c;这里将可独立的…...

设置VsCode 将打开的多个文件分行(栏)排列,实现全部显示

目录 1. 前言 2. 设置VsCode 多文件分行(栏)排列显示 1. 前言 主流编程IDE几乎都有排列切换选择所要查看的文件功能&#xff0c;如下为Visual Studio 2022的该功能界面&#xff1a; 图 1 图 2 当在Visual Studio 2022打开很多文件时&#xff0c;可以按照图1、图2所示找到自…...

Vue.js2+Cesium1.103.0 六、标绘与测量

Vue.js2Cesium1.103.0 六、标绘与测量 点&#xff0c;线&#xff0c;面的绘制&#xff0c;可实时编辑图形&#xff0c;点击折线或多边形边的中心点&#xff0c;可进行添加线段移动顶点位置等操作&#xff0c;并同时计算出点的经纬度&#xff0c;折线的距离和多边形的面积。 De…...

【redis 延时队列】使用go-redis的list做异步,生产消费者模式

分享一个用到的&#xff0c;使用go-redis的list做异步&#xff0c;生产消费者模式&#xff0c;接着再用 go 协程去检测队列里是否有东西去消费 如果队列为空&#xff0c;就会一直pop&#xff0c;空轮询导致 cpu 资源浪费和redis qps无效升高&#xff0c;所以可以通过 time.Sec…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...