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);
    
        @Override
        public 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);

      @Override
      public 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,订阅一波不再迷路

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【面试】面向对象编程的三大概念(实例辅助记忆)

【面试】面向对象编程的三大概念&#xff08;实例辅助记忆&#xff09; 虑面向对象编程的三大特性&#xff0c;它们是&#xff1a; 封装&#xff08;Encapsulation&#xff09;&#xff1a; 将对象的状态和行为封装在一起&#xff0c;对外部隐藏对象的内部实现细节。这样可以防…

Nginx 简介和入门 - part1

虽然作为1个后端程序员&#xff0c; 终究避不开这东西 安装Nginx 本人的测试服务器是debian &#xff0c; 安装过程跟ubuntu基本一样 sudo apt-get install nginx问题是 nginx 安装后 执行文件在/usr/sbin 而不是/usr/bin 所以正常下普通用户是无法使用的。 必须切换到root…

事件循环的理解

1.单线程 Js是一个单线程的语言,代码只能一行一行去执行,遇到同步的代码就直接执行了,如果遇到异步的代码怎么办&#xff1f; 不可能等到异步的代码执行完&#xff0c;在去执行后面同步的代码。 2.主线程 遇到同步的代码,就在主线程里面直接执行了。 3.任务队列 遇到异步的…

数位dp详解,记忆化搜索,递推,OJ精讲

文章目录 前言引例-不降数前置知识差分转换枚举技巧前缀状态 状态分析状态设计状态转移初始状态记忆化搜索 引例代码实现状态初始化数位初始化记忆化搜索 非递归如何实现&#xff1f;状态设计状态转移算法原理算法实现初始化递推求解 OJ精讲Good Numbers不要62不含连续1的非负整…

redis 从0到1完整学习 (十四):RedisObject 之 ZSet 类型

文章目录 1. 引言2. redis 源码下载3. redisObject 管理 ZSet 类型的数据结构4. 参考 1. 引言 前情提要&#xff1a; 《redis 从0到1完整学习 &#xff08;一&#xff09;&#xff1a;安装&初识 redis》 《redis 从0到1完整学习 &#xff08;二&#xff09;&#xff1a;re…

【基础】【Python网络爬虫】【1.认识爬虫】什么是爬虫,爬虫分类,爬虫可以做什么

Python网络爬虫基础 认识爬虫1.什么是爬虫2.爬虫可以做什么3.为什么用 Ptyhon 爬虫4.爬虫的分类通用爬虫聚焦爬虫功能爬虫增量式爬虫分布式爬虫 5.爬虫的矛与盾&#xff08;重点&#xff09;6.盗亦有道的君子协议robots7.爬虫合法性探究 认识爬虫 1.什么是爬虫 网络爬虫&…

LAYABOX:2024新年寄语

2024新年寄语 过去的一年&#xff0c;尽管许多行业面临严峻挑战和发展压力&#xff0c;小游戏领域却逆势上扬&#xff0c;年产值首次突破400亿元大关&#xff0c;众多优质小游戏企业收获颇丰。 对此&#xff0c;祝福大家&#xff0c;2024一定更好&#xff01; 过去的一年&#…

基于低代码的指尖遐想_2

广义低代码解决了企业或个人的哪些问题&#xff0c;其快速发展的背后说明了什么&#xff1f; 基于一个简要的企业信息化系统来分析阐述&#xff08;天下大事合久必分&#xff0c;分久必合&#xff09;&#xff1a; 2010年前后&#xff0c;一个合格的程序员&#xff0c;可以做需…

YOLOv8改进 | 2023主干篇 | FasterNeT跑起来的主干网络( 提高FPS和检测效率)

一、本文介绍 本文给大家带来的改进机制是FasterNet网络&#xff0c;将其用来替换我们的特征提取网络&#xff0c;其旨在提高计算速度而不牺牲准确性&#xff0c;特别是在视觉任务中。它通过一种称为部分卷积&#xff08;PConv&#xff09;的新技术来减少冗余计算和内存访问。…

系统学习Python——装饰器:函数装饰器-[对方法进行装饰:使用嵌套函数装饰方法]

分类目录&#xff1a;《系统学习Python》总目录 如果想要函数装饰器在简单函数和类级别的方法上都能工作&#xff0c;最直接的解决办法在于使用前面文章介绍的状态保持方案之一&#xff1a;把自己的函数装饰器编写为嵌套的def&#xff0c;这样你就不会陷入单一的self实例参数既…

听GPT 讲Rust源代码--library/portable-simd

File: rust/library/portable-simd/crates/core_simd/examples/spectral_norm.rs spectral_norm.rs是一个示例程序&#xff0c;它展示了如何使用Portable SIMD库中的SIMD&#xff08;Single Instruction Multiple Data&#xff09;功能来实现频谱规范化算法。该示例程序是Rust源…

仿网易云音乐网站PHP源码,可运营的原创音乐分享平台源码,在线音乐库系统

源码介绍 使用PHP和MYSQL开发的原创音乐分享平台源码&#xff0c;仿网易云音乐网站。用户可以在网站上注册并上传自己的音乐作品&#xff0c;系统内置广告系统&#xff0c;为网站创造收入来源。 安装教程 1.导入sql.sql 2.修改 includes\config.php 数据库信息和网址都改成…

3、事务与持久化

目录 1、事务 2、持久化 1. RDB 2.AOF 3.Redis的主从架构 4、哨兵 5、Redis的集群 1、事务 Redis事务&#xff1a;一次失误操作&#xff0c;该成功的成功&#xff0c;该失败的失败。 先开启事务&#xff0c;执行一些列的命令&#xff0c;但是命令不会立即执行&#xf…

第9章 继承和派生习题(详解)

一、选择题 1&#xff0e;下列表示引用的方法中&#xff0c; &#xff08;&#xff09; 是正确的。已知&#xff1a;int m10&#xff1a; A&#xff0e;int &xm&#xff1b; B&#xff0e;int &y10&#xff1b; C&#xff0e;int &z&#xff1b; D&#xff0e;fl…

双指针——移动零

题目 示例 算法原理 我们使用两个指针&#xff0c;cur扫描数组&#xff0c;如果nums[cur]为非0&#xff0c;dest&#xff0c;然后让nums[cur]与nums[dest]交换&#xff0c;从而实区间[0,dest]为非0,[dest1,cur]为0&#xff0c;[cur,numsSize-1]为未扫描 题目链接&#xff1a;28…

1.1 理解大数据(2)

小肥柴的Hadoop之旅 1.1 理解大数据&#xff08;2&#xff09; 目录1.1 理解大数据1.1.3 大数据概述1.1.4 更多思考 参考文献和资料 目录 1.1 理解大数据 1.1.3 大数据概述 step_0 大数据定义 【《大数据算法设计分析》】&#xff1a; 通常来讲大数据&#xff08;Big Data&am…

pyqt5用qtdesign设计页面时,去掉页面的空白界面、边框和标题栏

前言 Windows默认的标题栏有时候自己觉得不太美观&#xff0c;就想自己设计一个&#xff0c;然后把默认的去掉&#xff0c;并且把长方形的边框和多余的空表界面去掉&#xff0c;就是下图中圈出来的区域&#xff1a; 去掉之后的效果如图&#xff1a; 这样我们就可以自定义窗…

Spring Boot 3 集成 Jasypt详解

随着信息安全的日益受到重视&#xff0c;加密敏感数据在应用程序中变得越来越重要。Jasypt&#xff08;Java Simplified Encryption&#xff09;作为一个简化Java应用程序中数据加密的工具&#xff0c;为开发者提供了一种便捷而灵活的加密解决方案。本文将深入解析Jasypt的工作…

57.网游逆向分析与插件开发-游戏增加自动化助手接口-接管游戏的自动药水设定功能

内容来源于&#xff1a;易道云信息技术研究院VIP课 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;51307d6bf69f2f3c645c70d09f841f5e32da79b9 代码下载地址&#xff0c;在 SRO_EX 目录下&…

MySQL 8.0 InnoDB Tablespaces之Undo Tablespaces(UNDO表空间)

文章目录 MySQL 8.0 InnoDB Tablespaces之Undo Tablespaces&#xff08;UNDO表空间&#xff09;Undo Tablespaces&#xff08;UNDO表空间&#xff09;默认UNDO表空间添加 Undo 表空间查看Undo 相关的信息查看Undo 相关参数变量查看Undo 状态信息通过information_schema.innodb_…