您当前的位置:首页 > 互联网百科 > 百科

如何快速全面掌握Kafka?5000字吐血整理

时间:2020-03-17 10:43:53  来源:  作者:

Kafka 是目前主流的分布式消息引擎及流处理平台,经常用做企业的消息总线、实时数据管道,本文挑选了 Kafka 的几个核心话题,帮助大家快速掌握 Kafka,包括:

  • Kafka 体系架构

  • Kafka 消息发送机制

  • Kafka 副本机制

  • Kafka 控制器

  • Kafka Rebalance 机制

因为涉及内容较多,本文尽量做到深入浅出,全面的介绍 Kafka 原理及核心组件,不怕你不懂 Kafka。

 

1. Kafka 快速入门

Kafka 是一个分布式消息引擎与流处理平台,经常用做企业的消息总线、实时数据管道,有的还把它当做存储系统来使用。早期 Kafka 的定位是一个高吞吐的分布式消息系统,目前则演变成了一个成熟的分布式消息引擎,以及流处理平台。

1.1 Kafka 体系架构

Kafka 的设计遵循生产者消费者模式,生产者发送消息到 broker 中某一个 topic 的具体分区里,消费者从一个或多个分区中拉取数据进行消费。拓扑图如下:

如何快速全面掌握Kafka?5000字吐血整理

目前,Kafka 依靠 Zookeeper 做分布式协调服务,负责存储和管理 Kafka 集群中的元数据信息,包括集群中的 broker 信息、topic 信息、topic 的分区与副本信息等。

 

1.2 Kafka 术语

这里整理了 Kafka 的一些关键术语:

  • Producer:生产者,消息产生和发送端。

  • Broker:Kafka 实例,多个 broker 组成一个 Kafka 集群,通常一台机器部署一个 Kafka 实例,一个实例挂了不影响其他实例。

  • Consumer:消费者,拉取消息进行消费。 一个 topic 可以让若干个消费者进行消费,若干个消费者组成一个 Consumer Group 即消费组,一条消息只能被消费组中一个 Consumer 消费。

  • Topic:主题,服务端消息的逻辑存储单元。一个 topic 通常包含若干个 Partition 分区。

  • Partition:topic 的分区,分布式存储在各个 broker 中, 实现发布与订阅的负载均衡。若干个分区可以被若干个 Consumer 同时消费,达到消费者高吞吐量。一个分区拥有多个副本(Replica),这是Kafka在可靠性和可用性方面的设计,后面会重点介绍。

  • message:消息,或称日志消息,是 Kafka 服务端实际存储的数据,每一条消息都由一个 key、一个 value 以及消息时间戳 timestamp 组成。

  • offset:偏移量,分区中的消息位置,由 Kafka 自身维护,Consumer 消费时也要保存一份 offset 以维护消费过的消息位置。

 

1.3 Kafka 作用与特点

Kafka 主要起到削峰填谷(缓冲)、系统解构以及冗余的作用,主要特点有:

  • 高吞吐、低延时:这是 Kafka 显著的特点,Kafka 能够达到百万级的消息吞吐量,延迟可达毫秒级;

  • 持久化存储:Kafka 的消息最终持久化保存在磁盘之上,提供了顺序读写以保证性能,并且通过 Kafka 的副本机制提高了数据可靠性。

  • 分布式可扩展:Kafka 的数据是分布式存储在不同 broker 节点的,以 topic 组织数据并且按 partition 进行分布式存储,整体的扩展性都非常好。

  • 高容错性:集群中任意一个 broker 节点宕机,Kafka 仍能对外提供服务。

 

2. Kafka 消息发送机制

Kafka 生产端发送消息的机制非常重要,这也是 Kafka 高吞吐的基础,生产端的基本流程如下图所示:

如何快速全面掌握Kafka?5000字吐血整理

主要有以下方面的设计:

2.1 异步发送

Kafka 自从 0.8.2 版本就引入了新版本 Producer API,新版 Producer 完全是采用异步方式发送消息。生产端构建的 ProducerRecord 先是经过 keySerializer、valueSerializer 序列化后,再是经过 Partition 分区器处理,决定消息落到 topic 具体某个分区中,最后把消息发送到客户端的消息缓冲池 accumulator 中,交由一个叫作 Sender 的线程发送到 broker 端。

这里缓冲池 accumulator 的最大大小由参数 buffer.memory 控制,默认是 32M,当生产消息的速度过快导致 buffer 满了的时候,将阻塞 max.block.ms 时间,超时抛异常,所以 buffer 的大小可以根据实际的业务情况进行适当调整。

2.2 批量发送

发送到缓冲 buffer 中消息将会被分为一个一个的 batch,分批次的发送到 broker 端,批次大小由参数 batch.size 控制,默认16KB。这就意味着正常情况下消息会攒够 16KB 时才会批量发送到 broker 端,所以一般减小 batch 大小有利于降低消息延时,增加 batch 大小有利于提升吞吐量。

那么生成端消息是不是必须要达到一个 batch 大小时,才会批量发送到服务端呢?答案是否定的,Kafka 生产端提供了另一个重要参数 linger.ms,该参数控制了 batch 最大的空闲时间,超过该时间的 batch 也会被发送到 broker 端。

2.3 消息重试

此外,Kafka 生产端支持重试机制,对于某些原因导致消息发送失败的,比如网络抖动,开启重试后 Producer 会尝试再次发送消息。该功能由参数 retries 控制,参数含义代表重试次数,默认值为 0 表示不重试,建议设置大于 0 比如 3。

 

3. Kafka 副本机制

前面提及了 Kafka 分区副本(Replica)的概念,副本机制也称 Replication 机制是 Kafka 实现高可靠、高可用的基础。Kafka 中有 leader 和 follower 两类副本。

3.1 Kafka 副本作用

Kafka 默认只会给分区设置一个副本,由 broker 端参数 default.replication.factor 控制,默认值为 1,通常我们会修改该默认值,或者命令行创建 topic 时指定 replication-factor 参数,生产建议设置 3 副本。副本作用主要有两方面:

  • 消息冗余存储,提高 Kafka 数据的可靠性;

  • 提高 Kafka 服务的可用性,follower 副本能够在 leader 副本挂掉或者 broker 宕机的时候参与 leader 选举,继续对外提供读写服务。

3.2 关于读写分离

这里要说明的是 Kafka 并不支持读写分区,生产消费端所有的读写请求都是由 leader 副本处理的,follower 副本的主要工作就是从 leader 副本处异步拉取消息,进行消息数据的同步,并不对外提供读写服务。

Kafka 之所以这样设计,主要是为了保证读写一致性,因为副本同步是一个异步的过程,如果当 follower 副本还没完全和 leader 同步时,从 follower 副本读取数据可能会读不到最新的消息。

3.3 ISR 副本集合

Kafka 为了维护分区副本的同步,引入 ISR(In-Sync Replicas)副本集合的概念,ISR 是分区中正在与 leader 副本进行同步的 replica 列表,且必定包含 leader 副本。

ISR 列表是持久化在 Zookeeper 中的,任何在 ISR 列表中的副本都有资格参与 leader 选举。

如何快速全面掌握Kafka?5000字吐血整理

ISR 列表是动态变化的,并不是所有的分区副本都在 ISR 列表中,哪些副本会被包含在 ISR 列表中呢?副本被包含在 ISR 列表中的条件是由参数 replica.lag.time.max.ms 控制的,参数含义是副本同步落后于 leader 的最大时间间隔,默认10s,意思就是说如果某一 follower 副本中的消息比 leader 延时超过10s,就会被从 ISR 中排除。Kafka 之所以这样设计,主要是为了减少消息丢失,只有与 leader 副本进行实时同步的 follower 副本才有资格参与 leader 选举,这里指相对实时。

3.4 Unclean leader 选举

既然 ISR 是动态变化的,所以 ISR 列表就有为空的时候,ISR 为空说明 leader 副本也“挂掉”了,此时 Kafka 就要重新选举出新的 leader。但 ISR 为空,怎么进行 leader 选举呢?

Kafka 把不在 ISR 列表中的存活副本称为“非同步副本”,这些副本中的消息远远落后于 leader,如果选举这种副本作为 leader 的话就可能造成数据丢失。Kafka broker 端提供了一个参数 unclean.leader.election.enable,用于控制是否允许非同步副本参与 leader 选举;如果开启,则当 ISR 为空时就会从这些副本中选举新的 leader,这个过程称为 Unclean leader 选举。

前面也提及了,如果开启 Unclean leader 选举,可能会造成数据丢失,但保证了始终有一个 leader 副本对外提供服务;如果禁用 Unclean leader 选举,就会避免数据丢失,但这时分区就会不可用。这就是典型的 CAP 理论,即一个系统不可能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)中的两个。所以在这个问题上,Kafka 赋予了我们选择 C 或 A 的权利。

我们可以根据实际的业务场景选择是否开启 Unclean leader选举,这里建议关闭 Unclean leader 选举,因为通常数据的一致性要比可用性重要的多。

 

4. Kafka 控制器

控制器(Controller)是 Kafka 的核心组件,它的主要作用是在 Zookeeper 的帮助下管理和协调整个 Kafka 集群。集群中任意一个 broker 都能充当控制器的角色,但在运行过程中,只能有一个 broker 成为控制器。

这里先介绍下 Zookeeper,因为控制器的产生依赖于 Zookeeper 的 ZNode 模型和 Watcher 机制。Zookeeper 的数据模型是类似 Unix 操作系统的 ZNode Tree 即 ZNode 树,ZNode 是 Zookeeper 中的数据节点,是 Zookeeper 存储数据的最小单元,每个 ZNode 可以保存数据,也可以挂载子节点,根节点是 /。基本的拓扑图如下:

如何快速全面掌握Kafka?5000字吐血整理

Zookeeper 有两类 ZNode 节点,分别是持久性节点和临时节点。持久性节点是指客户端与 Zookeeper 断开会话后,该节点依旧存在,直到执行删除操作才会清除节点。临时节点的生命周期是和客户端的会话绑定在一起,客户端与 Zookeeper 断开会话后,临时节点就会被自动删除。

Watcher 机制是 Zookeeper 非常重要的特性,它可以在 ZNode 节点上绑定监听事件,比如可以监听节点数据变更、节点删除、子节点状态变更等事件,通过这个事件机制,可以基于 ZooKeeper 实现分布式锁、集群管理等功能。

4.1 控制器选举

当集群中的任意 broker 启动时,都会尝试去 Zookeeper 中创建 /controller 节点,第一个成功创建 /controller 节点的 broker 则会被指定为控制器,其他 broker 则会监听该节点的变化。当运行中的控制器突然宕机或意外终止时,其他 broker 能够快速地感知到,然后再次尝试创建 /controller 节点,创建成功的 broker 会成为新的控制器。

4.2 控制器功能

前面我们也说了,控制器主要作用是管理和协调 Kafka 集群,那么 Kafka 控制器都做了哪些事情呢,具体如下:

  • 主题管理:创建、删除 topic,以及增加 topic 分区等操作都是由控制器执行。

  • 分区重分配:执行 Kafka 的 reassign 脚本对 topic 分区重分配的操作,也是由控制器实现。

  • Preferred leader 选举:这里有一个概念叫 Preferred replica 即优先副本,表示的是分配副本中的第一个副本。Preferred leader 选举就是指 Kafka 在某些情况下出现 leader 负载不均衡时,会选择 preferred 副本作为新 leader 的一种方案。这也是控制器的职责范围。

  • 集群成员管理:控制器能够监控新 broker 的增加,broker 的主动关闭与被动宕机,进而做其他工作。这里也是利用前面所说的 Zookeeper 的 ZNode 模型和 Watcher 机制,控制器会监听 Zookeeper 中 /brokers/ids 下临时节点的变化。

  • 数据服务:控制器上保存了最全的集群元数据信息,其他所有 broker 会定期接收控制器发来的元数据更新请求,从而更新其内存中的缓存数据。

从上面内容我们大概知道,控制器可以说是 Kafka 的心脏,管理和协调着整个 Kafka 集群,因此控制器自身的性能和稳定性就变得至关重要。

社区在这方面做了大量工作,特别是在 0.11 版本中对控制器进行了重构,其中最大的改进把控制器内部多线程的设计改成了单线程加事件队列的方案,消除了多线程的资源消耗和线程安全问题,另外一个改进是把之前同步操作 Zookeeper 改为了异步操作,消除了 Zookeeper 端的性能瓶颈,大大提升了控制器的稳定性。

 

5. Kafka 消费端 Rebalance 机制

前面介绍消费者术语时,提到了消费组的概念,一个 topic 可以让若干个消费者进行消费,若干个消费者组成一个 Consumer Group 即消费组 ,一条消息只能被消费组中的一个消费者进行消费。我们用下图表示Kafka的消费模型。

如何快速全面掌握Kafka?5000字吐血整理

5.1 Rebalance 概念

就 Kafka 消费端而言,有一个难以避免的问题就是消费者的重平衡即 Rebalance。Rebalance 是让一个消费组的所有消费者就如何消费订阅 topic 的所有分区达成共识的过程,在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 的完成。因为要停止消费等待重平衡完成,因此 Rebalance 会严重影响消费端的 TPS,是应当尽量避免的。

5.2 Rebalance 发生条件

关于何时会发生 Rebalance,总结起来有三种情况:

  • 消费组的消费者成员数量发生变化

  • 消费主题的数量发生变化

  • 消费主题的分区数量发生变化

其中后两种情况一般是计划内的,比如为了提高消息吞吐量增加 topic 分区数,这些情况一般是不可避免的,后面我们会重点讨论如何避免因为组内消费者成员数发生变化导致的 Rebalance。

5.3 Kafka 协调器

在介绍如何避免 Rebalance 问题之前,先来认识下 Kafka 的协调器 Coordinator,和之前 Kafka 控制器类似,Coordinator 也是 Kafka 的核心组件。

主要有两类 Kafka 协调器:

  • 组协调器(Group Coordinator)

  • 消费者协调器(Consumer Coordinator)

Kafka 为了更好的实现消费组成员管理、位移管理,以及 Rebalance 等,broker 服务端引入了组协调器(Group Coordinator),消费端引入了消费者协调器(Consumer Coordinator)。每个 broker 启动的时候,都会创建一个 GroupCoordinator 实例,负责消费组注册、消费者成员记录、offset 等元数据操作,这里也可以看出每个 broker 都有自己的 Coordinator 组件。另外,每个 Consumer 实例化时,同时会创建一个 ConsumerCoordinator 实例,负责消费组下各个消费者和服务端组协调器之前的通信。可以用下图表示协调器原理:

如何快速全面掌握Kafka?5000字吐血整理

客户端的消费者协调器 Consumer Coordinator 和服务端的组协调器 Group Coordinator 会通过心跳不断保持通信。

5.4 如何避免消费组 Rebalance

接下来我们讨论下如何避免组内消费者成员发生变化导致的 Rebalance。组内成员发生变化无非就两种情况,一种是有新的消费者加入,通常是我们为了提高消费速度增加了消费者数量,比如增加了消费线程或者多部署了一份消费程序,这种情况可以认为是正常的;另一种是有消费者退出,这种情况多是和我们消费端代码有关,是我们要重点避免的。

正常情况下,每个消费者都会定期向组协调器 Group Coordinator 发送心跳,表明自己还在存活,如果消费者不能及时的发送心跳,组协调器会认为该消费者已经“死”了,就会导致消费者离组引发 Rebalance 问题。这里涉及两个消费端参数:session.timeout.ms 和 heartbeat.interval.ms,含义分别是组协调器认为消费组存活的期限,和消费者发送心跳的时间间隔,其中 heartbeat.interval.ms 默认值是3s,session.timeout.ms 在 0.10.1 版本之前默认 30s,之后默认 10s。另外,0.10.1 版本还有两个值得注意的地方:

  • 从该版本开始,Kafka 维护了单独的心跳线程,之前版本中 Kafka 是使用业务主线程发送的心跳。

  • 增加了一个重要的参数 max.poll.interval.ms,表示 Consumer 两次调用 poll 方法拉取数据的最大时间间隔,默认值 5min,对于那些忙于业务逻辑处理导致超过 max.poll.interval.ms 时间的消费者将会离开消费组,此时将发生一次 Rebalance。

此外,如果 Consumer 端频繁 FullGC 也可能会导致消费端长时间停顿,从而引发 Rebalance。因此,我们总结如何避免消费组 Rebalance 问题,主要从以下几方面入手:

  • 合理配置 session.timeout.ms 和 heartbeat.interval.ms,建议 0.10.1 之前适当调大 session 超时时间尽量规避 Rebalance。

  • 根据实际业务调整 max.poll.interval.ms,通常建议调大避免 Rebalance,但注意 0.10.1 版本之前没有该参数。

  • 监控消费端的 GC 情况,避免由于频繁 FullGC 导致线程长时间停顿引发 Rebalance。

合理调整以上参数,可以减少生产环境中 Rebalance 发生的几率,提升 Consumer 端的 TPS 和稳定性。

 

6. 总结

本文总结了 Kafka 体系架构、Kafka 消息发送机制、副本机制,Kafka 控制器、消费端 Rebalance 机制等各方面核心原理,通过本文的介绍,相信你已经对 Kafka 的内核知识有了一定的掌握,更多的 Kafka 原理实践后面有时间再介绍。

☞ Ambari 2.7.3.0 安装部署 hadoop 3.1.0.0 集群视频完整版

☞ 【实战】使用 Kettle 工具将 MySQL 数据增量导入到 MongoDB 中

☞ 都快2020年了,ambari自定义服务集成,你还没掌握吗?文末有福利

☞ HBase原理(一):架构理解

☞ HBase二次开发之搭建HBase调试环境,如何远程debug HBase源代码

☞ Kafka消费者 之 指定位移消费

☞ Kylin配置Spark并构建Cube(修订版)

☞ 看完您如果还不明白 Kerberos 原理,算我输!



Tags:Kafka   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言Kafka 中有很多延时操作,比如对于耗时的网络请求(比如 Produce 是等待 ISR 副本复制成功)会被封装成 DelayOperation 进行延迟处理操作,防止阻塞 Kafka请求处理线程。Kafka...【详细内容】
2021-12-27  Tags: Kafka  点击:(1)  评论:(0)  加入收藏
1.1 docker命令直接部署1.1.1 拉取镜像docker pull wurstmeister/zookeeperdocker pull wurstmeister/kafka1.1.2 启动zookeeper容器docker run -d --name myzookeeper -p 2...【详细内容】
2021-11-15  Tags: Kafka  点击:(48)  评论:(0)  加入收藏
一、开篇经过上次文章的铺垫,相信大家对 java 的 NIO 有了一些感性的认识,也初步了解了它的 API 了,可以开始去阅读 Kafka Producer 端的发送消息的部分了。突然想感叹一下,阅读...【详细内容】
2021-08-11  Tags: Kafka  点击:(65)  评论:(0)  加入收藏
Kafka是开源事件流软件,可允许您构建事件驱动系统。虽然有其他指南,但我希望专注于可视化Kafka背后的主要概念。这样,当你阅读其他指南时,你会感到更自信。有那个,让我们开始!基本...【详细内容】
2021-04-30  Tags: Kafka  点击:(285)  评论:(0)  加入收藏
在 KafkaProducer 中大部分的参数都有合理的默认值,一般不需要修改它们。不过了解这些参数可以让我们更合理地使用生产者客户端,其中还有一些重要的参数涉及程序的可用性和性...【详细内容】
2021-02-03  Tags: Kafka  点击:(155)  评论:(0)  加入收藏
消息中间件的应用场景 异步解耦 削峰填谷 顺序收发 分布式事务一致性腾讯应用案例: 主流 MQ 框架及对比 说明 Kafka:整个行业应用广泛 RocketMQ:阿里,从 apache 孵化 Pulsar:雅...【详细内容】
2021-01-26  Tags: Kafka  点击:(208)  评论:(0)  加入收藏
本文要点 在构建应用程序和系统时,我们一直面临的一个挑战是如何有效地在它们之间交换信息,同时保持接口修改的灵活性,而不会对其他地方产生不恰当的影响。 事件提供了一种“金...【详细内容】
2020-11-30  Tags: Kafka  点击:(110)  评论:(0)  加入收藏
Kafka集群安装、配置和启动Kafka需要依赖zookeeper,并且自身集成了zookeeper,zookeeper至少需要3个节点保证集群高可用,下面是在单机linux下创建kafka3个节点伪集群模式。1、下...【详细内容】
2020-11-10  Tags: Kafka  点击:(111)  评论:(0)  加入收藏
背景在大数据时代,存在大量基于数据的业务。数据需要在不同的系统之间流动、整合。通常,核心业务系统的数据存在OLTP数据库系统中,其它业务系统需要获取OLTP系统中的数据。传统...【详细内容】
2020-10-29  Tags: Kafka  点击:(167)  评论:(0)  加入收藏
以前我们讨论的消费组,都是 group 的形式,group 可以自动地帮助消费者分配分区,且在发生异常时,还能自定地进行重平衡(Rebalance)。正常来说,group 帮助用户实现自动监听分区消费,但...【详细内容】
2020-09-19  Tags: Kafka  点击:(105)  评论:(0)  加入收藏
▌简易百科推荐
在互联网的江湖里头,每天都有新鲜事。我们昨天还在聊元宇宙,还没有了解元宇宙是个啥,今天又开始了web3.0,站在时代的浪潮之巅,哪怕我们只是一滴最普通的水,也会随着巨浪此起彼伏,直...【详细内容】
2021-12-24  程序员老王聊科技    Tags:Web3.0   点击:(7)  评论:(0)  加入收藏
社交媒体营销的好处: 我们可以使用社交媒体来定义企业的品牌标识以及提供的产品或服务。 社交媒体可以和不了解我们产品和服务的人群建立关系。 社交媒体可以和用户完成互动...【详细内容】
2021-12-22  搜索引擎和短视频推广    Tags:社交媒体   点击:(12)  评论:(0)  加入收藏
之前我们讲到说,有些网站在复制的时候会提示一个弹窗,我们可以去掉网站的copy事件监听就可以了,但是我们经常用到的一个网站,百度文库,网站不是这样设计的。 可以随便打开一个百...【详细内容】
2021-12-03  追风Office    Tags:百度文库   点击:(9)  评论:(0)  加入收藏
都说现在的年轻人网上冲浪有三大痛点:听歌被版权拒之门外,看剧被超前点播劝退,网盘下载文件被无情限速。不巧,这些痛点我刚好都经历过,手机里光是这些类型的APP都有好几个,这不是...【详细内容】
2021-11-11  黑科技工具圈    Tags:网盘   点击:(46)  评论:(0)  加入收藏
今天跟大家分享9个珍藏已久的网站,满满的黑科技,保证看完会让你感觉发现了新大陆,下面一起来看看吧。1.国际空间站模拟器这个网站满满的科技感,它在线真实模拟了宇宙飞船对接国...【详细内容】
2021-11-01  职场办公技能    Tags:网站   点击:(33)  评论:(0)  加入收藏
好用的网站能够受益一生,一些优质的网站用起来的确可以这样说。今天分享6个资源丰富的网站,质量杠杠的。 1.【字由网】推荐理由:免费商用字体、字体库 当工作中需要制作海报或...【详细内容】
2021-10-27  小杰好厉害呀    Tags:资源网站   点击:(18)  评论:(0)  加入收藏
终于准备离开网络了吗?如果您正在阅读本文,则您的个人信息很可能会向公众公开。我所说的“公共”是指任何地方的每个人。那么,如何将自己从互联网上删除以阻止公司获取您的信...【详细内容】
2021-10-27  编程老妖    Tags:互联网   点击:(25)  评论:(0)  加入收藏
经过上次会议的"洗礼",郝自信对于职场"黑话"已经有了一定的了解和学习,这次会议他表现如何呢? 播放视频看来,黑话作为职场通用语,不仅能让经验丰富的职场人彼此心照不宣,也能让一...【详细内容】
2021-10-26    计算机世界  Tags:互联网   点击:(26)  评论:(0)  加入收藏
从开始分享软件工具到现在,询问百度网盘名不限速下载的问题就没有间断过,看来大家也是苦百度网盘已久,本文咱们就分享一下百度网盘不限速下载的方法。 文章比较长,实际上操作...【详细内容】
2021-10-25  网创智慧库    Tags:百度网盘   点击:(526)  评论:(0)  加入收藏
我们使用电脑查询或寻找素材时,会去浏览器搜索,搜索结果五花八门,要花时间去筛选。但如果去对应的网站搜索,那就方便多了,今天分享6个资源超多的网站,个个都是精品。 【办公资源网...【详细内容】
2021-10-21  小杰好厉害呀    Tags:网站   点击:(28)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条