RocketMQ 生产者源码分析:DefaultMQProducer、DefaultMQProducerImpl

在这里插入图片描述

🔭 嗨,您好 👋 我是 vnjohn,在互联网企业担任 Java 开发,CSDN 优质创作者
📖 推荐专栏:Spring、MySQL、Nacos、Java,后续其他专栏会持续优化更新迭代
🌲文章所在专栏:RocketMQ
🤔 我当前正在学习微服务领域、云原生领域、消息中间件等架构、原理知识
💬 向我询问任何您想要的东西,ID:vnjohn
🔥觉得博主文章写的还 OK,能够帮助到您的,感谢三连支持博客🙏
😄 代词: vnjohn
⚡ 有趣的事实:音乐、跑步、电影、游戏

目录

  • 前言
  • MQProducer
    • 方法介绍
    • 消息可靠性
  • DefaultMQProducer
    • 属性介绍
    • 构造方法
    • 参数配置
  • DefaultMQProducerImpl
    • 属性介绍
    • 构造方法
  • ClientConfig
  • 总结

前言

生产者(Producer)就是消息的发送者,Apache RocketMQ 拥有丰富的消息类型,可以支持不同的应用场景,在不同的场景中,需要使用不同的消息进行发送。比如在电商交易中超时未支付关闭订单的场景,在订单创建时会发送一条延时消息。这条消息将会在 30 分钟以后投递给消费者,消费者收到此消息后需要判断对应的订单是否已完成支付。如支付未完成,则关闭订单。如已完成支付则忽略,此时就需要用到延迟消息;电商场景中,业务上要求同一订单的消息保持严格顺序,此时就要用到顺序消息。在日志处理场景中,可以接受的比较大的发送延迟,但对吞吐量的要求很高,希望每秒能处理百万条日志,此时可以使用批量消息。在银行扣款的场景中,要保持上游的扣款操作和下游的短信通知保持一致,此时就要使用事务消息

MQProducer

MQProducer 是基于最顶层的接口,它将一些子类各自行为抽象为定义

在这里插入图片描述

方法介绍

常见的几个方法定义如下:

// 采用同步的方式发送消息,此方法仅在发送过程全部完成以后才返回
SendResult send(final Message msg);// 采用异步的方式将其发送到 Broker 中,执行该方法会立即返回,在发送完成以后,将会执行 SellCallbak 函数的内容
void send(final Message msg, final SendCallback sendCallback);// 此方法在返回之前不会等待broker的确认。显然,它具有最大的吞吐量,但也存在消息丢失的可能性
void sendOneway(final Message msg);// 消息指定同一个 Message-Queue 来保证消息的顺序发送
SendResult send(final Message msg, final MessageQueueSelector selector, final Object arg);// 该方法用于发送事务类型的消息,由 TransactionMQProducer 类具体去实现
TransactionSendResult sendMessageInTransaction(final Message msg, final Object arg);// 采用同步的方式,请求消息并回复消息时才返回
Message request(final Message msg, final long timeout);// 采用异步的方式请求消息,该方法会立即返回,当收到回复消息以后,会调用 RequestCallback 函数的内容
void request(final Message msg, final RequestCallback requestCallback, final long timeout);

生产者发送消息有同步、异步、单向发送、顺序的方式,同步和异步的方式对消息来说是较为可靠的,因为它们会有发送是否成功的应答

在 send 方法处不 + SendCallback 实现,默认就是同步的发送方式

既然是投递消息,由于网络抖动或业务异常等原因,会影响到消息的正常投递结果,在 RocketMQ 内部有重投的机制来保证这种错误的发生

消息可靠性

生产者:重投保证消息可靠性

生产者在发送消息时,同步消息失败会重投,异步消息有重试,oneway 没有任何保证。消息重投保证消息尽可能发送成功、不丢失,但可能会造成消息重复,消息重复在 RocketMQ 中是无法避免的问题。消息重复在一般情况下不会发生,当出现消息量大、网络抖动,消息重复就会是大概率事件。另外,生产者主动重发、consumer 负载变化也会导致重复消息。如下方法可以设置消息重试策略:

  1. retryTimesWhenSendFailed:同步发送失败重投次数,默认为 2,因此生产者会最多尝试发送 retryTimesWhenSendFailed + 1次。不会选择上次失败的 broker,尝试向其他 broker 发送,最大程度保证消息不丢失。超过重投次数,抛出异常,由客户端保证消息不丢。当出现 RemotingException、MQClientException 和部分 MQBrokerException 时会重投

    由 RocketMQ 重投机制来保证的情况下,消息仍然是处于投递失败的情况,可能是因为 MQ 集群在此时处于一个不健康的状态下,在业务应用的场景下,当所处的业务特别之重要时,我们通常需要增加本地消息表来保证业务信息不丢失,通过定时任务的方式来扫描本地消息表确保业务消息数据处于健康态。

  2. retryTimesWhenSendAsyncFailed:异步发送失败重试次数,异步重试不会选择其他 broker,仅在同一个 broker 上做重试,不保证消息不丢失

    由于它处于异步的方式发送,在发送完成以后不做任何生产者侧状态的维护了,自然而然不会增加额外的维护成本去更换 Broker

  3. retryAnotherBrokerWhenNotStoreOK:消息刷盘(主或备)超时或 Slave 不可用(返回状态非 SEND_OK),是否尝试发送到其他broker,默认 false。十分重要消息可以开启

以上考虑的是在生产者侧的角度来看「如何保证消息不丢失」问题,追加消息准确的极致是采用同步的方式进行发送

单向发送:在日志传输的场景下特别常用

异步发送:追求的是响应时间,在一块业务上不要求 RT 太长.
顺序发送:业务上追加消息处理的顺序性

同步发送:企业围绕这块的重要业务开展工作的

在针对消息的重投机制,应用程序这侧要针对业务字段维护好消息的幂等性

Broker:配置降低吞吐保证消息可靠性

SEND_OK 消息发送成功,要注意的是消息发送成功也不意味着它是可靠的。要确保不会丢失任何消息,还应启用同步 Master 服务器或同步刷盘,即 SYNC_MASTER 或 SYNC_FLUSH

因为在生产者投递消息到 Broker 时,Broker 使用默认的方式进行消息落盘时,运用的仍然是内核的 page cache 页缓存机制,它只有当内存的容量达到一定数量以后才会才 Dirty 刷写到磁盘中去,在此期间,发送了机器故障或机房停电等情况下,消息是有发送丢失风险的.

DefaultMQProducer

DefaultMQProducer 是作为一个生产消息的应用程序入口,上面介绍的一些相关的参数配置都在该类能够体现

属性介绍

// 生产者组
private String producerGroup;
// 创建每个默认 Topic 的队列数
private volatile int defaultTopicQueueNums = 4;
// 发送消息超时时间
private int sendMsgTimeout = 3000;
// 压缩消息体的阈值,即默认压缩大于 4K 的消息体
private int compressMsgBodyOverHowmuch = 1024 * 4;
// 在同步模式下声明发送失败之前,要在内部执行的最大重试次数
private int retryTimesWhenSendFailed = 2;
// 在异步模式下声明发送失败之前,要在内部执行的最大重试次数
private int retryTimesWhenSendAsyncFailed = 2;
// 是否在内部重试失败时重试另外一个 Broker
private boolean retryAnotherBrokerWhenNotStoreOK = false;
// 最大允许的消息大小,以字节为单位,4M
private int maxMessageSize = 1024 * 1024 * 4;
// 异步传输的数据接口
private TraceDispatcher traceDispatcher = null;// DefaultMQProducer 类所有方法进行内部包装实现,互相持有对方的引用!!!
// DefaultMQProducer -> DefaultMQProducerImpl
// DefaultMQProducerImpl -> DefaultMQProducer
protected final transient DefaultMQProducerImpl defaultMQProducerImpl;// 17、14、1、16、204、205
// 当发送消息到 Broker 处时,Broker 返回的是以下这些状态码,代表是需要进行重投的!
private final Set<Integer> retryResponseCodes = new CopyOnWriteArraySet<Integer>(Arrays.asList(ResponseCode.TOPIC_NOT_EXIST,ResponseCode.SERVICE_NOT_AVAILABLE,ResponseCode.SYSTEM_ERROR,ResponseCode.NO_PERMISSION,ResponseCode.NO_BUYER_ID,ResponseCode.NOT_IN_CURRENT_UNIT
));

构造方法

对于 DefaultMQProducer 有以下几个方法的构造:

// 默认构造,默认组名:DEFAULT_PRODUCER
public DefaultMQProducer() {this(null, MixAll.DEFAULT_PRODUCER_GROUP, null);
}
// 构造一个指定钩子的生产者实例
// RPCHook 当执行每个远程命令调用时会触发的,可用于做一些统计指标
public DefaultMQProducer(RPCHook rpcHook) {this(null, MixAll.DEFAULT_PRODUCER_GROUP, rpcHook);
}// 构造一个指定生产者组的实例
public DefaultMQProducer(final String producerGroup) {this(null, producerGroup, null);
}// 构造一个指定生产者组的实例
// namespace 用于隔离生产者实例
public DefaultMQProducer(final String namespace, final String producerGroup) {this(namespace, producerGroup, null);
}// 构建一个指定的命名空间、生产者组以及 Rpc 钩子
// DefaultMQProducerImpl(this:才是最重要的!!
public DefaultMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook) {this.namespace = namespace;this.producerGroup = producerGroup;defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
}

参数配置

public void setProducerGroup(String producerGroup) {this.producerGroup = producerGroup;
}public void setCreateTopicKey(String createTopicKey) {this.createTopicKey = createTopicKey;
}public void setSendMsgTimeout(int sendMsgTimeout) {this.sendMsgTimeout = sendMsgTimeout;
}public void setCompressMsgBodyOverHowmuch(int compressMsgBodyOverHowmuch) {this.compressMsgBodyOverHowmuch = compressMsgBodyOverHowmuch;
}public void setRetryAnotherBrokerWhenNotStoreOK(boolean retryAnotherBrokerWhenNotStoreOK) {this.retryAnotherBrokerWhenNotStoreOK = retryAnotherBrokerWhenNotStoreOK;
}public void setMaxMessageSize(int maxMessageSize) {this.maxMessageSize = maxMessageSize;
}public void setDefaultTopicQueueNums(int defaultTopicQueueNums) {this.defaultTopicQueueNums = defaultTopicQueueNums;
}public void setDefaultTopicQueueNums(int defaultTopicQueueNums) {this.defaultTopicQueueNums = defaultTopicQueueNums;
}public void setRetryTimesWhenSendFailed(int retryTimesWhenSendFailed) {this.retryTimesWhenSendFailed = retryTimesWhenSendFailed;
}public void setNotAvailableDuration(final long[] notAvailableDuration) {this.defaultMQProducerImpl.setNotAvailableDuration(notAvailableDuration);
}public void setLatencyMax(final long[] latencyMax) {this.defaultMQProducerImpl.setLatencyMax(latencyMax);
}public void setRetryTimesWhenSendAsyncFailed(final int retryTimesWhenSendAsyncFailed) {this.retryTimesWhenSendAsyncFailed = retryTimesWhenSendAsyncFailed;
}
名称描述参数类型默认值
producerGroup生产组的名称,一类 Producer 的标识StringDEFAULT_PRODUCER
createTopicKey发送消息的时候,如果没有找到 Topic,若想自动创建该 Topic,需要一个 Key Topic,这个值即是 Key Topic 的值StringTopicValidator.AUTO_CREATE_TOPIC_KEY_TOPIC
defaultTopicQueueNums自动创建 Topic的话,默认 Queue 数量是多少int4
sendMsgTimeout默认的发送超时时间int默认值 3000,单位毫秒
compressMsgBody
OverHowmuch
消息 Body 需要压缩的阈值int默认值 1024*4,4K
retryTimesWhenSendFailed同步发送失败的话,RocketMQ 内部重试多少次int2
retryTimes
WhenSendAsyncFailed
异步发送失败的话,RocketMQ 内部重试多少次int2
retryAnotherBroker
WhenNotStoreOK
发送的结果如果不是 SEND_OK 状态,是否当作失败处理而尝试往其他 Broker 重发booleanfalse
maxMessageSize客户端验证,允许发送的最大消息体大小int1014 * 1024 * 4,4M
traceDispatcher异步传输数据接口TraceDispatcher

latencyMax、notAvailableDuration:用于设置容错策略不可用时每个级别对应的退避时长,在发送消息时计算发送成功的前后时间然后对比策略时长,围绕的是如下数组从后到前遍历的,比如:在往 Broker-A 同步投递消息时,发送消息的时长为 3000 毫秒 3 秒,那么下次我逃避它不再往这个 Broker 上投递了,先退避 180000 毫秒 3 分钟

private long[] latencyMax = {50L, 100L, 550L, 1000L, 2000L, 3000L, 15000L};
// notAvailableDuration 参数调整的就是这个退避时长,每一个元素{可}以 latencyMax 下标一一对应
private long[] notAvailableDuration = {0L, 0L, 30000L, 60000L, 120000L, 180000L, 600000L};

retryTimesWhenSendAsyncFailed:在异步模式下声明发送失败之前,要在内部执行的最大重试次数

isAutoCreateTopicEnable:生产环境强烈建议管理所有主题的生命周期,关闭自动创建参数,以避免生产集群出现大量无效主题,无法管理和回收,造成集群注册压力增大,影响生产集群的稳定性。

DefaultMQProducerImpl

MQProducer 与 DefaultMQProducerImpl 是互相持有的一个关系,DefaultMQProducerImpl 的实例化工作交由给 MQProducer 来完成,在这里又做了一层抽象,DefaultMQProducer 的方法定义来自于 MQProducer,DefaultMQProducer 的实现来自于 DefaultMQProducerImpl

属性介绍

下面,我们来介绍 DefaultMQProducerImpl 类下具体的一些属性,如下:

// 每次发送消息都会生成一个随机数,记录日志
private final Random random = new Random();
// 持有来自实例化当前类的引用
private final DefaultMQProducer defaultMQProducer;
// 每个 Topic 所持有的 MessageQueue 以及 Broker 元数据信息
private final ConcurrentMap<String/* topic */, TopicPublishInfo> topicPublishInfoTable =new ConcurrentHashMap<String, TopicPublishInfo>();
// 来自发送普通消息前、后要处理的钩子
private final ArrayList<SendMessageHook> sendMessageHookList = new ArrayList<SendMessageHook>();
private final ArrayList<EndTransactionHook> endTransactionHookList = new ArrayList<EndTransactionHook>();
// 执行远程调用命令触发前后的钩子
private final RPCHook rpcHook;
// 通过异步方式发送普通消息时采用的线程池以及阻塞队列
private final BlockingQueue<Runnable> asyncSenderThreadPoolQueue;
private final ExecutorService defaultAsyncSenderExecutor;
// 通过异步方式检查本地事务消息时采用的线程池以及阻塞队列
protected BlockingQueue<Runnable> checkRequestQueue;
protected ExecutorService checkExecutor;
// 维护生产者服务的状态
private ServiceState serviceState = ServiceState.CREATE_JUST;
// 生产者、消费者都会维护一个这样的客户端示例,来与 broker 做消息的处理
private MQClientInstance mQClientFactory;
private ArrayList<CheckForbiddenHook> checkForbiddenHookList = new ArrayList<CheckForbiddenHook>();
private int zipCompressLevel = Integer.parseInt(System.getProperty(MixAll.MESSAGE_COMPRESS_LEVEL, "5"));
// MQ 提供的容错机制,针对 sendLatencyFaultEnable 参数是否开启故障转移的功能
private MQFaultStrategy mqFaultStrategy = new MQFaultStrategy();
// defaultAsyncSenderExecutor | asyncSenderExecutor 在发送消息采用的那个线程池
private ExecutorService asyncSenderExecutor;

介绍一些核心的属性,在生产者这一侧做了一些特别的处理:

  1. topicPublishInfoTable 属性:维护的是一个 Map 集合,Key -> Topic、Value -> Topic MessageQueue 以及 Broker 元数据信息,注意的是,它不是在一开始实例化时将所有 Topic 信息都加载好的,而是采用延迟的方式在启动生产者客户端实例通过定时线程去加载的,核心方法是:tryToFindTopicPublishInfo

  2. defaultAsyncSenderExecutor | asyncSenderExecutor、asyncSenderThreadPoolQueue:在这里有两个线程池,二选一的决策,我们在构建生产者实例时是可以自定义一个线程池「分配资源」去投递消息的,自定义的线程池说的就是 asyncSenderExecutor,未自定义线程池时用的就是 defaultAsyncSenderExecutor,配合使用的阻塞队列就是 asyncSenderThreadPoolQueue

    this.defaultAsyncSenderExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors(),1000 * 60,TimeUnit.MILLISECONDS,this.asyncSenderThreadPoolQueue,new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "AsyncSenderExecutor_" + this.threadIndex.incrementAndGet());}});
    
  3. checkExecutor、checkRequestQueue:该线程池是用于处理事务型消息的,检查本地事务消息的方法是采用异步回调的方式,也是支持自定义线程池的,若不指定,默认采用的是单线程的方式去检查本地事务消息状态的
    在这里插入图片描述

  4. mQClientFactory:在每个应用程序内,都是单个实例的存在,无论是生产者端还是消费者端,都会有这么一个类去充当 RocketMQ 的客户端角色,它用来与服务端 Broker 交互,在它内部有一个 MQClientAPIImpl 客户端 API 类,它用来发起远程调用请求以及响应请求的,具体的核心作用我们后续再介绍

  5. mqFaultStrategy:MQ 提供的容错机制,在生产者投递消息时做故障转移以及降级作用的

在这里介绍一个知识点,在 RocketMQ 无论是生产消息还是消费消息都是采用负载均衡的方式去进行处理的,针对的其实就是 TopicPublishInfo 的处理,Producer 生产者端在发送消息的时候,会先根据 Topic 找到指定的 TopicPublishInfo,在获取了 TopicPublishInfo 路由信息后,RocketMQ 的客户端在默认方式下 MQFaultStrategy#selectOneMessageQueue 方法会从 TopicPublishInfo 中 messageQueueList 中选择一个队列(MessageQueue)进行发送消息,同时在这里结合了容错策略的处理是以上介绍的另外一个属性:mqFaultStrategy

具体的容错策略均在 MQFaultStrategy 这个类中定义。这里有一个 sendLatencyFaultEnable 开关变量,如果开启,在随机递增取模的基础上,再过滤掉 not available 的 Broker 代理。所谓的"latencyFaultTolerance",是指对之前失败的,按一定的时间做退避。例如,如果上次请求的 latency 超过 550L ms,就退避 30000Lms;超过 1000L,就退避 60000L;如果关闭,采用随机递增取模的方式选择一个队列(MessageQueue)来发送消息,latencyFaultTolerance 机制的开启是实现消息发送高可用的核心关键所

在一个 Topic 下有多个 MessageQueue,在集群模式下, 一组 MessageQueue 是分散在多个 Broker 节点下的

在这里插入图片描述

构造方法

public DefaultMQProducerImpl(final DefaultMQProducer defaultMQProducer) {this(defaultMQProducer, null);
}public DefaultMQProducerImpl(final DefaultMQProducer defaultMQProducer, RPCHook rpcHook) {this.defaultMQProducer = defaultMQProducer;this.rpcHook = rpcHook;this.asyncSenderThreadPoolQueue = new LinkedBlockingQueue<Runnable>(50000);this.defaultAsyncSenderExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors(),1000 * 60,TimeUnit.MILLISECONDS,this.asyncSenderThreadPoolQueue,new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "AsyncSenderExecutor_" + this.threadIndex.incrementAndGet());}});
}

在它的构造方法中,只是将投递异步消息默认的线程池构造了出来,并未做其他的处理,

ClientConfig

客户端常用配置:适用于生产者、消费者

DefaultMQProducer、TransactionMQProducer、DefaultMQPushConsumer、DefaultMQPullConsumer 都继承于 ClientConfig 类,ClientConfig 为客户端的公共配置类。客户端的配置都是 getter、setter 形式,每个参数都可以用 Spring 来配置,也可以在代码中配置,例如 namesrvAddr 这个参数可以这样配置,producer.setNamesrvAddr(“192.168.0.1:9876”),其他参数同理

名称描述参数类型默认值
namesrvAddrNameServer的地址列表String从 -D 系统参数 rocketmq.namesrv.addr 或环境变量
instanceName客户端实例名称String从 -D 系统参数 rocketmq.client.name 获取,否则就是 DEFAULT
clientIP客户端IPStringRemotingUtil.getLocalAddress()
namespace客户端命名空间String
accessChannel设置访问通道AccessChannelLOCAL
clientCallbackExecutorThreads客户端通信层接收到网络请求的时候,处理器的核数intRuntime.getRuntime().availableProcessors()
pollNameServerInterval轮询从 NameServer 获取路由信息的时间间隔int默认值 30000,单位毫秒
heartbeatBrokerInterval定期发送注册心跳到 broker 的间隔int默认值 30000,单位毫秒
persistConsumerOffsetInterval作用于 Consumer,持久化消费进度的间隔int默认值 5000,单位毫秒
pullTimeDelayMillsWhenException拉取消息出现异常的延迟时间设置long默认值 1000,单位毫秒
vipChannelEnabled是否启用vip netty通道以发送消息boolean从 -D com.rocketmq.sendMessageWithVIPChannel 参数的值,若无则是 true
mqClientApiTimeoutMQ 客户端 API 超时设置默认值 3000,单位毫秒

总结

该篇博文主要介绍生产者侧相关的类:MQProducer 能力定义者、DefaultMQProducer 能力抽象者、DefaultMQProducerImpl 能力实现者、ClientConfig RocketMQ 客户端公共的配置类,主要围绕它们内部的参数配置以及属性先初步开展介绍了一下,后续主要在启动 Producer 以及通过 Producer 发送消息时,在生产者这一侧处理的工作,希望该篇博文你能够喜欢,感谢三连支持❤️

🌟🌟🌟愿你我都能够在寒冬中相互取暖,互相成长,只有不断积累、沉淀自己,后面有机会自然能破冰而行!

博文放在 RocketMQ 专栏里,欢迎订阅,会持续更新!

如果觉得博文不错,关注我 vnjohn,后续会有更多实战、源码、架构干货分享!

推荐专栏:Spring、MySQL,订阅一波不再迷路

大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下文见!

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

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

相关文章

第6课 用window API捕获麦克风数据并加入队列备用

今天是2024年1月1日&#xff0c;新年的第一缕阳光已经普照大地&#xff0c;祝愿看到这篇文章的所有程序员或程序爱好者都能在新的一年里持之以恒&#xff0c;事业有成。 今天也是我加入CSDN的第4100天&#xff0c;但回过头看一看&#xff0c;这么长的时间也没有在CSDN写下几篇…

【docker实战】02 用docker安装mysql

本示例采用bitnami的镜像进行安装MySQL&#xff0c;官网链接&#xff1a;https://bitnami.com/stacks 一、镜像搜索 先搜索一下mysql有哪些镜像 [rootlocalhost ~]# docker search mysql NAME DESCRIPTION …

啊?这也算事务?!

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

【SAM系列】Auto-Prompting SAM for Mobile Friendly 3D Medical Image Segmentation

论文链接&#xff1a;https://arxiv.org/pdf/2308.14936.pdf 核心&#xff1a; finetune SAM,为了不依赖外部prompt&#xff0c;通过将深层的特征经过一个编-解码器来得到prompt embedding&#xff1b;finetune完之后做蒸馏

c语言:用指针输入两个数组|练习题

一、题目 利用指针&#xff0c;输入两个数组 如图&#xff1a; 二、代码截图【带注释】 三、源代码【带注释】 #include <stdio.h> int main() { int a[50]; int b[50]; int *paa,*pbb; //输入第一组数组 printf("请输入第一组5个数字&#xff1a;…

骑砍战团MOD开发(27)-module_tableau_materials.py材质

一.配置材质资源 OpenBrf寻找对应材质资源. tableau配置材质资源 ("round_shield_1", 0, "sample_shield_round_1", 512, 256, 0, 0, 0, 0,[(store_script_param, ":banner_mesh", 1),(set_fixed_point_multiplier, 100),(init_position, pos1),…

【中南林业科技大学】计算机组成原理复习包括题目讲解(超详细)

来都来了点个赞收藏关注一下再走呗&#x1f339;&#x1f339;&#x1f339;&#x1f339; 第1章&#xff1a;绪论 1.冯诺依曼机特点&#xff0c;与现代计算机的区别 冯诺依曼计算机的基本思想是&#xff1a;程序和数据以二进制形式表示&#xff0c;存储程序控制。在计算机中&…

ctfshow——文件上传

文章目录 文件上传思路web 151web 152web 153知识点解题 web 154web 155web 156web 157web 158web 159web160web 161 文件上传思路 web 151 打开页面显示&#xff1a;前台校验不可靠。说明这题是前端验证。 右键查看源代码&#xff0c;找到与上传点有关的前端代码&#xff1a…

Android 13 默认关闭 快速打开相机

介绍 在设置菜单的手势界面里&#xff0c;快速打开相机是默认开启的&#xff0c;此功能当开启时连续点击两次电源键会打开相机&#xff0c;现在客户需要默认关闭。 效果展示 修改 这里一开始想到的就是配置文件&#xff0c;在路径下果然找到了,从注释中看使我们需要的&#x…

Vue中使用JavaScript中的requestAnimationFrame动画循环实现循环滚动效果-demo

效果 requestAnimationFrame是一个由浏览器提供的 JavaScript 方法&#xff0c;用于在下一次浏览器重绘之前执行指定的回调函数。它接受一个回调函数作为参数&#xff0c;并返回一个整数值&#xff0c;可以用于取消动画循环。 使用 requestAnimationFrame 可以创建平滑的动画效…

机器学习分类

1. 监督学习 监督学习指的是人们给机器一大堆标记好的数据&#xff0c;比如&#xff1a; 一大堆照片&#xff0c;标记出哪些是猫的照片&#xff0c;哪些是狗的照片 让机器自己学习归纳出算法或模型 使用该算法或模型判断出其他没有标记的照片是否是猫或狗 上述流程如下图所…

二叉树详解(深度优先遍历、前序,中序,后序、广度优先遍历、二叉树所有节点的个数、叶节点的个数)

目录 一、树概念及结构(了解) 1.1树的概念 1.2树的表示 二、二叉树概念及结构 2.1概念 2.2现实中的二叉树&#xff1a; 2.3数据结构中的二叉树&#xff1a; 2.4特殊的二叉树&#xff1a; 2.5 二叉树的存储结构 2.51 顺序存储&#xff1a; 2.5.2 链式存储&…

1月2日代码随想录二叉树的最小深度及层序遍历总结

个人认为这么一个层序遍历的章节放这么多基本一样的题目算是很没意思的了 填充每个节点的下一个右侧节点和二叉树最大深度和前面的代码几乎完全一样&#xff0c;所以我就跳过了 代码随想录 (programmercarl.com) 代码随想录 (programmercarl.com) 111.二叉树的最小深度 给…

Ant Design Vue 编译后的网页特点是什么,怎么确认他是用的前端 Ant Design Vue 技术栈的呢?

Ant Design Vue 是一个前端 UI 框架&#xff0c;使用 Vue.js 构建。它包含了大量的预设样式和组件&#xff0c;如按钮、表单、表格等&#xff0c;可以帮助开发者快速构建出优雅且功能丰富的网页。但是&#xff0c;要确定一个编译后的网页是否使用了 Ant Design Vue&#xff0c;…

【解决】电脑上的WIFI图标不见了咋整?

相信不少同学都遇到过这种情况&#xff1a;电脑上的wifi图标莫名不见了&#xff0c;甚至有时候还是在使用的中途突然断网消失的。 遇到这种情况一般有两种解决方案&#xff1a; 1. 在开机状态下长按电源键30秒以上 这种办法应该是给主板放电&#xff0c;一般应用在wifi6上面。…

OpenCV实战 -- 维生素药片的检测记数

文章目录 检测记数原图经过操作开始进行消除粘连性--形态学变换总结实现方法1. 读取图片&#xff1a;2. 形态学处理&#xff1a;3. 二值化&#xff1a;4. 提取轮廓&#xff1a;5. 轮廓筛选和计数&#xff1a; 分水岭算法&#xff1a;逐行解释在基于距离变换的分水岭算法中&…

WPF+Halcon 培训项目实战 完结(13):HS 鼠标绘制图形

文章目录 前言相关链接项目专栏运行环境匹配图片矩形鼠标绘制Halcon添加右键事件Task封装运行结果个人引用问题原因推测 圆形鼠标绘制代码运行结果 课程完结&#xff1a; 前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原因无非是想换个工作。相关的教学视…

CUDA驱动深度学习发展 - 技术全解与实战

全面介绍CUDA与pytorch cuda实战 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认证的资深架构师&#xff0c;项目管理专业人士&…

Flink实时电商数仓之DWS层

需求分析 关键词 统计关键词出现的频率 IK分词 进行分词需要引入IK分词器&#xff0c;使用它时需要引入相关的依赖。它能够将搜索的关键字按照日常的使用习惯进行拆分。比如将苹果iphone 手机&#xff0c;拆分为苹果&#xff0c;iphone, 手机。 <dependency><grou…

【IoT网络层】STM32 + ESP8266 +MQTT + 阿里云物联网平台 |开源,附资料|

目标&#xff1a;实现STM32连接阿里云物联网平台发送数据同时接收数据&#xff0c;IOT studio界面显示数据。具体来说&#xff1a;使用ESP8266 ESP-01来连接网络&#xff0c;获取设备数据发送到阿里云物联网平台并显示且oled显示屏当前的设备数据&#xff0c;通过IOT studio界面…