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

Kafka架构:构建高吞吐量分布式消息系统的艺术

在这里插入图片描述

目录

  • Kafka架构:构建高吞吐量分布式消息系统的艺术
    • 引言:探索Kafka的宇宙
    • Kafka核心概念与架构总览
      • 什么是Kafka?
      • Kafka的核心架构组件
      • Kafka的数据模型
    • ZooKeeper在Kafka架构中的关键作用
      • ZooKeeper的核心职责
      • ZooKeeper的数据结构
      • ZooKeeper集群配置
      • Controller机制
    • Kafka的分区与复制机制
      • 分区策略
      • 自定义分区器
      • 复制机制与ISR
      • 分区分配策略
    • Kafka的存储机制
      • 日志存储结构
      • 高效的存储设计
      • 日志清理策略
    • Kafka的消费模型
      • 消费者组与重平衡
      • ZooKeeper在消费者协调中的作用
      • 消费者实现
    • Kafka性能调优与最佳实践
      • ZooKeeper性能优化
      • Broker配置优化
      • 可靠性保证
      • 监控与运维
    • 总结:Kafka架构的艺术与实践
    • 参考链接
    • 关键词标签

Kafka架构:构建高吞吐量分布式消息系统的艺术

🌟 你好,我是 励志成为糕手 !
🌌 在代码的宇宙中,我是那个追逐优雅与性能的星际旅人。 ✨
每一行代码都是我种下的星光,在逻辑的土壤里生长成璀璨的银河;
🛠️ 每一个算法都是我绘制的星图,指引着数据流动的最短路径; 🔍
每一次调试都是星际对话,用耐心和智慧解开宇宙的谜题。
🚀 准备好开始我们的星际编码之旅了吗?

引言:探索Kafka的宇宙

在当今数据驱动的世界中,我一直在寻找能够高效处理海量数据流的解决方案。作为一名专注于分布式系统的开发者,我深刻体会到消息队列在现代架构中的重要性。而在众多消息中间件中,Apache Kafka以其卓越的性能、可扩展性和容错能力脱颖而出,成为了大数据生态系统中不可或缺的一部分。

在我的实践中,我发现很多开发者对Kafka的核心架构理解不够深入,特别是对ZooKeeper在Kafka集群中的关键作用认识不足,导致在实际应用中无法充分发挥其潜力。因此,我决定撰写这篇文章,带领大家深入探索Kafka的核心架构设计,剖析其高吞吐量和高可靠性的秘密。我们将从Kafka的基础概念出发,逐步深入到其内部机制,包括ZooKeeper的协调作用、分区策略、复制机制、存储结构以及消费模型等关键组件。

通过这篇文章,我希望能够帮助你建立对Kafka架构的系统性认识,理解其设计哲学和技术选择背后的原因。特别是ZooKeeper作为Kafka集群的"大脑",如何协调整个分布式系统的运行,这是理解Kafka架构的关键所在。无论你是刚接触Kafka的新手,还是希望深化理解的有经验开发者,这篇文章都将为你提供有价值的见解和实践指导。让我们一起揭开Kafka的神秘面纱,探索这个强大消息系统的内部世界!

Kafka核心概念与架构总览

什么是Kafka?

Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后来成为Apache基金会的顶级项目。它被设计用于构建实时数据管道和流式应用程序,具有高吞吐量、可扩展性、持久性和容错性等特点。

“Kafka不仅仅是一个消息队列,它是一个分布式的、分区的、多副本的提交日志服务。这些特性使其成为大规模、高性能数据管道的理想选择。” —— Jay Kreps,Kafka的创始人之一

Kafka的核心架构组件

ZooKeeper Ensemble
ZooKeeper 2
ZooKeeper 1
ZooKeeper 3
Kafka Cluster
Broker 2
Broker 1
Broker 3
Producer 1
Producer 2
Consumer 1
Consumer Group

图1:Kafka核心架构组件流程图

Kafka的架构由以下几个核心组件构成:

  1. Broker:Kafka服务器,负责接收和处理客户端请求,存储消息数据
  2. Producer:生产者,将消息发送到Kafka集群
  3. Consumer:消费者,从Kafka集群订阅并消费消息
  4. ZooKeeper:管理和协调Kafka集群,存储元数据信息
  5. Topic:消息的逻辑分类,每个Topic可以有多个分区

Kafka的数据模型

Kafka的数据模型围绕Topic、Partition和Offset展开:

Topic A
Partition 0
3
0
1
2
Partition 1
2
0
1
Partition 2
4
0
1
2
3

图2:Kafka数据模型流程图

  • Topic:消息的逻辑分类,类似于数据库中的表
  • Partition:每个Topic被分为多个Partition,实现并行处理
  • Offset:每条消息在Partition中的唯一标识,按顺序递增
  • Segment:Partition在物理上由多个Segment文件组成

ZooKeeper在Kafka架构中的关键作用

ZooKeeper的核心职责

ZooKeeper作为Kafka集群的协调服务,承担着多项关键职责:

  1. 集群成员管理:跟踪哪些Broker是活跃的
  2. Leader选举:为每个分区选举Leader副本
  3. 配置管理:存储Topic配置和集群配置信息
  4. 访问控制列表(ACL):管理权限和安全策略
  5. 消费者组协调:管理消费者组的元数据(在新版本中已迁移到Kafka内部)

ZooKeeper的数据结构

ZooKeeper使用类似文件系统的层次化命名空间来存储Kafka的元数据:

/kafka
├── brokers
│   ├── ids
│   │   ├── 0 (broker.id=0的信息)
│   │   ├── 1 (broker.id=1的信息)
│   │   └── 2 (broker.id=2的信息)
│   └── topics
│       └── my-topic
│           ├── partitions
│           │   ├── 0
│           │   │   └── state (Leader和ISR信息)
│           │   ├── 1
│           │   │   └── state
│           │   └── 2
│           │       └── state
├── controller (控制器信息)
├── controller_epoch (控制器纪元)
├── config
│   ├── topics
│   │   └── my-topic (Topic配置)
│   └── brokers
│       └── 0 (Broker配置)
└── admin└── delete_topics (待删除的Topic)

ZooKeeper集群配置

// ZooKeeper连接配置
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092,broker3:9092");
props.put("zookeeper.connect", "zk1:2181,zk2:2181,zk3:2181/kafka");
props.put("zookeeper.connection.timeout.ms", "6000");
props.put("zookeeper.session.timeout.ms", "6000");// 创建AdminClient来管理集群
AdminClient adminClient = AdminClient.create(props);// 获取集群元数据
DescribeClusterResult clusterResult = adminClient.describeCluster();
System.out.println("Cluster ID: " + clusterResult.clusterId().get());
System.out.println("Controller: " + clusterResult.controller().get());

上述代码展示了如何配置ZooKeeper连接。zookeeper.connect参数指定了ZooKeeper集群的地址,/kafka是ZooKeeper中Kafka数据的根路径。

Controller机制

Kafka集群中的一个Broker会被选举为Controller,负责管理整个集群的状态:

ZooKeeperControllerBroker 1Broker 2Broker 3Controller选举过程尝试创建/controller节点成功,成为Controller尝试创建/controller节点失败,节点已存在尝试创建/controller节点失败,节点已存在Controller管理集群监听Broker变化发送LeaderAndIsr请求发送LeaderAndIsr请求确认接收确认接收ZooKeeperControllerBroker 1Broker 2Broker 3

图3:Kafka Controller选举与管理时序图

Controller的主要职责包括:

  • 分区Leader选举:当分区Leader失效时,选举新的Leader
  • 副本重分配:管理分区副本在Broker间的分配
  • Topic管理:处理Topic的创建、删除和配置变更
  • Broker管理:处理Broker的加入和离开

Kafka的分区与复制机制

分区策略

分区是Kafka实现并行处理和水平扩展的基础。每个Topic可以有多个分区,分区数决定了Topic的并行度。

// 创建Topic时指定分区数和复制因子
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
AdminClient adminClient = AdminClient.create(props);NewTopic newTopic = new NewTopic("my-topic",      // Topic名称3,               // 分区数(short) 2        // 复制因子
);// 可以指定分区的副本分配
Map<Integer, List<Integer>> replicaAssignments = new HashMap<>();
replicaAssignments.put(0, Arrays.asList(0, 1)); // 分区0的副本在Broker 0和1上
replicaAssignments.put(1, Arrays.asList(1, 2)); // 分区1的副本在Broker 1和2上
replicaAssignments.put(2, Arrays.asList(2, 0)); // 分区2的副本在Broker 2和0上NewTopic customTopic = new NewTopic("custom-topic", replicaAssignments);
adminClient.createTopics(Arrays.asList(newTopic, customTopic));

上述代码展示了两种创建Topic的方式:自动分配副本和手动指定副本分配。手动分配可以更好地控制数据分布和负载均衡。

自定义分区器

// 自定义分区器示例
public class CustomPartitioner implements Partitioner {private final AtomicInteger counter = new AtomicInteger(0);@Overridepublic int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);int numPartitions = partitions.size();if (key == null) {// 如果没有key,使用轮询策略return counter.getAndIncrement() % numPartitions;} else {// 基于key的哈希值进行分区return Math.abs(key.hashCode()) % numPartitions;}}@Overridepublic void close() {// 清理资源}@Overridepublic void configure(Map<String, ?> configs) {// 配置初始化}
}// 使用自定义分区器
Properties producerProps = new Properties();
producerProps.put("bootstrap.servers", "broker1:9092,broker2:9092");
producerProps.put("partitioner.class", "com.example.CustomPartitioner");

自定义分区器允许我们根据业务需求实现特定的分区逻辑,比如按用户ID分区、按地理位置分区等。

复制机制与ISR

Kafka通过复制机制实现高可用性。每个分区可以有多个副本,其中一个作为Leader,其余作为Follower。

Partition 2
Follower
Broker 0
Leader
Broker 2
Follower
Broker 1
Partition 1
Follower
Broker 0
Leader
Broker 1
Follower
Broker 2
Partition 0
Leader
Broker 0
Follower
Broker 1
Follower
Broker 2

图4:Kafka分区副本分布架构图

ISR (In-Sync Replicas) 是Kafka保证数据一致性的关键机制:

  • ISR包含Leader副本和所有与Leader保持同步的Follower副本
  • 只有ISR中的副本才有资格在Leader失效时被选为新Leader
  • 通过replica.lag.time.max.ms参数控制副本是否保持同步

分区分配策略

Consumer Group中的消费者如何分配分区是Kafka消费模型的重要部分:

// 配置消费者分区分配策略
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("group.id", "my-consumer-group");
props.put("partition.assignment.strategy", "org.apache.kafka.clients.consumer.RangeAssignor," +"org.apache.kafka.clients.consumer.RoundRobinAssignor," +"org.apache.kafka.clients.consumer.StickyAssignor");// 自定义分区分配策略
public class CustomAssignor extends AbstractPartitionAssignor {@Overridepublic String name() {return "custom";}@Overridepublic Map<String, List<TopicPartition>> assign(Map<String, Integer> partitionsPerTopic,Map<String, Subscription> subscriptions) {// 实现自定义分配逻辑Map<String, List<TopicPartition>> assignment = new HashMap<>();// ... 分配逻辑实现return assignment;}
}

Kafka提供了多种分区分配策略:

  1. Range分配器:将单个Topic的连续分区分配给消费者
  2. RoundRobin分配器:轮询方式将所有Topic的分区分配给消费者
  3. Sticky分配器:尽量保持现有分配,减少重平衡开销
  4. Cooperative Sticky分配器:增量式重平衡,减少服务中断

Kafka的存储机制

日志存储结构

Kafka的核心是一个分布式提交日志系统,其存储结构设计是高性能的关键。

每个分区由多个Segment组成,每个Segment包含三种文件:

  • .log:实际存储消息数据的文件
  • .index:偏移量索引文件,加速消息查找
  • .timeindex:时间戳索引文件,支持基于时间的查询

高效的存储设计

Kafka的存储设计有几个关键特点:

  1. 顺序写入:利用顺序I/O提高写入性能
  2. 零拷贝:直接从文件系统缓存到网络缓冲区,减少数据拷贝
  3. 批量处理:批量发送和接收消息,提高吞吐量
  4. 页缓存利用:充分利用操作系统的页缓存
// 生产者批处理配置
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("batch.size", 16384);           // 批次大小(字节)
props.put("linger.ms", 10);               // 等待时间,增加批处理机会
props.put("buffer.memory", 33554432);     // 缓冲区大小
props.put("compression.type", "lz4");     // 压缩类型// 配置序列化器
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");KafkaProducer<String, String> producer = new KafkaProducer<>(props);// 异步发送消息
producer.send(new ProducerRecord<>("my-topic", "key", "value"), new Callback() {@Overridepublic void onCompletion(RecordMetadata metadata, Exception exception) {if (exception != null) {exception.printStackTrace();} else {System.out.printf("Sent message to topic %s partition %d offset %d%n",metadata.topic(), metadata.partition(), metadata.offset());}}});

这段配置代码中,batch.size控制批次大小,linger.ms增加批处理机会,compression.type启用压缩以减少网络传输。

日志清理策略

Kafka提供两种日志清理策略:

// Topic配置:日志保留策略
Properties topicConfig = new Properties();
topicConfig.put("cleanup.policy", "delete");        // 删除策略
topicConfig.put("retention.ms", "604800000");       // 保留7天
topicConfig.put("retention.bytes", "1073741824");   // 保留1GB// 或者使用压缩策略
Properties compactConfig = new Properties();
compactConfig.put("cleanup.policy", "compact");     // 压缩策略
compactConfig.put("min.cleanable.dirty.ratio", "0.5"); // 脏数据比例阈值
compactConfig.put("delete.retention.ms", "86400000");  // 删除标记保留时间// 创建Topic时应用配置
NewTopic topic = new NewTopic("my-topic", 3, (short) 2);
topic.configs(topicConfig);
  • 删除策略(delete):基于时间或大小删除旧数据
  • 压缩策略(compact):保留每个key的最新值,删除旧版本

Kafka的消费模型

消费者组与重平衡

Kafka的消费模型基于消费者组(Consumer Group)概念,同一组内的消费者共同消费Topic的数据。

ZooKeeper在消费者协调中的作用

虽然新版本Kafka已将消费者组协调迁移到Kafka内部,但了解ZooKeeper的历史作用仍然重要:

/kafka/consumers
├── my-consumer-group
│   ├── ids
│   │   ├── consumer-1 (消费者实例信息)
│   │   └── consumer-2
│   ├── owners
│   │   ├── my-topic
│   │   │   ├── 0 (分区0的所有者)
│   │   │   ├── 1 (分区1的所有者)
│   │   │   └── 2 (分区2的所有者)
│   └── offsets
│       └── my-topic
│           ├── 0 (分区0的偏移量)
│           ├── 1 (分区1的偏移量)
│           └── 2 (分区2的偏移量)

消费者实现

// 消费者配置
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("group.id", "my-consumer-group");
props.put("enable.auto.commit", "false");  // 禁用自动提交
props.put("auto.offset.reset", "earliest"); // 从最早的消息开始消费
props.put("session.timeout.ms", "30000");   // 会话超时时间
props.put("heartbeat.interval.ms", "10000"); // 心跳间隔
props.put("max.poll.interval.ms", "300000"); // 最大轮询间隔// 配置反序列化器
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("my-topic"));try {while (true) {ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));// 按分区处理消息for (TopicPartition partition : records.partitions()) {List<ConsumerRecord<String, String>> partitionRecords = records.records(partition);for (ConsumerRecord<String, String> record : partitionRecords) {System.out.printf("Partition: %d, Offset: %d, Key: %s, Value: %s%n",record.partition(), record.offset(), record.key(), record.value());// 处理消息processMessage(record);}// 手动提交特定分区的偏移量long lastOffset = partitionRecords.get(partitionRecords.size() - 1).offset();consumer.commitSync(Collections.singletonMap(partition, new OffsetAndMetadata(lastOffset + 1)));}}
} catch (Exception e) {e.printStackTrace();
} finally {consumer.close();
}private void processMessage(ConsumerRecord<String, String> record) {// 业务逻辑处理try {// 模拟处理时间Thread.sleep(10);System.out.println("Processed message: " + record.value());} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}

这段代码展示了消费者的完整实现。关键点包括:

  • 禁用自动提交(enable.auto.commit=false
  • 按分区处理消息以提高效率
  • 手动控制偏移量提交确保消息处理的可靠性

Kafka性能调优与最佳实践

ZooKeeper性能优化

ZooKeeper的性能直接影响Kafka集群的稳定性:

# ZooKeeper配置优化 (zoo.cfg)
tickTime=2000                    # 基本时间单位
initLimit=10                     # 初始化连接时限
syncLimit=5                      # 同步时限
dataDir=/var/lib/zookeeper       # 数据目录
clientPort=2181                  # 客户端连接端口
maxClientCnxns=60               # 最大客户端连接数
autopurge.snapRetainCount=3     # 保留快照数量
autopurge.purgeInterval=24      # 清理间隔(小时)# 服务器列表
server.1=zk1:2888:3888
server.2=zk2:2888:3888
server.3=zk3:2888:3888

Broker配置优化

参数说明默认值推荐值影响
num.network.threads网络线程数3核心数处理网络请求的能力
num.io.threadsI/O线程数8核心数*2处理磁盘I/O的能力
socket.send.buffer.bytes套接字发送缓冲区100KB1MB网络发送性能
socket.receive.buffer.bytes套接字接收缓冲区100KB1MB网络接收性能
log.retention.hours日志保留时间168 (7天)根据业务需求存储空间使用
log.segment.bytes日志段大小1GB根据消息大小调整文件管理效率
replica.fetch.max.bytes副本获取最大字节数1MB根据消息大小调整副本同步性能
zookeeper.session.timeout.msZooKeeper会话超时6000根据网络延迟调整集群稳定性

可靠性保证

Kafka提供多级别的消息发送可靠性保证:

// 生产者可靠性配置
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("acks", "all");                 // 所有ISR副本确认
props.put("retries", Integer.MAX_VALUE);  // 无限重试
props.put("retry.backoff.ms", 100);       // 重试间隔
props.put("max.in.flight.requests.per.connection", 1); // 防止消息乱序
props.put("enable.idempotence", true);    // 启用幂等性
props.put("delivery.timeout.ms", 120000); // 交付超时时间Producer<String, String> producer = new KafkaProducer<>(props);// 事务支持
props.put("transactional.id", "my-transactional-id");
Producer<String, String> transactionalProducer = new KafkaProducer<>(props);transactionalProducer.initTransactions();
try {transactionalProducer.beginTransaction();// 发送多条消息transactionalProducer.send(new ProducerRecord<>("topic1", "key1", "value1"));transactionalProducer.send(new ProducerRecord<>("topic2", "key2", "value2"));// 提交事务transactionalProducer.commitTransaction();
} catch (Exception e) {// 中止事务transactionalProducer.abortTransaction();throw e;
}

acks参数控制生产者的可靠性级别:

  • acks=0:不等待确认,最高吞吐量但可能丢失数据
  • acks=1:等待Leader确认,平衡性能和可靠性
  • acks=all:等待所有ISR副本确认,最高可靠性但性能较低

监控与运维

// 集群健康检查
public class KafkaHealthChecker {private final AdminClient adminClient;public KafkaHealthChecker(String bootstrapServers) {Properties props = new Properties();props.put("bootstrap.servers", bootstrapServers);this.adminClient = AdminClient.create(props);}public void checkClusterHealth() throws Exception {// 检查集群基本信息DescribeClusterResult clusterResult = adminClient.describeCluster();System.out.println("Cluster ID: " + clusterResult.clusterId().get());System.out.println("Controller: " + clusterResult.controller().get());// 检查Broker状态Collection<Node> nodes = clusterResult.nodes().get();System.out.println("Active Brokers: " + nodes.size());// 检查Topic状态ListTopicsResult topicsResult = adminClient.listTopics();Set<String> topics = topicsResult.names().get();System.out.println("Total Topics: " + topics.size());// 检查消费者组状态ListConsumerGroupsResult groupsResult = adminClient.listConsumerGroups();Collection<ConsumerGroupListing> groups = groupsResult.all().get();System.out.println("Active Consumer Groups: " + groups.size());}
}

总结:Kafka架构的艺术与实践

在这篇文章中,我们深入探索了Kafka的核心架构设计,从基础概念到内部机制,全面剖析了这个强大的分布式消息系统。作为一名多年从事分布式系统开发的工程师,我深刻体会到Kafka在处理大规模数据流方面的卓越能力,特别是ZooKeeper在其中发挥的关键协调作用。

通过对Kafka分区机制、复制策略、存储结构和消费模型的详细分析,我们可以看到Kafka的设计哲学:通过简单而优雅的抽象,构建高度可扩展、高吞吐量的消息系统。ZooKeeper作为集群的"大脑",负责元数据管理、Leader选举和集群协调,虽然新版本Kafka正在减少对ZooKeeper的依赖,但理解其工作原理对于深入掌握Kafka架构仍然至关重要。

在我的实践经验中,正确理解和应用Kafka架构知识是构建高效、可靠数据管道的关键。无论是实时数据处理、日志聚合还是事件驱动架构,Kafka都能提供强大的支持。但同时,我也发现很多团队在使用Kafka时只停留在表面,没有充分理解ZooKeeper的作用和Kafka的内部机制,导致在生产环境中遇到各种问题。

希望这篇文章能够帮助你建立对Kafka架构的系统性认识,掌握其核心设计原则和最佳实践。在未来的数据驱动世界中,Kafka无疑将继续扮演重要角色,而深入理解其架构,包括ZooKeeper的协调机制,将为你的技术实践提供坚实基础。记住,优秀的架构不仅仅是技术的堆砌,更是对问题本质的洞察和对解决方案的精心设计。让我们在实践中不断探索和完善,共同推动分布式系统技术的发展!

参考链接

  1. Apache Kafka 官方文档
  2. Apache ZooKeeper 官方文档
  3. Kafka: The Definitive Guide
  4. Kafka Internals: How It Works
  5. Confluent Developer: Kafka Architecture

关键词标签

#Kafka架构 #ZooKeeper协调 #分布式消息系统 #数据流处理 #高可用性


文章转载自:

http://b9NYHgbr.Lsnhs.cn
http://ueiGRkjq.Lsnhs.cn
http://RXx5uXsT.Lsnhs.cn
http://QUq8RIl0.Lsnhs.cn
http://K5MHSypW.Lsnhs.cn
http://Awq9TG4I.Lsnhs.cn
http://7GE93FCb.Lsnhs.cn
http://wh4lyWOD.Lsnhs.cn
http://TF8yPvd4.Lsnhs.cn
http://BZZ43Tcl.Lsnhs.cn
http://ZOtspGzB.Lsnhs.cn
http://tOU3C9FA.Lsnhs.cn
http://GHjpImb5.Lsnhs.cn
http://2zFgWPKa.Lsnhs.cn
http://0xc3pdBG.Lsnhs.cn
http://ySV6inq0.Lsnhs.cn
http://IaBoap8Y.Lsnhs.cn
http://LVd1QT1M.Lsnhs.cn
http://mVfrK3UJ.Lsnhs.cn
http://0kOMq4rz.Lsnhs.cn
http://aGVl9kvR.Lsnhs.cn
http://repXXX6s.Lsnhs.cn
http://f5JyWTbQ.Lsnhs.cn
http://gJGOwEyD.Lsnhs.cn
http://3qyPIeBX.Lsnhs.cn
http://C0yMZLEb.Lsnhs.cn
http://9pcpjZIC.Lsnhs.cn
http://RrRkluFs.Lsnhs.cn
http://kJcgHqqm.Lsnhs.cn
http://OEco1NxP.Lsnhs.cn
http://www.dtcms.com/a/379838.html

相关文章:

  • 5G NR-NTN协议学习系列:NR-NTN介绍(2)
  • AI原创音乐及视频所有权属问题研究:法律框架、司法实践与产业展望
  • 深度学习笔记35-YOLOv5 使用自己的数据集进行训练
  • C++日志输出库:spdlog
  • 企业数字化转型案例:Heinzel集团SAP S/4HANA系统升级完成
  • 企业能源管理供电供水数据采集监测管理解决方案
  • React 进阶
  • ES相关问题汇总
  • 为什么Cesium不使用vue或者react,而是 保留 Knockout
  • Mysql杂志(十五)——公用表达式CTE
  • Javascript忘记了,好像又想起来了一点?
  • AI + 制造:NebulaAI 场景实践来了!
  • mosdns缓存dns服务器配置记录
  • android14 硬键盘ESC改BACK按键返回无效问题
  • 代码随想录算法训练营第62天 | Floyd 算法精讲、A * 算法精讲 (A star算法)、最短路算法总结篇、图论总结
  • 教程:用免费 Google Translate API 在 VSCode 中实现中文注释自动翻译英文
  • 数据储存方式
  • Java生态圈核心组件深度解析:Spring技术栈与分布式系统实战
  • 解决Ubuntu中apt-get -y安装时弹出交互提示的问题
  • 硅基计划3.0 Map类Set类
  • Ubuntu20.04手动安装中文输入法
  • 算法训练营DAY60 第十一章:图论part11
  • java 反射Class类/加载类/创建对象及方法
  • RL【9】:Policy Gradient
  • Java短链接生成服务实战指南
  • JAVA Web —— A / 网页开发基础
  • TensorFlow深度学习实战:从零开始构建你的第一个神经网络
  • Keepalived 负载均衡
  • 智能文档处理业务,应该选择大模型还是OCR专用小模型?
  • 《Redis核心机制解析》