RocketMQ如何保证消息的可靠性传递❓

news2024/10/7 17:23:21

RocketMQ 通过一系列的机制来保证消息的可靠性传递,确保在面对各种异常和故障情况时,消息系统能够稳定地处理和传递消息。以下是 RocketMQ 保证可靠性传递的关键机制:

1. 同步双写机制 (Synchronous Write Mechanism):

RocketMQ的同步双写机制通过操作系统的页缓存、同步刷盘和CommitLog的持久化操作,为消息的可靠性传递提供了坚实的基础。在生产环境中,根据实际需求可以适度调整相关配置,以平衡性能和可靠性。

1.1 同步双写机制工作流程:

  1. 消息生产:当生产者发送消息到RocketMQ时,消息首先被写入操作系统的页缓存。
  2. 同步刷盘(Sync Flush):RocketMQ会在消息写入页缓存后进行同步刷盘操作。这意味着将消息从页缓存刷写到磁盘的存储介质,确保消息已经被持久化。
  3. CommitLog持久化:一旦消息成功同步刷盘,RocketMQ将消息写入 CommitLog。CommitLog是消息的物理存储,包含了所有已发送的消息。
  4. 返回成功响应:一旦消息被成功写入CommitLog,生产者收到成功响应,可以认为消息已经被成功持久化。

1.2 同步双写机制流程:

  • 消息发送:
    • 生产者将消息发送到 RocketMQ 主节点。
  • 主节点写入消息存储文件(Master CommitLog):
    • 主节点负责将消息写入自身的 CommitLog 文件,保证消息在主节点的存储文件中持久化。
  • 主节点同步消息到从节点:
    • 主节点将刚刚写入的消息同步到所有配置的从节点。
    • 同步可以采用不同的方式,如同步、异步或半同步,这取决于 RocketMQ 的配置。
  • 从节点写入消息存储文件(Slave CommitLog):
    • 从节点接收到主节点的同步消息后,将消息写入自身的 CommitLog 文件。
  • 从节点确认消息同步完成:
    • 从节点向主节点发送确认消息已成功同步的信号。
    • 主节点收到所有从节点的确认后,认为消息在从节点的同步操作已经完成。
  • 消息标记为可投递:
    • 一旦消息在主节点和所有从节点的 CommitLog 文件中都成功写入,消息被标记为可投递状态。
    • 这意味着消息已经在多个节点上得到了持久化。
  • 消息可供消费者消费:
    • 消费者可以消费已被确认写入的消息,保证消息在多节点之间的同步。
1.2.2 流程图:

1.2 关键概念:

  1. 页缓存(Page Cache):操作系统中用于存储文件系统缓存的内存区域。RocketMQ通过将消息首先写入页缓存,实现了消息在内存中的持久化。
  2. 同步刷盘(Sync Flush):是指将内存中的数据同步刷写到磁盘。RocketMQ确保消息在被发送后,首先在内存中得到持久化,然后再刷写到磁盘,从而防止数据的丢失。
  3. CommitLog:是RocketMQ中消息的物理存储结构,包含了所有已发送的消息。CommitLog的持久化保证了即使在异常情况下,如Broker宕机,消息也能够被恢复。

1.3 为什么使用同步双写机制?

  1. 提高持久化可靠性:同步双写机制确保了消息在写入磁盘之前已经在内存中得到持久化,从而提高了消息的可靠性。
  2. 防止数据丢失:在异常情况下,如机器宕机,通过同步刷盘机制可以避免数据在写入磁盘之前丢失。
  3. 加速消息的发送速度:同步双写机制实际上将消息发送和持久化的操作异步化,提高了消息发送的吞吐量。

1.4 配置同步双写机制:

在RocketMQ的配置文件中,可以通过配置 isolateDiskReadAndWrite 参数为 true 来启用同步双写机制。该配置项在broker.conf中配置:

# broker配置文件中启用同步双写机制的配置
isolateDiskReadAndWrite=true

这样的配置将确保消息的同步刷盘机制被启用,提高了消息持久化的可靠性。

2. 主从复制机制 (Master-Slave Replication):

RocketMQ采用主从复制机制来提高系统的可用性。每个Broker都有一个主Broker和若干个从Broker。主Broker负责消息的读写,而从Broker用于备份和容灾。如果主Broker发生故障,RocketMQ能够迅速切换到从Broker以提供服务,确保消息的持久化和传递。

2.1 主从复制的工作原理:

  1. 角色定义: 在 RocketMQ 的 Broker 集群中,每个 Broker 都有主 Broker 和若干个从 Broker。主 Broker 负责消息的读写,而从 Broker 用于备份和容灾。
  2. 主节点写入消息: 当生产者发送消息时,消息首先被写入主节点的 CommitLog 中。
  3. 同步复制到从节点: 主节点将写入的消息同步复制到所有从节点。这确保了从节点具有与主节点相同的消息副本。
  4. 从节点备份: 从节点保存了主节点的消息备份,以备主节点宕机或发生故障时使用。
  5. 切换到从节点: 当主节点发生故障或宕机时,RocketMQ 能够快速切换到某个从节点,并确保消息服务的持续性。切换后,从节点成为新的主节点。

2.2 主从复制的配置:

在 RocketMQ 的配置文件中,可以通过配置 brokerRole 参数来指定 Broker 的角色,即是主节点还是从节点。以下是一个简化的配置示例:

# broker配置文件中的master配置
brokerClusterName=MyBrokerCluster
brokerName=broker-a
brokerId=0
listenPort=10911
namesrvAddr=localhost:9876
brokerRole=ASYNC_MASTER
# broker配置文件中的slave配置
brokerClusterName=MyBrokerCluster
brokerName=broker-a
brokerId=1
listenPort=10912
namesrvAddr=localhost:9876
brokerRole=SLAVE

在这个配置中,brokerRole 参数指定了 Broker 的角色,主节点使用 ASYNC_MASTER,从节点使用 SLAVE。

2.3 关键概念:

  • 主 Broker(Master Broker): 负责消息的读写和写入 CommitLog。
  • 从 Broker(Slave Broker): 用于备份主 Broker 的消息,确保在主 Broker 故障时可以顺利切换。
  • 同步复制: 主节点将消息同步复制到所有从节点,确保从节点具有相同的消息副本。
  • 切换(Failover): 在主节点发生故障时,从节点可以快速切换为新的主节点,确保消息服务的持续性。

2.4 为什么使用主从复制机制?

  1. 提高可用性: 主从复制机制提高了系统的可用性,即使主节点发生故障,从节点可以迅速切换为主节点,保障消息服务的持续性。
  2. 容灾备份: 从节点充当主节点的备份,确保在主节点宕机或发生故障时,仍能够提供服务。
  3. 负载均衡: 主从复制机制支持在多个节点之间分摊消息读写的负载,提高系统的整体性能。

2.5 配置主从复制机制:

在配置文件中通过设置 brokerRole 参数,可以指定 Broker 的角色。主节点使用 ASYNC_MASTER,从节点使用 SLAVE。通过合理配置主从节点的数量和位置,可以优化系统的性能和可靠性。

3. 消息拉取和消费确认机制 (Message Pull and Consumer Acknowledgment):

消费者在拉取消息时,RocketMQ记录每条消息的消费状态。消费者在处理完消息后,会向Broker提交消息的消费确认。Broker将这些消费确认的信息持久化到CommitLog中。这确保了消息在被成功消费后会被正确地持久化,而且在消费者或者Broker出现问题时,能够根据这些记录进行恢复。

3.1 消息拉取机制:

在 RocketMQ 中,消费者通过拉取(Pull)的方式获取消息。这种机制相对于推送(Push)的方式更为灵活,允许消费者按照自己的速度主动拉取消息。消息拉取的流程如下:

  1. 消费者向 Broker 发起拉取消息的请求。
  2. Broker 返回一批消息给消费者。
  3. 消费者处理拉取到的消息。

3.2 消费确认机制:

在 RocketMQ 中,消费者在成功消费一条消息后,需要向 Broker 发送消费确认。这个机制确保了消息被成功消费,并避免消息在消费者宕机或发生其他故障时被重复消费。消费确认的流程如下:

  1. 消费者成功处理一条消息后,向 Broker 发送消费确认。
  2. Broker 接收到消费确认后,将消息的消费状态记录在 CommitLog 中。
  3. 消费者定期向 Broker 发送消费进度,以便 Broker 知晓消费的最新状态。

3.3 消费确认的两种模式:

RocketMQ 提供了两种消费确认模式:

  1. 自动确认(Auto Acknowledge): 消费者在成功消费消息后,会自动向 Broker 发送消费确认,无需手动处理。适用于那些不需要精确控制消费确认的场景。
  2. 手动确认(Manual Acknowledge): 消费者需要手动调用确认接口,显式地告知 Broker 消息已经被成功消费。这种模式更为灵活,适用于需要精确控制消费确认的场景,例如在事务消息中。

3.4 防止消息重复消费:

RocketMQ 通过消费确认机制防止消息重复消费。即使在消息推送和拉取的情况下,一旦消息被成功消费,消费者向 Broker 发送确认后,Broker 将记录消息的消费状态,防止消息被重复消费。

3.5 代码示例(Java):

// RocketMQ消费者示例代码
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("example_topic", "*");

consumer.registerMessageListener((List<MessageExt> messages, ConsumeConcurrentlyContext context) -> {
    for (MessageExt message : messages) {
        // 处理消息的业务逻辑

        // 手动确认消息已经被消费
        consumer.acknowledge(message.getMsgId());
    }
    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});

consumer.start();
System.out.println("Consumer Started.");

在这个示例中,消费者注册了消息监听器,对拉取到的消息进行处理,并使用 consumer.acknowledge() 手动确认消息的消费。这种手动确认的方式可以确保消息在被成功处理后才发送确认,防止消息重复消费。

3.6 消息拉取和消费确认机制流程:

3.6.1 流程说明:

1)消费者订阅主题和标签:

消费者通过调用 subscribe 方法订阅对应的主题和标签。

调用的组件:DefaultMQPushConsumer(消费者实例)

源码示例:

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("topic", "tag");

2)拉取消息:

消费者通过调用 poll 或者注册消息监听器,并启动消息消费线程。

消费者主动向 RocketMQ 服务器发起拉取消息的请求。

调用的组件:MQClientAPIImpl(RocketMQ 客户端 API 实现)

源码示例:

PullResult pullResult = pullMessageSync(queue, subExpression, expressionType,
    nextOffset, pullBatchSize, sysFlag, commitOffset, brokerAddr);

3)服务器返回消息:

RocketMQ 服务器收到拉取消息请求后,从消息队列中获取消息,并将消息返回给消费者。

调用的组件:MessageQueue(消息队列)

源码示例:

MessageExt msg = queueMessageList.get(i);

4)消费者处理消息:

消费者收到消息后,执行消息的业务处理逻辑。

消费者可以在业务逻辑中判断消息是否处理成功。

5)确认消息消费:

如果消息处理成功,消费者调用 acknowledge 方法确认消息已被成功消费。

如果消息处理失败,消费者可以选择不确认消息,以触发消息的重新投递。

调用的组件:ConsumeMessageService(消息消费服务)

源码示例:

this.consumeMessageService.submitConsumeRequest(
    msgs,
    processQueue,
    messageQueue,
    dispathToConsume);

6)服务器更新消费进度:

消费者确认消息后,RocketMQ 服务器更新消费者的消费进度。

消费进度用于记录消费者在消息队列中的消费位置,以便下次拉取消息时从正确的位置开始。

调用的组件:ConsumerOffsetManager(消费者位移管理器)

源码示例:

this.consumerOffsetManager.commitOffset(
    consumerGroup,
    clientid,
    messageQueue,
    offset);

7)消费者定时发送心跳:

消费者定时向 RocketMQ 服务器发送心跳,以保持与服务器的连接。

调用的组件:HeartbeatProducer(心跳生产者)

源码示例:

this.heartbeatProducer.sendHeartbeatToAllBrokerWithLock();

8)消息拉取轮询:

消费者定期发起消息拉取请求,获取新的消息。

消费者拉取的频率可以根据业务需求和性能调优进行配置。

调用的组件:DefaultMQPushConsumerImpl(消费者实现)

源码示例:

this.mQClientAPIImpl.pullMessage(
    brokerAddr,
    requestHeader,
    timeoutMillis,
    communicationMode,
    pullCallback);

9)重复以上流程:

消费者将不断重复以上流程,持续从 RocketMQ 服务器拉取消息并进行消费。

整个流程是在 DefaultMQPushConsumer 内部进行的,包含了对 MQClientAPIImplConsumeMessageServiceConsumerOffsetManager 等多个组件的调用

3.7 为什么使用消息拉取和消费确认机制?

  1. 灵活性: 消息拉取机制使得消费者可以按照自己的速度主动拉取消息,适用于不同业务场景的需求。
  2. 可靠性: 消费确认机制确保了消息被成功消费后才进行确认,防止消息在异常情况下被重复消费。
  3. 控制权: 消费者可以通过手动确认的方式更加灵活地控制消息的消费进度,适用于需要更细粒度控制的场景。

4. 消息的重新投递机制 (Message Redelivery Mechanism):

RocketMQ支持消息的重新投递机制。当消息发送失败或者消费失败时,RocketMQ会根据预设的重试策略将消息重新投递给消费者。这包括了设置最大重试次数和重试间隔时间,以确保消息在遇到短暂故障后能够尽快被重新处理。

4.1 工作原理:

  1. 消息发送失败: 当消息发送到 Broker 时,如果发生发送失败,RocketMQ 将对该消息进行重新投递。
  2. 消息消费失败: 当消费者处理消息时发生失败,消费者可以选择将消息标记为重新投递。这通常发生在消息处理的业务逻辑出现异常或者无法处理当前消息的情况下。
  3. 重试次数控制: RocketMQ 允许设置消息的最大重试次数,即消息最多被重新投递的次数。超过重试次数后,消息将被标记为死信消息,不再重新投递。
  4. 重试间隔控制: 为了避免短时间内频繁地重试,RocketMQ 支持设置消息的重试间隔时间。在每次重新投递时,消息将等待一定的时间再次被尝试。

4.2 代码示例(Java):

生产者端:
DefaultMQProducer producer = new DefaultMQProducer("example_group");
producer.setNamesrvAddr("localhost:9876");
producer.start();

Message message = new Message("example_topic", "TagA", "Hello, RocketMQ".getBytes());

// 发送消息,并指定最大重试次数和重试间隔时间
SendResult sendResult = producer.send(message, (m, ex, retryTimes) -> {
    if (retryTimes < 2) { // 最多重试两次
        // 返回重新投递的策略
        return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
    } else {
        // 不再重试,消息将被标记为死信消息
        return ConsumeOrderlyStatus.SUCCESS;
    }
}, 3, 3000);

System.out.println("Message ID: " + sendResult.getMsgId());

producer.shutdown();
消费者端:
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("example_topic", "*");

consumer.registerMessageListener((List<MessageExt> messages, ConsumeConcurrentlyContext context) -> {
    for (MessageExt message : messages) {
        try {
            // 处理消息的业务逻辑
            // 如果处理失败,可以选择重新投递消息
            if (someBusinessLogic(message)) {
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            } else {
                // 返回重新投递的策略,RocketMQ 将根据配置进行重试
                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
        } catch (Exception e) {
            // 异常情况也可以选择重新投递
            return ConsumeConcurrentlyStatus.RECONSUME_LATER;
        }
    }
    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});

consumer.start();
System.out.println("Consumer Started.");

在这个示例中,生产者通过 producer.send() 方法发送消息,并通过 SendCallback 设置了消息发送失败后的处理逻辑。消费者在 MessageListener 中处理消息的业务逻辑,并根据处理结果返回不同的消费状态,以决定是否重新投递消息。

4.3 为什么使用消息的重新投递机制?

  1. 处理瞬时故障: 在分布式系统中,瞬时故障是难以避免的。可能由于网络抖动、服务暂时不可用等原因导致消息发送失败或者消费失败。重新投递机制能够帮助系统在这些短暂的故障发生后,通过重新尝试来最终成功地将消息发送或者处理。
  2. 提高消息处理成功率: 在一些场景下,消息的处理可能涉及到复杂的业务逻辑、依赖外部服务,或者受到临时资源限制等。第一次处理时可能由于各种原因失败,但通过重新投递机制,系统有机会在稍后重新尝试,从而提高消息的最终成功率。
  3. 应对消费端问题: 消费端可能由于程序bug、处理逻辑错误或者依赖服务故障等原因导致消息处理失败。通过重新投递,系统有机会在修复问题后重新处理这些消息。
  4. 系统的自愈能力: 重新投递机制使得系统在遇到短期故障或处理失败的情况下,能够自动尝试修复问题,降低了对人工干预的依赖,提高了系统的自愈能力。
  5. 降低手动处理成本: 对于某些失败的消息,人工介入可能是昂贵的,尤其在大规模系统中。通过自动的重新投递机制,系统能够更加高效地处理这些失败的消息,降低了手动处理的成本。
  6. 避免消息丢失: 在一些异常情况下,如网络分区、服务器故障等,消息可能在发送或者消费的过程中丢失。通过重新投递机制,系统有机会在这些异常恢复后重新处理消息,避免消息的永久丢失。

5. 事务消息机制 (Transactional Message Mechanism):

RocketMQ提供了事务消息的支持,用于处理分布式事务。在事务消息中,生产者发送半消息(half message),然后等待事务的执行结果。最终,根据事务的结果,生产者提交或回滚这条消息。这个机制保证了在分布式事务场景中消息的一致性。

5.1 工作原理:

  1. 事务消息发送: 事务消息发送分为两个阶段。首先,生产者发送半消息(Half Message),该消息处于预提交状态,但尚未提交到 CommitLog 中。
  2. 本地事务执行: 生产者在发送半消息后,会执行本地事务。本地事务可能成功、失败或者状态不确定。
  3. 事务消息状态回查: RocketMQ 定期进行事务消息的状态回查。对于处于预提交状态的半消息,RocketMQ 会向生产者发送回查请求。
  4. 生产者处理回查请求: 生产者收到回查请求后,需要根据本地事务的执行结果来确定如何处理该消息。如果本地事务成功,则提交该消息;如果本地事务失败,则回滚该消息。
  5. 消息提交或回滚: 在回查后,生产者根据本地事务的执行结果提交或回滚事务消息。如果提交,消息变为可投递状态;如果回滚,消息将被删除。
  6. 消息可投递: 一旦事务消息提交,RocketMQ 将允许该消息被消费者消费。

5.2 事务消息机制流程:

5.2.1 流程描述:
  1. 半消息发送: 生产者发送半消息(Half Message)到 RocketMQ 服务器。
  2. 消息状态为 Prepared: 半消息的状态被设置为 "Prepared",表示消息处于预提交状态。
  3. 本地事务执行: 生产者执行本地事务逻辑,可能是数据库操作、文件写入等。
  4. 事务消息状态回查: RocketMQ 定期进行事务消息的状态回查,向生产者询问半消息的本地事务执行结果。
  5. 生产者处理回查请求: 生产者根据本地事务的执行结果,可能是提交、回滚或保持不变,返回相应的事务状态。
  6. 消息提交或回滚: 如果本地事务成功,生产者将消息状态设置为 "Commit",表示提交;如果本地事务失败,生产者将消息状态设置为 "Rollback",表示回滚。
  7. 消息可投递或删除: 如果消息状态为 "Commit",RocketMQ 允许该消息被消费者消费;如果消息状态为 "Rollback",该消息将被删除。

5.2.2 流程图:

5.3 代码示例(Java):

事务消息生产者:
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_group");
producer.setNamesrvAddr("localhost:9876");

// 设置事务监听器
producer.setTransactionListener(new TransactionListener() {
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        // 执行本地事务逻辑,返回事务执行状态
        // 可能是 COMMIT_MESSAGE、ROLLBACK_MESSAGE、UNKNOW
        // 具体返回值要根据本地事务的执行结果来确定
    }

    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        // 回查本地事务状态,返回事务执行状态
        // 可能是 COMMIT_MESSAGE、ROLLBACK_MESSAGE、UNKNOW
        // 具体返回值要根据本地事务的执行结果来确定
    }
});

producer.start();

// 发送事务消息
Message message = new Message("transaction_topic", "TagA", "Hello, RocketMQ".getBytes());
SendResult sendResult = producer.sendMessageInTransaction(message, null);

System.out.println("Transaction Send Result: " + sendResult.getSendStatus());

producer.shutdown();
事务消息消费者:
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction_consumer_group");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("transaction_topic", "*");

consumer.registerMessageListener((List<MessageExt> messages, ConsumeConcurrentlyContext context) -> {
    for (MessageExt message : messages) {
        // 处理事务消息的业务逻辑
        // 注意:由于事务消息的状态可能是 UNKNOW,所以需要确保消息的幂等性
    }
    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});

consumer.start();
System.out.println("Transaction Consumer Started.");

5.4 为什么使用事务消息机制?

  1. 确保消息的一致性: 事务消息机制允许生产者和消费者参与到分布式事务中,确保消息在发送和消费的过程中的一致性。
  2. 处理本地事务: 生产者可以在发送半消息后执行本地事务。如果本地事务成功,生产者提交该消息;如果本地事务失败,生产者回滚该消息。
  3. 状态回查: RocketMQ 定期回查半消息的状态,以确保在网络分区、节点故障等情况下,能够最终处理事务消息的状态。
  4. 消息的幂等性: 由于事务消息可能经历多次状态回查,消费者需要确保消息的处理是幂等的,以避免因为重复处理消息而引发的问题。
  5. 消息的可靠性: 事务消息机制提供了一种可靠的消息传递方式,特别适用于要求事务一致性的场景,如订单支付、库存扣减等。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

oracle 拼接语句怎么写?

||的妙用&#xff0c;字符串和变量列名之间都得用||分隔&#xff0c;oracle等数据库两个单引号输出一个单引号&#xff0c;因为如果只写了一个的话 他会和最近的单引号被数据库认为是组成了一个空字符串&#xff0c;因此需要用两个单引号来表示这是个单引号 查询列间用&#xf…

锂电池基础知识及管理方式总结

这两天在排查一个锂电池无法充电的问题&#xff0c;用的是电池管理芯片BQ25713&#xff0c;网上相关的资料也很少&#xff0c;查看数据手册时&#xff0c;里面也有很多术语参数等不是很理解&#xff0c;所以&#xff0c;在此对锂电池的基础知识做个简单的总结&#xff0c;方面后…

算法:有效的括号(入栈出栈)

时间复杂度 O(n) 空间复杂度 O(n∣Σ∣)&#xff0c;其中 Σ 表示字符集&#xff0c;本题中字符串只包含 6 种括号 /*** param {string} s* return {boolean}*/ var isValid function(s) {const map {"(":")","{":"}","["…

vue3若依框架,在页面中点击新增按钮跳转到新的页面,不是弹框,如何实现

在router文件中的动态路由数组中新增一个路由配置&#xff0c;这个配置的就是新的页面。 注意path不要和菜单配置中的路径一样&#xff0c;会不显示内容。 在菜单配置中要写权限标识就是permissions:[]里的内容 在children里的path要写占位符info/:data 点击新增按钮&#x…

初级数据结构(三)——栈

文中代码源文件已上传&#xff1a;数据结构源码 <-上一篇 初级数据结构&#xff08;二&#xff09;——链表 | 初级数据结构&#xff08;四&#xff09;——队列 下一篇-> 1、栈的特性 1.1、函数栈帧简述 即使是刚入门几天的小白&#xff0c;对栈这个字…

一文读懂NISQ时代的量子竞赛

/目录/ 一、关于NISQ&#xff0c;业内专家怎么看&#xff1f; 二、NISQ时代的量子硬件资源估算 2.1. NISQ量子比特要求 2.2. NISQ计算时间 2.3. NISQ代码经典模拟 三、主流量子算法“优势”的资源估算 3.1. 用于化学模拟的VQE算法 3.2. 用于组合优化的QAOA算法 3.3. 量…

你真的了解Shiro框架吗?

关注公众号回复20231110获取最新网络安全以及内网渗透等资料。 文章目录 关注公众号回复20231110获取最新网络安全以及内网渗透等资料。Shiro的核心架构Shiro中的认证认证shiro中认证的关键对象 认证流程调试认证流程Shiro的加密过程Shiro中的解密过程总结 Shiro的核心架构 Shi…

低代码开发与传统软件开发:未来趋势与竞争格局

近年来&#xff0c;低代码开发平台的快速发展引起了各行各业的广泛关注。低代码开发平台简化了软件开发的复杂性&#xff0c;提供了更快速、更灵活的开发方式。于是&#xff0c;许多人开始产生一个疑问&#xff1a;未来低代码开发是否会取代传统软件开发&#xff1f;今天这篇文…

验收支撑-软件项目验收计划书

软件项目验收计划的作用主要有以下几点&#xff1a; 确保项目质量&#xff1a;通过项目验收&#xff0c;客户或相关方可以对项目的成果进行全面、系统的评估&#xff0c;以确保项目达到预期的质量标准。发现和解决问题&#xff1a;在项目开发过程中&#xff0c;难免会存在一些问…

ROS-ROS运行管理-ROS元功能包

ROS是多进程(节点)的分布式框架&#xff0c;一个完整的ROS系统实现&#xff1a; 可能包含多台主机&#xff1b;每台主机上又有多个工作空间(workspace)&#xff1b;每个的工作空间中又包含多个功能包(package)&#xff1b;每个功能包又包含多个节点(Node)&#xff0c;不同的节…

(Nerf学习)GaussianEditor

论文链接 https://arxiv.org/pdf/2311.14521.pdf 原码链接 https://github.com/buaacyw/GaussianEditor 一、安装&#xff08;WIN失败&#xff0c;求解决方法&#xff09; 我使用的环境是&#xff1a;Win11 python3.8 CUDA11.8 显卡3060 1、克隆我们的存储库并创建 conda …

【INTEL(ALTERA)】Agilex7 FPGA Development Kit DK-DK-DEV-AGI027RBES 编程/烧录/烧写/下载步骤

DK-DEV-AGI027RBES 的编程步骤&#xff1a; 将 USB 电缆插入 USB 端口 J8&#xff08;使用 J10 时&#xff0c;DIPSWITCH SW5.3&#xff08;DK-DEV-AGI027RES 和 DK-DEV-AGI027R1BES&#xff09;和 SW8.3&#xff08;DK-DEV-AGI027RB 和 DK-DEV-AGI027-RA&#xff09;应关闭&a…

【动态规划】路径问题_不同路径_C++

题目链接&#xff1a;leetcode不同路径 目录 题目解析&#xff1a; 算法原理 1.状态表示 2.状态转移方程 3.初始化 4.填表顺序 5.返回值 编写代码 题目解析&#xff1a; 题目让我们求总共有多少条不同的路径可到达右下角&#xff1b; 由题可得&#xff1a; 机器人位于…

蚂蚁SEO实用的网络baidu蜘蛛有哪些

网络蜘蛛是一种用于从互联网上自动抓取信息的程序。它们根据给定的规则和指令&#xff0c;遍历网站上的页面&#xff0c;收集信息并将其存储在数据库中。网络蜘蛛在搜索引擎、数据挖掘、信息提取等领域有着广泛的应用。本文将介绍一种实用的网络蜘蛛&#xff0c;并探讨其实现原…

iOS加密CoreML模型

生成模型加密密钥 必须在Xcode的Preferences的Accounts页面登录Apple ID&#xff0c;才能在Xcode中生成模型加密密钥。 在Xcode中打开模型&#xff0c;单击Utilities选项卡&#xff0c;然后单击“Create Encryption Key”按钮。 从下拉菜单中选择当前App的Personal Team&…

【抽象责任链模式】实践优化

责任链模式 原文来自 ——> https://nageoffer.com/pages/51ffef/#%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F &#xff08;小调整重点标注&#xff0c;我是菜鸡&#xff09; 1. 什么是责任链 责任链设计模式是一种行为型设计模式&#xff0c;其主要目的是解耦请求发送…

mongoAltas使用

创建项目 https://cloud.mongodb.com/v2#/org/6548f5a62d5ab00f5b67a61a/projects 部署数据库 选择厂商部署 更改数据库用户和密码 添加数据库可访问地址 添加连接信息到vscode MONGO_URLmongodbsrv://burnchi:burnchicluster0.ynoq32i.mongodb.net/Auth-Mongodb偶尔分享web开…

mysql的redolog、undo、binlog的作用

概览&#xff1a; MySQL三大日志包括&#xff1a;undolog&#xff0c;redo log&#xff0c;binlog&#xff0c;它们分别有以下作用&#xff1a; undolog&#xff1a;是Innodb存储引擎事务生成的日志。用于事务的回滚和MVCC&#xff0c;保证了事务的原子性。 redo log&#x…

H5页面生成工具源码

源码介绍 H5是基于Vue2.0开发的&#xff0c;通过拖拽的形式&#xff0c;生成页面的工具&#xff0c;类似易企秀、百度H5等工具。 H5特征&#xff1a; 1、编辑器 参考线 吸附线、组件对齐 拽改变组件形状 元素: 复制&#xff08;画布&#xff09; 元素: 删除&#xff08…

Fractal-Streets

title: Fractal Streets date: 2023-12-13 14:48:45 tags: 分形 categories: 算法进阶指南 题目大意 将原来的城市复制一遍放在原城市的上方&#xff0c;将原城市顺时针90放在原城市的左上方&#xff0c;将逆时针90后的城市放在原城市的左边&#xff0c;然后用道路将四部分链接…