kafka详解

kafka角色构成

  1. Producer :消息生产者,向kafka broker发消息的客户端;
  2. Consumer :消息消费者,从kafka broker取消息的客户端;
  3. Topic :数据存储在Topic中,可以理解为一个队列;
  4. Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic;
  5. Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。
  6. Offset:partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序;
  7. Segment:partition物理上由多个segment组成,每个Segment存着message信息
  8. Leader:负责给定Partition的所有读取和写入的节点。
  9. Follower:跟随Leader指令的节点被称为Follower。 如果Leader节点宕机,其中一个Follower将通过选举自动成为新的Leader。
  10. Consumer Group (CG):这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。一个topic可以有多个CG。topic的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个partion只会把消息发给该CG中的一个consumer。如果需要实现广播,只要每个consumer有一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic;
  11. Zookeeper:为集群分布式一致性提供服务,以及在早期kafka版本保存消息相关元数据。

kafka的Partition机制(高可用)

Partition机制是kafka的文件存储结构,kafka的高可用就是依赖于这样的文件存储结构。

kafka中的Topic是由多个Partition组成的,Topic是逻辑上的概念,Partition是物理存储上的概念。

分区(Partition)

消息发送时都被发送到一个topic,其本质就是一个目录,而topic是由一些Partition Logs(分区日志)组成,其组织结构如下图所示:
在这里插入图片描述
在这里插入图片描述
kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka。

分区的原因

  1. 负载均衡,方便在集群中扩展。每个Partition可以通过调整以适应它所在的机器,而一个Topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了。
  2. 可以提高并发,因为可以以Partition为单位读写了。
Partition的物理存储与读取

每个partiton在物理上对应一个目录(文件夹),以topic名称+有序序号的形式命名(序号从0开始计,最大为partition数-1)。
在这里插入图片描述
每个分区文件夹下存储这个分区的所有消息(.log)和索引文件(.index)。“.index”索引文件存储大量的元数据,“.log”数据文件存储大量的消息,索引文件中的元数据指向对应数据文件中message的物理偏移地址。一个消息(.log)文件和它的索引文件(.index)是一个Segment。
如下图:
在这里插入图片描述
其中以“.index”索引文件中的元数据[3, 348]为例,在“.log”数据文件表示第3个消息,即在全局partition中表示170410+3=170413个消息,该消息的物理偏移地址为348。

通过offset查找message

那么如何从partition中通过offset查找message呢?大致分为三步:

  1. 找到相应的index文件
  2. 二分查找,找到index文件中对应的位置,得到消息所在的具体位置。
  3. 得到消息。

以上图为例,读取offset=170418的消息,首先查找segment文件,其中 00000000000000000000.index为最开始的文件,第二个文件为00000000000000170410.index(起始偏移为170410+1=170411),而第三个文件为00000000000000239430.index(起始偏移为239430+1=239431),所以这个offset=170418就落到了第二个文件之中。

其他 后续文件可以依次类推,以其实偏移量命名并排列这些文件,然后根据二分查找法就可以快速定位到具体文件位置。其次根据 00000000000000170410.index文件中的[8,1325]定位到00000000000000170410.log文件中的1325的位置进行读取。

Kafka中topic的每个partition有一个预写式的日志文件,虽然partition可以继续细分为若干个segment文件,但是对于上层应用来说可以将 partition看成最小的存储单元(一个有多个segment文件拼接的“巨型”文件),每个partition都由一些列有序的、不可变的消息组成,这些消息被连续的追加到partition中。

消息队列是以log文件的形式存储,消息生产者只能将消息添加到既有的文件尾部,没有任何ID信息用于消息的定位,完全依靠文件内的位移,因此消息的使用者只能依靠文件位移顺序读取消息,这样也就不需要维护复杂的支持随即读取的索引结构。

那如何保证消息是按顺序消费的?

因为kafka可以将一个topic分为多个Partition,因此如果消费者同时取多个Partition的消息时,是无法保证消息的有序性的,但如果每个消费者只拿一个Partition中的消息,那这种消费消息就是有序的。

也就是单个Partition可以保证有序,多个Partition就无法保证。

那如何保证消息均匀的分布到不同的partition中?

选择分区的方式

  1. 已指定Partition,则直接使用该Partition。
  2. 未指定Partition但指定了Key,则通过对Key进行哈希计算得出一个Partition。
  3. Partition和Key都未指定,则轮询选出一个Partition。

生产者在生产数据的时候,可以为每条消息指定Key,这样消息被发送到broker时,会根据分区规则选择被存储到哪一个分区中,如果分区规则设置的合理,那么所有的消息将会被均匀的分布到不同的分区中,这样就实现了负载均衡和水平扩展。分区规则可以自定义,比如将消息的key做了hashcode,然后和分区数(numPartitions)做模运算,使得每一个key都可以分布到一个分区中。

链接:https://www.jianshu.com/p/f8f1d74a75cd

kafka的replication与leader机制(可靠性)

为什么有replication与leader机制

为了保证kafka的可靠性,Kafka每个topic的partition有N个副本。

假如只有一个partition,一旦Broker宕机,其上所有Partition的数据都不可被Consumer消费,同时Producer也不能再将数据存于其上的Partition。

replication机制

为了提高消息的可靠性,Kafka每个topic的partition有N个副本(replication),其中N(大于等于1)是topic的复制因子(replication fator)的个数。
这个时候每个 partition下面就有可能有多个 replication副本,但是这多个replication并不一定分布在一个broker上,有可能存储在其他的broker上,而这时候为了更好的在replication之间复制数据,此时会选出一个leader(leader机制)。

leader机制

而这时候为了更好的在replication之间复制数据,此时会选出一个Leader,Producer和Consumer只与这个Leader交互,其它Replication作为Follower从Leader中复制数据 。
producer会push消息到这个leader,consumer也会从这个leader pull 消息,其他的 replication 只是作为 follower 从leader复制数据,leader负责所有的读写,这样数据的一致性和有序性可以得到保证。

如何选出一个leader

当Leader宕机了,怎样在Follower中选举出新的Leader?
Kafka在Zookeeper中动态维护了一个ISR(in-sync replicas),这个ISR里的所有Replica都跟上了leader,只有ISR里的成员才有被选为Leader的可能。

选举最简单最直观的方案是:谁写进去谁就是leader

所有Follower都在Zookeeper上设置一个Watch,一旦Leader宕机,其对应的ephemeral znode会自动删除,此时所有Follower都尝试创建该节点,而创建成功者(Zookeeper保证只有一个能创建成功)即是新的Leader,其它Replica即为Follower。

链接:https://www.jianshu.com/p/f8f1d74a75cd

Producer写入消息(ACK机制)

  1. Producer发送消息
  2. 找到相应的Topic
  3. 找到Topic下的某个的Partition(方式上面有介绍)
  4. 找到Partition的Leader
  5. 向Leader写入数据,包括index和log文件
  6. Follower从Leader中pull消息,仅log文件,后续follower会异步的同步至磁盘中
  7. Follower写入成功后返回ack,表明写入成功
  8. Leader向Producer返回ACK结果,表明写入成功。
    在这里插入图片描述

异步返回ack

为了提高性能,每个follower在接受到消息之后就会直接返回给leader ack消息,而并非等数据写入到log里(异步),所以,可以认为对于已经commit的数据,只可以保证消息已经存在与所有的replica的内存中,但是不保证已经被持久化到磁盘中。

producer确认写入成功

producer在写入消息后,需要确认是否真正的写入,但是它有三种级别可以选择,向 Kafka 写数据时,producer 设置 ack 是否提交完成:

  • 0:不等待leader replica的返回确认消息
  • 1:Leader 保存成功返回
  • -1:所有备份都保存成功返回

这三种机制,性能依次递减 (producer吞吐量降低1-3倍),数据健壮性则依次递增。

Consumer读取消息

消费的步骤:

  1. Consumer读取消息
  2. 找到对应的Topic
  3. 找到对应的partition
  4. 根据offset查找index文件
  5. 根据index文件查找log文件
  6. 读取消息成功

Consumer采用Pull模式从Broker中读取数据。

为什么选择pull方式

Push模式很难适应消费速率不同的Consumer,因为消息发送速率是由Broker决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成Consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而Pull模式则可以根据Consumer的消费能力以适当的速率消费消息。

对于Kafka而言,Pull模式更合适,它可简化Broker的设计,Consumer可自主控制消费消息的速率,同时Consumer可以自己控制消费方式——即可以批量消费也可以逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。

Pull模式不足之处是,如果Kafka没有数据,消费者可能会陷入循环中,一直等待数据到达。为了避免这种情况,可以Pull请求中设置参数,允许Consumer请求在等待数据到达的“长轮询”中进行阻塞(并且可选地等待到给定的字节数,以确保大的传输大小)。

怎么防止重复消费

kafka是通过偏移量来实现的,每个消息在分区(Partition)中都有一个唯一的偏移量。消费者在消费消息后,会将当前消费的偏移量提交给 Kafka 集群,以便下次从该偏移量继续消费。

Kafka 支持两种偏移量提交方式:

  • 自动提交:消费者定期自动提交偏移量,默认时间为 5 秒。这种方式简单易用,但可能会导致消息的重复消费,因为如果消费者在提交偏移量之前崩溃,那么重启后的消费者会从上次提交的偏移量重新开始消费。
  • 手动提交:消费者在代码中显式地提交偏移量。这种方式更加灵活,可以精确控制偏移量的提交时机,从而避免消息的重复消费。

Consumer Group

因为有多个Partition,Producer有三种方式选择写入哪个Partition,Consumer也需要有自己的方式来读取Partition,这就是Consumer Group。

Consumer是以Consumer Group的方式工作,由一个或者多个Consumer组成一个Group,共同消费一个Topic每个Partition在同一时间只能由Group中的一个Consumer读取,但是一个Consumer可以读取多个Partition,多个Group可以同时消费同一个Partition。某个Consumer读取某个Partition,也可以叫做某个Consumer是某个Partition的拥有者。

在这种情况下,Consumer可以通过水平扩展的方式同时读取大量的消息。另外,如果一个Consumer失败了,那么Group中其它的Consumer会自动负载均衡读取之前失败的Consumer负责的Partition。

在这里插入图片描述

链接:https://www.jianshu.com/p/f7725749e90d

Kafka的持久特性

Kafka的内存管理模式十分激进,基本的意思就是不管理。。。kafka不在JVM进程内部维护消息Cache,消息直接从文件中读写,使用文件存储消息,完全依赖操作系统在文件系统层面的cache,这就直接决定kafka在性能上严重依赖文件系统的本身特性

这样避免在JVM中管理Cache带来的额外数据结构开销和GC带来的性能代价。

kafka优化的方式主要有两种:

  1. 顺序读写
  2. 批量处理

无论任何 OS 下,对文件系统本身的优化几乎没有可能,kafka 是对日志文件进行 append 操作,顺序读写。因此磁盘检索的开支是较小的。
同时为了减少磁盘写入的次数,broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数,最大化利用文件系统的Cache机制和规避文件读写相对内存读写的性能代价。

链接:https://www.jianshu.com/p/f8f1d74a75cd

Kafka集群管理

Kafka也是一种服务,它也是由Zookeeper进行管理的。如下:
#### Producer生产过程
kafka的存活条件包括两个条件:

  1. 节点必须可以维护和zookeeper的连接,zookeeper通过心跳机制检查每个节点的连接(通过zookeeper的心跳机制)
  2. 如果节点是个follower,它必须能及时的同步leader的写操作,延时不能太久。

leader会跟踪与其保持着同步的replica列表简称ISR,(in-sync replica),如果一个follower宕机或是落后太多,leader就会把它从ISR中移除掉。

这里指的落后太多是说 follower复制的消息落后的超过了预设值,(该值可在KAFKA_HOME/config/server.properties中通过replica.lag.time.max.ms来配置,其默认值是10000)没有向leader发起fetch请求。

Kafka为什么会出现重复消费的情况

因为kafka的三个特性:

  1. kafka消费后的数据不会立马删除。
  2. kafka的consumer根据offset来确定从哪里开始消费。
  3. 如果一个Consumer失败,Consumer Group会找一个其它的Consumer,顶替它继续读取Partition。

具体过程为:

  1. kafka的consumer会从broker里面取出一批数据,数量由max.poll.records设置,给消费线程进行消费。
  2. 由于取出的一批消息数量太大,consumer在规定的时间之内没有消费完成,这个时间是max.poll.interval.ms设置的
  3. consumer coordinator 会由于没有接受到心跳而挂掉,并且自动提交offset失败(这时出现一些日志)
  4. Consumer Group会重新分配partition给其他的Consumer
  5. 由于自动提交offset失败,导致重新分配了partition的Consumer 又重新消费之前的一批数据
  6. 接着consumer重新消费,又出现了消费超时,无限循环下去。

链接:https://www.jianshu.com/p/98697293d827

解决方案:

  1. 增大max.poll.interval.ms,默认是300s,不推荐
  2. 减小max.poll.records,推荐
  3. 使用手动提交offset方法consumer.commitSync()/consumer.commitAsync(),在不确定数据情况下推荐
  4. 一个线程消费kafka数据,消费后将数据传给其他线程处理

Kafka的offset存在哪里?

Kafka版本[0.10.1.1],已默认将消费的 offset 迁入到了 Kafka 一个名为 __consumer_offsets 的Topic中。其实,早在 0.8.2.2 版本,已支持存入消费的 offset 到Topic中,只是那时候默认是将消费的 offset 存放在 Zookeeper 集群中。那现在,官方默认将消费的offset存储在 Kafka 的Topic中,同时,也保留了存储在 Zookeeper 的接口,通过 offsets.storage 属性来进行设置。

原文链接:https://blog.csdn.net/llflilongfei/article/details/81483509

Kafka怎么防止重复消费

  1. 业务上的幂等性处理,例如对消息的messageId做幂等、去重,或者是对业务主键做幂等
  2. 手动提交偏移量offset来保证,而不是让消费者自动隔3s提交一次offset
  3. 使用redis缓存,记录消费消息的内容等,防止不同messageId的重复消费
  4. 使用kafka的事务

Kafka为什么会出现数据丢失的情况

这是在ACK机制中,producer选择0、1的情况下会出现。

acks设置为0时,producer不和Kafka集群进行消息接受确认,当网络发生异常或者producer发生异常(producer异步发送消息的情况下死机)等情况时,存在消息丢失的可能;

acks设置为1时,Leader副本接收成功,Kafka集群就返回成功确认信息,而Follower副本可能还在同步。这时Leader副本突然出现异常,新Leader副本(原Follower副本)未能和其保持一致,就会出现消息丢失的情况。

怎么处理数据丢失的问题呢:

  • 副本机制:Kafka 通过副本机制(Replication)来保证消息的高可用性和不丢失。每个分区可以配置多个副本,其中一个为主副本(Leader),其余为从副本(Follower)。主副本负责读写操作,从副本同步主副本的数据。
  • 持久化存储:Kafka 将消息持久化存储在磁盘上,确保即使在节点故障后也能恢复数据。每个分区的消息以段文件(Segment File)的形式存储,每个段文件包含一定数量的消息。Kafka 还提供了多种日志清理策略,如删除过期消息(Delete)和压缩日志(Compact),以确保磁盘空间的有效利用。
  • 生产者重试机制,生产者重试机制Kafka 生产者在发送消息时,如果遇到网络故障或节点故障,可以自动重试。通过配置 retries 和 retry.backoff.ms 参数,可以控制重试次数和重试间隔时间。这样可以确保消息在遇到临时故障时不会丢失。

Kafka消息积压怎么解决

  1. 增加消费者实例:如果消费者的处理能力不足,可以通过水平扩展增加消费者实例来提高消费速率。
  2. 增加topic的Partition,这样多个Partition可以被多个消费者并行消费
  3. 优化消费者的效率:优化消费者的处理逻辑,减少处理时间,提高消费效率。例如:优化代码、异步线程处理、多线程调用RPC接口、批量处理消息等等。注意kafka消费者是单线程处理,消费者不能自动扩展消费的线程数,只能通过assign方法手动分配分区给线程。
  4. 生产者限流:对生产者进行限流,防止消息生产过快。

Kafka的分区重平衡机制

Kafka的分区重平衡机制是确保消费者组中的分区在消费者之间合理分配的关键过程。

它的触发时机为:

  • 消费者实例变化:当消费者组中有新的消费者实例加入或现有消费者实例退出时,会触发重平衡,以重新分配分区给消费者。
  • 分区数量变化:如果Kafka主题的分区数量发生变化(增加或减少),也会导致重平衡。
  • 消费者组协调器变更:如果负责管理消费者组的协调器节点发生故障或变更,也可能触发重平衡。
  • 消费者会话超时:如果消费者没有在指定的会话超时时间内向Kafka发送心跳,Kafka会认为该消费者已失效,从而触发重平衡。

它的过程为:

  1. 停止消费:在重平衡开始时,所有消费者实例会停止消费并提交当前的偏移量,以确保在重平衡期间不会丢失数据。
  2. 协调器选择:Kafka 集群中的一个节点会被选为消费者组的协调器,负责管理该组的重平衡过程。
  3. 分区分配策略:协调器根据配置的分区分配策略(如 Range、RoundRobin、Sticky 等)决定如何将分区分配给消费者。每种策略有不同的分配逻辑:Range:按主题分配连续的分区范围给消费者。RoundRobin:以循环的方式分配分区。Sticky:尽量保持分区分配的稳定性,减少分区的重新分配。
  4. 分配分区:协调器将分配结果发送给每个消费者实例,告知它们需要消费哪些分区。
  5. 恢复消费:消费者实例根据新的分配结果恢复消费。

它带来的影响:

  • 消费中断:在重平衡期间,消费者会暂时停止消费消息,直到新的分区分配完成。这可能导致消费延迟。
  • 性能影响:频繁的重平衡会导致系统性能下降,因为每次重平衡都需要重新分配分区和恢复消费状态。
  • 状态恢复开销:消费者在重平衡后需要重新初始化状态(如读取偏移量),这可能增加延迟和资源消耗。

kafka延迟消息怎么实现的

方案一:kafka本身 + 固定时间队列分区

Kafka 本身不支持延迟队列,但可以通过以下方式实现。

实现原理

  • 将消息按延迟时间分区,每个分区对应一个延迟时间段。
  • 使用定时任务消费对应分区的消息。

实现步骤

  • 创建多个主题或分区,每个分区对应一个延迟时间段。
  • 生产者根据延迟时间将消息发送到对应的分区。
  • 消费者定时轮询分区,消费到期的消息。

优点

  • 高吞吐量。
  • 适合大数据场景。

缺点

  1. 实现复杂。
  2. 分区数量可能过多。

方案二:kafka本身 + 随机延迟时间

可以用时间轮(TimingWheel) 算法来实现随机延迟消息。

时间轮是指有一个环形的队列,例如60s,设置一个有60个槽位的环形队列,然后每一秒向前移动一个指针,就轮询槽位里的所有延迟消息任务,并发送。

单层时间轮受限于槽位数(如60秒的轮子无法处理1小时延迟),因此引入 分层设计:

  1. 高层轮:粗粒度时间(如分钟、小时)。
  2. 低层轮:细粒度时间(如秒、毫秒)。
  3. 任务降级:当高层轮的任务到期后,将其重新插入低层轮。

方案二:基于redis

Redis 可以通过有序集合(Sorted Set)实现延迟队列。

实现原理

  • 使用有序集合存储消息,score 为消息的到期时间戳,value 为消息内容。
  • 定时轮询有序集合,获取当前时间之前到期的消息。

实现步骤

  1. 添加消息:ZADD delay_queue <到期时间戳> <消息内容>
  2. 消费消息:使用 ZRANGEBYSCORE 获取到期的消息,删除已处理的消息。

优点

  • 简单易实现。
  • 性能较高。

缺点

  • 需要定时轮询,可能产生延迟。
  • 消息量过大时,有序集合的性能会下降。

方案三:基于 RabbitMQ 的实现

使用死信队列(DLX)

  1. 创建一个普通队列,设置 x-message-ttl(消息过期时间)和 x-dead-letter-exchange(死信交换机)。
  2. 定时扫描队列,如过发现消息过期,消息会被转发到死信队列,消费者从死信队列消费。

优点

原生支持,可靠性高。无需额外开发。

缺点

配置较复杂。

kafka数据存在磁盘上怎么保证它的高吞吐量呢(怎么还很快呢)

Kafka通过以下关键设计和技术优化,在数据持久化到磁盘的同时实现高吞吐量:

  1. 顺序I/O写入:Kafka将消息以**顺序追加(append-only)**的方式写入磁盘,充分利用磁盘顺序读写的高效性(远高于随机读写)。每个分区(Partition)对应一个物理日志文件,新消息始终追加到文件末尾,避免磁盘寻道开销。
  2. 页缓存(Page Cache)优化:页缓存是操作系统内核中的一种缓存机制,Kafka直接利用操作系统的页缓存。生产者写入消息时,会先将数据先写入页缓存,不直接写入磁盘,而是等操作系统在合适的时机(例如页缓存满或定时任务)将数据异步刷入磁盘,减少了直接磁盘I/O次数。消息者读取消息时,如果需要读取的数据已经在页缓存中,Kafka 直接从内存中读取数据,无需访问磁盘,而因为afka 的消息是顺序写入、顺序读取,因此页缓存的命中率非常高。
  3. 零拷贝(Zero-Copy)技术:使用sendfile系统调用,将数据从页缓存直接发送到网络套接字,避免了数据在用户空间和内核空间之间的拷贝。
  4. 分区(Partition & Segment)设计:opic分为多个Partition,分布在不同Broker,并行读写提升吞吐。
  5. 稀疏索引管理:每个Segment配套.index和.timeindex文件,支持快速定位消息偏移量。
  6. 批量处理与压缩:生产者批量发送:通过batch.size和linger.ms配置,合并多条消息为单个请求,减少网络和磁盘I/O次数。消费者批量拉取:通过max.poll.records一次获取多条消息,提升处理效率。消息压缩:支持GZIP、Snappy、LZ4等算法,减少传输和存储的数据量。
  7. 网络与序列化优化:二进制协议:Kafka自定义高效二进制协议,减少序列化开销。高效序列化格式:如Avro、Protobuf,降低消息体积,提升网络利用率。

kafka为什么topic/分区太多会导致吞吐量大幅下降

当kafka的topic太多,例如有几百数千个的时候,可能会导致吞吐量大幅下降。原因是:

  1. ZooKeeper压力 : 每个 Topic/分区的创建、删除、Leader 选举等操作都会触发元数据变更(如分区状态、ISR 列表)。ZK 需要处理大量 Watcher 通知和持久化操作,成为瓶颈。
  2. 磁盘随机 I/O 增加:大量 Topic 导致日志文件分散,磁盘磁头频繁寻道(尤其是机械硬盘),破坏 Kafka 依赖的顺序写入优势。
  3. 分区与文件句柄:举例:1 万个 Topic,每个 Topic 2 个分区 → 至少 2 万个分区 → 假设每个分区打开 5 个文件句柄 → 10 万+ 文件句柄。超出操作系统限制(默认 ulimit -n 通常为 1024)时,Broker 会报 Too many open files 错误。
  4. 页缓存(Page Cache)污染:Kafka 依赖操作系统的页缓存加速消息读写,但 Topic 过多时,不同 Topic 的数据竞争有限的缓存空间。

转自链接:https://www.jianshu.com/p/f8f1d74a75cd

其他参考:https://blog.csdn.net/wanglei_storage/article/details/82692413

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值