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

RocketMQ 发送顺序消息

文章目录

  • 顺序消息应用场景
  • 消息组(MessageGroup)
  • 顺序性
    • 生产的顺序性
    • MQ 存储的顺序性
    • 消费的顺序性
  • rocketmq-client-java 示例(gRPC 协议)
    • 1. 创建 FIFO 主题
    • 生产者代码
    • 消费者代码
    • 解决办法
    • 解决后执行结果
  • rocketmq-client 示例(Remoting 协议)
    • 生产者
      • MessageQueueSelector 详解
    • 消费者

顺序消息应用场景

在有序事件处理、撮合交易、数据实时增量同步等场景下,异构系统间需要维持强一致的状态同步,上游的事件变更需要按照顺序传递到下游进行处理。在这类场景下使用 RocketMQ 的顺序消息可以有效保证数据传输的顺序性。比如:同一个用户的操作,一定是先生成订单、再进行支付、扣减库存、生成物流信息等。

消息组(MessageGroup)

RocketMQ 顺序消息的顺序关系通过消息组(MessageGroup)判定和识别,发送顺序消息时需要为每条消息设置归属的消息组,相同消息组的多条消息之间遵循先进先出的顺序关系,不同消息组、无消息组的消息之间不涉及顺序性。

基于消息组的顺序判定逻辑,支持按照业务逻辑做细粒度拆分,可以在满足业务局部顺序的前提下提高系统的并行度和吞吐能力。

顺序性

RocketMQ 的消息的顺序性分为两部分,生产顺序性和消费顺序性。

生产的顺序性

生产的顺序性就是必须保证每个消息在生成时是顺序的,且顺序的发送到 MQ 服务器。要保证生产的顺序,需要满足以下条件

  • 单一生产者:消息生产的顺序性仅支持单一生产者,不同生产者分布在不同的系统,即使设置相同的消息组,不同生产者之间产生的消息也无法判定其先后顺序。
  • 串行发送:Apache RocketMQ 生产者客户端支持多线程安全访问,但如果生产者使用多线程并行发送,则不同线程间产生的消息将无法判定其先后顺序。

MQ 存储的顺序性

MQ 按顺序收到消息后,会保证设置了同一消息组的消息,按照发送顺序存储在同一队列中。服务端顺序存储逻辑如下:

  • 相同消息组的消息按照先后顺序被存储在同一个队列。
  • 不同消息组的消息可以混合在同一个队列中,且不保证连续。
    在这里插入图片描述

消费的顺序性

消费的顺序性,是消费者在消费的时候要严格按照 MQ 中的存储顺序来执行。

  • 消费者保证执行的顺序
    • PushConsumer 类型消费者,RocketMQ 会保证消息按照存储顺序一条一条投递给消费者
    • SimpleConsumer 类型消费者,需要业务实现方自行保证消费的顺序。消费消息时需要严格按照接收—处理—应答的语义处理消息,避免因异步处理导致消息乱序。
  • 重试策略

Apache RocketMQ 顺序消息投递仅在重试次数限定范围内,即一条消息如果一直重试失败,超过最大重试次数后将不再重试,跳过这条消息消费,不会一直阻塞后续消息处理。

所以对于需要严格保证消费顺序的场景,请务设置合理的重试次数,避免参数不合理导致消息乱序。

rocketmq-client-java 示例(gRPC 协议)

1. 创建 FIFO 主题

本示例,我们模拟多个用户的一系列操作,并多个消息组区分不同的顺序消息。要求每个用户的消息按顺序执行,不同用户的消息之间不做必要关联。

$> ./mqadmin updatetopic -n localhost:9876 -c DefaultCluster -t MY_FIFO_TOPIC -o true -a +message.type=FIFO

注意:这里比普通消息和顺序消息多了一个 -o 参数,表示 order 的意思。

生产者代码

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.message.Message;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.SendReceipt;import java.io.IOException;public class FifoProducerDemo {public static void main(String[] args) throws ClientException, IOException {// 用于提供:生产者、消费者、消息对应的构建类 BuilderClientServiceProvider provider = ClientServiceProvider.loadService();// 构建配置类(包含端点位置、认证以及连接超时等的配置)ClientConfiguration configuration = ClientConfiguration.newBuilder()// endpoints 即为 proxy 的地址,多个用分号隔开。如:xxx:8081;xxx:8081.setEndpoints(MyMQProperties.ENDPOINTS).build();// 构建生产者Producer producer = provider.newProducerBuilder()// Topics 列表:生产者和主题是多对多的关系,同一个生产者可以向多个主题发送消息.setTopics("MY_FIFO_TOPIC").setClientConfiguration(configuration)// 构建生产者,此方法会抛出 ClientException 异常.build();for(int i = 1; i <= 10;i++) {String msgGroup = "user" ; // 表示有两个用户String keys = "key_" + i;// 构建消息类Message message = provider.newMessageBuilder()// 设置消息发送到的主题.setTopic("MY_FIFO_TOPIC")// 设置消息索引键,可根据关键字精确查找某条消息。其一般为业务上的唯一值。如:订单id.setKeys(keys)// 设置消息Tag,表示为创建订单.setTag("ORDER_CREATE")// 设置消息组.setMessageGroup(msgGroup)// 消息体,单条消息的传输负载不宜过大。所以此处的字节大小最好有个限制.setBody(("{\"success\":true,\"msg\":\""+ msgGroup + ":" + keys +"\"}").getBytes()).build();// 发送消息(此处最好进行异常处理,对消息的状态进行一个记录)try {SendReceipt sendReceipt = producer.send(message);System.out.println(keys);System.out.println("Send message successfully, messageId=" + sendReceipt.getMessageId());} catch (ClientException e) {System.out.println("Failed to send message");}}// 发送完,关闭生产者// producer.close();}}

发送顺序消息时,消息一定要设置消息组,同一消息组的消息将会按服务器接收的顺序进行消费。

注:发送顺序消息前需要设置 NameServer 中的配置 orderMessageEnable 和 returnOrderTopicConfigToBroker 为 true。特别是 orderMessageEnable 默认为 false。建议在启动 namesrv 的时候使用自定义配置,在自定义配置中配置选项为true即可。

# namesrv.conf 为我们自定义的配置文件
nohup sh bin/mqnamesrv -c conf/namesrv.conf &

消费者代码

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.consumer.ConsumeResult;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;
import org.apache.rocketmq.client.apis.consumer.PushConsumer;import java.nio.ByteBuffer;
import java.util.Collections;public class FifoConsumerDemo {public static void main(String[] args) throws ClientException {// 用于提供:生产者、消费者、消息对应的构建类 BuilderClientServiceProvider provider = ClientServiceProvider.loadService();// 构建配置类(包含端点位置、认证以及连接超时等的配置)ClientConfiguration configuration = ClientConfiguration.newBuilder()// endpoints 即为 proxy 的地址,多个用分号隔开。如:xxx:8081;xxx:8081.setEndpoints(MyMQProperties.ENDPOINTS).build();// 设置过滤条件(这里为使用 tag 进行过滤)String tag = "ORDER_CREATE";FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);// 构建消费者PushConsumer pushConsumer = provider.newPushConsumerBuilder().setClientConfiguration(configuration)// 设置消费者分组.setConsumerGroup("MY_FIFO_GROUP")// 设置主题与消费者之间的订阅关系.setSubscriptionExpressions(Collections.singletonMap("MY_FIFO_TOPIC", filterExpression)).setMessageListener(messageView -> {System.out.println(messageView);ByteBuffer rs = messageView.getBody();byte[] rsByte = new byte[rs.limit()];rs.get(rsByte);System.out.println("Message body:" + new String(rsByte));// 处理消息并返回消费结果。System.out.println("Consume message successfully, messageId=" + messageView.getMessageId());return ConsumeResult.SUCCESS;}).build();System.out.println(pushConsumer);// 如果不需要再使用 PushConsumer,可关闭该实例。// pushConsumer.close();}}

注:多验证几次后会发现,消费执行并没有严格的按照顺序执行,查找源码后发现,PushConsumer 的 builder 在构建 PushConsumer 的时候有个 Settings 对象,该对象的主题配置信息是从服务器获取,获取后有一个 isFifo 参数,此参数对应是否顺序消费,但是目前此值一直为false。此问题为消费者分组的问题,Remoting 协议方式无此问题,因为两种 Client 的实现是不一样的。

解决办法

在 MQ bin目录执行如下命令即可,具体的相关说明,我们将在后续章节中(《RocketMQ 消费者分类与分组》)详细说明。

$> ./mqadmin updateSubGroup -n 127.0.0.1:9876 -g MY_FIFO_GROUP -o true -c DefaultCluster

解决后执行结果

MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000001, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543268, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_2], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000000, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543178, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_1], messageGroup=user2, deliveryTimestamp=null, properties={__SHARDINGKEY=user2}}
Message body:{"success":true,"msg":"user1:key_2"}
Message body:{"success":true,"msg":"user2:key_1"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000000
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000001
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000002, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543279, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_3], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_3"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000002
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000004, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543294, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_5], messageGroup=user2, deliveryTimestamp=null, properties={__SHARDINGKEY=user2}}
Message body:{"success":true,"msg":"user2:key_5"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000004
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000003, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543288, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_4], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000005, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543301, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_6], messageGroup=user2, deliveryTimestamp=null, properties={__SHARDINGKEY=user2}}
Message body:{"success":true,"msg":"user1:key_4"}
Message body:{"success":true,"msg":"user2:key_6"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000005
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000003
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000006, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543313, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_7], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_7"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000006
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000007, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543320, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_8], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_8"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000007
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000008, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543331, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_9], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_9"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000008
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000009, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543340, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_10], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_10"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000009

注意:user1 和 user2 的操作顺序是一致的。因为我们不需要保证 user1 的操作必须在 user2 之前,只需要保证他们各自的操作为顺序的就可以。

rocketmq-client 示例(Remoting 协议)

生产者

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.shaded.commons.lang3.RandomUtils;import java.util.List;public class FifoProducerDemo {/*** 生产者分组*/private static final String PRODUCER_GROUP = "FIFO_PRODUCER_GROUP";/*** 主题*/private static final String TOPIC = "MY_FIFO_TOPIC";public static void main(String[] args) throws MQClientException {/** 创建生产者,并使用生产者分组初始化*/DefaultMQProducer producer = new DefaultMQProducer(PRODUCER_GROUP);/** NamesrvAddr 的地址,多个用分号隔开。如:xxx:9876;xxx:9876*/producer.setNamesrvAddr(MyMQProperties.NAMESRV_ADDR);/** 发送消息超时时间,默认即为 3000*/producer.setSendMsgTimeout(3000);/** 启动生产者,此方法抛出 MQClientException*/producer.start();/** 发送消息*/for (int i = 1; i <= 10; i++) {try {Message msg = new Message();msg.setTopic(TOPIC);// 设置消息索引键,可根据关键字精确查找某条消息。msg.setKeys("messageKey");// 设置消息Tag,用于消费端根据指定Tag过滤消息。msg.setTags("ORDER_CREATE");// 设置消息体msg.setBody(("顺序消息" + i).getBytes());// 这里 userId 取值为 1,2,3(模拟有3个用户的顺序操作)int userId = RandomUtils.nextInt(1,4);SendResult sendResult = producer.send(msg, new MessageQueueSelector() {@Overridepublic MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {// 这个arg就是对应userIdInteger userId = (Integer)arg;// 我们按队列的数量,对每个user进行分组int index = userId % mqs.size();// 同一个user的消息放入同一个队列return mqs.get(index);}},userId);System.out.printf("%s%n", sendResult);} catch (Exception e) {e.printStackTrace();System.out.println("消息发送失败!i = " + i);}}// 如果生产者不再使用,则调用关闭// 异步发送消息注意:异步发送消息,建议此处不关闭或者在sleep一段时间后再关闭// 因为异步 SendCallback 执行的时候,shutdow可能已经执行了,生产者被关闭了// producer.shutdown();}}

MessageQueueSelector 详解

public interface MessageQueueSelector {MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}

mqs:队列列表,我们前面说了,默认 8 个队列
msg:当前消息
arg:为我们 send 方法传的第三个参数,示例中就是 userId

MessageQueueSelector 意为队列选择器,Remoting 协议客户端中没有 消息组的概念,所以需要我们手动的为消息进行分组(将需要严格顺序的消息放在同一个队列),这个接口就是完成此任务的,而且分组的逻辑需要我们自己实现。实际应用中我们可以使用 用户id、订单id等来为顺序消息分组。

消费者

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;import java.util.List;public class FifoConsumerDemo {public static void main(String[] args) throws MQClientException {// 初始化 consumerDefaultMQPushConsumer consumer = new DefaultMQPushConsumer("REMOTING_FIFO_CONSUMER_GROUP");// 设置 namesrv 地址consumer.setNamesrvAddr(MyMQProperties.NAMESRV_ADDR);// 设置从开头开始读取消息consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);// 设置订阅的主题,以及过滤tagconsumer.subscribe("MY_FIFO_TOPIC", "ORDER_CREATE || TagA || TagD || messageTag");consumer.registerMessageListener(new MessageListenerOrderly() {@Overridepublic ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);for(MessageExt msg : msgs){System.out.println(new String(msg.getBody()));}return ConsumeOrderlyStatus.SUCCESS;}});consumer.start();System.out.printf("Consumer Started.%n");}}

注意:顺序消息消费者的监听类型为 MessageListenerOrderly ,注意与我们前面的示例 MessageListenerConcurrently 进行区分。

相关文章:

RocketMQ 发送顺序消息

文章目录 顺序消息应用场景消息组&#xff08;MessageGroup&#xff09;顺序性生产的顺序性MQ 存储的顺序性消费的顺序性 rocketmq-client-java 示例&#xff08;gRPC 协议&#xff09;1. 创建 FIFO 主题生产者代码消费者代码解决办法解决后执行结果 rocketmq-client 示例&…...

【面试经典150 | 双指针】判断子序列

文章目录 写在前面Tag题目来源题目解题解题思路方法一&#xff1a;双指针方法二&#xff1a;动态规划 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对…...

自动驾驶信息安全方案

目录 1. 修订历史... 3 2. 概述... 4 2.1 目的... 4 2.2 适用范围... 4 2.3 参考文档... 4 2.4 术语和缩写... 4 3. 安全分析... 5 4. 总体设计... 6 4.1 ACU的安全防护... 7 4.1.1 系统安全引导... 7 4.1.2 密钥安全存储... 8 4.1.3 应…...

【云原生】kubernetes中pod(最小的资源管理组件)

目录 前言 一、pod 1.1pause容器使得Pod中的所有容器可以共享两种资源&#xff1a; 1.2 通常把Pod分为两类 1.2.1自主式Pod 1.2.2控制器管理的Pod 1.3 Pod 容器的分类 1.3.1基础容器&#xff08;infrastructure container&#xff09; 1.3.2初始化容器&#xff08;initc…...

[DB]数据库--lowdb

[DB]数据库--lowdb lowdb基本应用获取数据数据变更写入文件 lodash的使用获取数据lodash方法使用数据变更写入文件 lowdb lowdb ,是一个基于文件存储的非关系型数据库 基于loadsh的轻量级数据库 可用于在json中存储数据&#xff0c;大小一般为0~200M的json文件 方便简单的数…...

Kotlin | 在for、forEach循环中正确的使用break、continue

文章目录 for循环中使用break、continueLabel标签forEach中如何退出循环资料 Kotlin 有三种结构化跳转表达式&#xff1a; return&#xff1a;默认从最直接包围它的函数或者匿名函数返回。break&#xff1a;终止最直接包围它的循环。continue&#xff1a;继续下一次最直接包围…...

【C++】详解std::mutex

2023年9月11日&#xff0c;周一中午开始 2023年9月11日&#xff0c;周一晚上23&#xff1a;25写完 目录 概述头文件std::mutex类的成员类型方法没有std::mutex会产生什么问题问题一&#xff1a;数据竞争问题二&#xff1a;不一致lock和unlock死锁 概述 std::mutex是C标准库中…...

Matlab图像处理-Lab模型

Lab模型 Lab模型是由CIE&#xff08;国际照明委员会&#xff09;制定的一种彩色模型。该模型与设备无关&#xff0c;弥补了RGB模型和CMYK模型必须依赖于设备颜色特性的不足&#xff1b; 另外&#xff0c;自然界中的任何颜色都可以在Lab空间中表现出来&#xff0c;也就是说RGB和…...

分布式ETL工具Sqoop实践

Mysql数据准备 1、在node02节点登录Mysql。 mysql -uroot -proot2、新建数据库testdb。 create database testdb;3、新建数据表ts。 use testdb; create table ts(id int, name varchar(10), age int, sex char(1));4、向表中插入数据。 insert into ts values(10001,张三…...

展会预告 | 图扑邀您共聚 IOTE 国际物联网展·深圳站

参展时间&#xff1a;9 月 20 日- 22 日 图扑展位&#xff1a;9 号馆 9B 35-1 参展地址&#xff1a;深圳国际会展中心&#xff08;宝安新馆&#xff09; IOTE 2023 第二十届国际物联网展深圳站&#xff0c;将于 9 月 20 日- 22 日在深圳国际会展中心&#xff08;宝安&#xf…...

如何下载安装 WampServer 并结合 cpolar 内网穿透,轻松实现对本地服务的公网访问

文章目录 前言1.WampServer下载安装2.WampServer启动3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 Wamp 是一个 Windows系统下的 Apache PHP Mysql 集成安装环境&#xff0c;是一组常用来…...

iOS添加Mapbox地图库

配置凭据 注册并导航到Account页面。你将需要&#xff1a; 公共访问令牌&#xff1a; 从帐户的tokens页面&#xff0c;你可以复制默认的公共令牌或单击"create a token"按钮来创建新的公共令牌。 带有Downloads:Read范围的秘密访问令牌&#xff1a; 从你帐户的t…...

destoon根据目录下的html文件生成地图索引

因为项目需要&#xff0c;destoon根据目录下的html文件生成地图索引&#xff0c;操作方法&#xff0c;代码如下&#xff1a; <?php $new_array array(); function loopDir($dir,&$new_array,$modurl) {$handle opendir($dir);header("Content-Type:text/xml&qu…...

gRPC之gRPC流

1、gRPC流 从其名称可以理解&#xff0c;流就是持续不断的传输。有一些业务场景请求或者响应的数据量比较大&#xff0c;不适合使用普通的 RPC 调用通过一次请求-响应处理&#xff0c;一方面是考虑数据量大对请求响应时间的影响&#xff0c;另一方面业务场景的设计不一 定需…...

Kafka Shell命令交互

Kafka提供了一个命令行工具,用于管理和与Kafka集群交互。这个命令行工具通常称为Kafka Shell,它允许您执行各种操作,如创建主题、发送和消费消息、查看主题列表等。 以下是一些常用的Kafka Shell命令: 创建主题(Topic): kafka-topics.sh --create --topic my-topic --pa…...

什么是回归测试?

什么是回归测试&#xff1f; 回归测试被定义为一种软件测试类型&#xff0c;以确认最近的程序或代码更改未对现有功能产生不利影响。 回归测试只不过是全部或部分选择已执行的测试用例&#xff0c;然后重新执行以确保现有功能正常运行。 进行此测试是为了确保新代码更改不会…...

ZTMap是如何在相关政策引导下让建筑更加智慧化的?

近几年随着智慧楼宇概念的深入&#xff0c;尤其是在“十四五规划”“新基建”“数字经济”等相关战略和政策的引导下&#xff0c;智慧楼宇也迎来了快速发展期&#xff0c;对推动智慧城市系统的建设越来越重要。那么究竟什么是智慧楼宇呢&#xff1f;智慧楼宇其实就是整合楼宇内…...

Python:函数和代码复用

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都准备好了&#xff0c;直接在文末名片自取就可 1、关于递归函…...

three.js——模型对象的使用材质和方法

模型对象的使用材质和方法 前言效果图1、旋转、缩放、平移&#xff0c;居中的使用1.1 旋转rotation&#xff08;.rotateX()、.rotateY()、.rotateZ()&#xff09;1.2缩放.scale()1.3平移.translate()1.4居中.center() 2、材质属性.wireframe 前言 BufferGeometry通过.scale()、…...

sql explain

目录 1. sql explain每个字段对应的含义1.1. id1.2. select_type1.3. table1.4. partitions1.5. type1.6. possible_keys1.7. key1.8. key_len1.9. ref1.10. rows1.11. Extra 索引实践联合索引最左列原则全值匹配不建议在索引列上做任何操作, 否则索引会失效转而全表扫描尽量使…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...