Flink架构及其工作原理

flink实战--flink原理解析 Flink出现的背景 我们知道目前流处理的主要流行的计算引擎有,Storm,SparkStreaming。但是这个两个计算引擎都有自己的局限性。Storm实现了低延迟,但是目前还没有实现高吞吐,也不能在故障发生的时候准确的处理计算状态(将数据从一个事件保存到另一个事件的,这些保留下来的是数据较计算状态),同时也不能实现exactly-once。SparkStreaming通过微批处理方法............ 阅读详情

 

 

目录

 

System Architecture

分布式系统需要解决:分配和管理在集群的计算资源、处理配合、持久和可访问的数据存储、失败恢复。Fink专注分布式流处理。

Components of a Flink Setup

  • JobManager :接受application,包含StreamGraph(DAG)、JobGraph(logical dataflow graph,已经进过优化,如task chain)和JAR,将JobGraph转化为ExecutionGraph(physical dataflow graph,并行化),包含可以并发执行的tasks。其他工作类似Spark driver,如向RM申请资源、schedule tasks、保存作业的元数据,如checkpoints。如今JM可分为JobMaster和ResourceManager(和下面的不同),分别负责任务和资源,在Session模式下启动多个job就会有多个JobMaster。
  • ResourceManager:一般是Yarn,当TM有空闲的slot就会告诉JM,没有足够的slot也会启动新的TM。kill掉长时间空闲的TM。
  • TaskManager类似Spark的executor,会跑多个线程的task、数据缓存与交换。
  • Dispatcher(Application Master)提供REST接口来接收client的application提交,它负责启动JM和提交application,同时运行Web UI。

task是最基本的调度单位,由一个线程执行,里面包含一个或多个operator。多个operators就成为operation chain,需要上下游并发度一致,且传递模式(之前的Data exchange strategies)是forward。

slot是TM的资源子集。结合下面Task Execution的图,一个slot并不代表一个线程,它里面并不一定只放一个task。多个task在一个slot就涉及slot sharing group。一个jobGraph的任务需要多少slot,取决于最大的并发度,这样的话,并发1和并发2就不会放到一个slot中。Co-Location Group是在此基础上,数据的forward形式,即一个slot中,如果它处理的是key1的数据,那么接下来的task也是处理key1的数据,此时就达到Co-Location Group。

尽管有slot sharing group,但一个group里串联起来的task各自所需资源的大小并不好确定。阿里日常用得最多的还是一个task一个slot的方式。

Session模式(上图):预先启动好AM和TM,每提交一个job就启动一个Job Manager并向Flink的RM申请资源,不够的话,Flink的RM向YARN的RM申请资源。适合规模小,运行时间短的作业。./bin/flink run ./path/to/job.jar

Job模式:每一个job都重新启动一个Flink集群,完成后结束Flink,且只有一个Job Manager。资源按需申请,适合大作业。./bin/flink run -m yarn-cluster ./path/to/job.jar

下面是简单例子,详细看官网。

# 启动yarn-session,4个TM,每个有4GB堆内存,4个slot
cd flink-1.7.0/
./bin/yarn-session.sh -n 4 -jm 1024m -tm 4096m -s 4
# 启动作业
./bin/flink run -m yarn-cluster -yn 4 -yjm 1024m -ytm 4096m ./examples/batch/WordCount.jar

细节取决于具体环境,如不同的RM

Application Deployment

Framework模式:Flink作业为JAR,并被提交到Dispatcher or JM or YARN。

Library模式:Flink作业为application-specific container image,如Docker image,适合微服务。

Task Execution

作业调度:在流计算中预先启动好节点,而在批计算中,每当某个阶段完成计算才启动下一个节点。

资源管理:slot作为基本单位,有大小和位置属性。JM有SlotPool,向Flink RM申请Slot,FlinkRM发现自己的SlotManager中没有足够的Slot,就会向集群RM申请。后者返回可用TM的ip,让FlinkRM去启动,TM启动后向FlinkRM注册。后者向TM请求Slot,TM向JM提供相应Slot。JM用完后释放Slot,TM会把释放的Slot报告给FlinkRM。在Blink版本中,job模式会根据申请slot的大小分配相应的TM,而session模式则预先设置好TM大小,每有slot申请就从TM中划分相应的资源。

任务可以是相同operator (data parallelism),不同 operator (task parallelism),甚至不同application (job parallelism)。TM提供一定数量的slots来控制并行的任务数。

上图A和C是source function,E是sink function,小数字表示并行度。

一个TM是一个JVM进程,它通过多线程完成任务。线程的隔离不太好,一个线程失败有可能导致整个TM失败。

Highly-Available Setup

从失败中恢复需要重启失败进程、作业和恢复它的state。

当一个TM挂掉而RM又无法找到空闲的资源时,就只能暂时降低并行度,直到有空闲的资源重启TM。

当JM挂掉就靠ZK来重新选举,和找到JM存储到远程storage的元数据、JobGraph。重启JM并从最后一个完成的checkpoint开始。

JM在执行期间会得到每个task checkpoints的state存储路径(task将state写到远程storage)并写到远程storage,同时在ZK的存储路径留下pointer指明到哪里找上面的存储路径。

背压

数据涌入的速度大于处理速度。在source operator中,可通过Kafka解决。在任务间的operator有如下机制应对:

Local exchange:task1和2在同一个工作节点,那么buffer pool可以直接交给下一个任务,但下一个任务task2消费buffer pool中的信息速度减慢时,当前任务task1填充buffer pool的速度也会减慢。

Remote exchange:TM保证每个task至少有一个incoming和一个outgoing缓冲区。当下游receiver的处理速度低于上有的sender的发送速度,receiver的incoming缓冲区就会开始积累数据(需要空闲的buffer来放从TCP连接中接收的数据),当挤满后就不再接收数据。上游sender利用netty水位机制,当网络中的缓冲数据过多时暂停发送。

TM负责数据在tasks间的转移,转移之前会存储到buffer(这又变回micro-batches)。每个TM有32KB的网络buffer用于接收和发送数据。如果sender和receiver在不同进程,那么会通过操作系统的网络栈来通信。每对TM保持permanent TCP连接来交换数据。每个sender任务能够给所有receiving任务发送数据,反之,所有receiver任务能够接收所有sender任务的数据。TM保证每个任务都至少有一个incoming和outgoing的buffer,并增加额外的缓冲区分配约束来避免死锁。

如果sender和receiver任务在同一个TM进程,sender会序列化结果数据到buffer,如果满了就放到队列。receiver任务通过队列得到数据并进行反序列化。这样的好处是解耦任务并允许在任务中使用可变对象,从而减少了对象实例化和垃圾收集。一旦数据被序列化,就能安全地修改。而缺点是计算消耗大,在一些条件下能够把task穿起来,避免序列化。(C10)

Flow Control with Back Pressure

receiver放到缓冲区的数据变为队列,sender将要发送的数据变为队列,最后sender减慢发送速度。

Event Time Processing

event time处理的数据必须有时间戳(Long unix timestamp)并定义了watermarks。watermark是一种特殊的records holding a timestamp long value。它必须是递增的(防止倒退),有一个timestamp t(下图的5),暗示所有接下来的数据都会大于这个值。后来的,小于这个值,就被视为迟来数据,Flink有其他机制处理。

Watermarks and Event Time

WM在Flink是一种特殊的record,它会被operator tasks接收和释放。

tasks有时间服务来维持timers(timers注册到时间服务上),在time-window task中,timers分别记录了各个window的结束时间。当任务获得一个watermark时,task会根据这个watermark的timestamp更新内部的event-time clock。任务内部的时间服务确定所有timers时间是否小于watermark的timestamp,如果大于则触发call-back算子来释放记录并返回结果。最后task还会将更新的event-time clock的WM进行广播。(结合下图理解)

只有ProcessFunction可以读取和修改timestamp或者watermark(The ProcessFunction can read the timestamp of a currently processed record, request the current event-time of the operator, and register timers)。下面是PF的行为。

当收到WM大于所有目前拥有的WM,就会把event-time clock更新为所有WM中最小的那个,并广播这个最小的WM。即便是多个streams输入,机制也一样,只是增加Paritition WM数量。这种机制要求获得的WM必须是累加的,而且task必须有新的WM接收,否则clock就不会更新,task的timers就不会被触发。另外,当多个streams输入时,timers会被WM比较离散的stream主导,从而使更密集的stream的state不断积累。

Timestamp Assignment and Watermark Generation

当streaming application消化流时产生。Flink有三种方式产生:

  • SourceFunction:产生的record带有timestamp,一些特殊时点产生WM。如果SF暂时不再发送WM,则会被认为是idle。Flink会从接下来的watermark operators中排除由这个SF生产的分区(上图有4个分区),从而解决timer不触发的问题。
  • AssignerWithPeriodicWatermarks 提取每条记录的timestamp,并周期性的查询当前WM,即上图的Partition WM。
  • AssignerWithPunctuatedWatermarks 可以从每条数据提取WM。

上面两个User-defined timestamp assignment functions通常用在source operator附近,因为stream一经处理就很难把握record的时间顺序了。所以UDF可以修改timestamp和WM,但在数据处理时使用不是一个好主意。

State Management

由任务维护并用于计算函数结果的所有数据都属于任务的state。其实state可以理解为task业务逻辑的本地或实例变量。

在Flink,state总是和特定的operator关联。operator需要注册它的state,而state有两种类型:

  • Operator State:由同一并行任务处理的所有记录都可以访问相同的state,而其他的task或operator不能访问,即一个task专属一个state。这种state有三种primitives
    • List State represents state as a list of entries.
    • Union List State同上,但在任务失败和作业从savepoint重启的行为不一样
    • Broadcast State(v1.5) 同样一个task专属一个state,但state都是一样的(需要自己注意保持一致,对state更新时,实际上只对当前task的state进行更新。只有所有task的更新一样时,即输入数据一样(一开始广播所以一样,但数据的顺序可能不一样),对数据的处理一样,才能保证state一样)。这种state只能存储在内存,所以没有RockDB backend。
  • Keyed State:相同key的record共享一个state。
    • Value State:每个key一个值,这个值可以是复杂的数据结构.
    • List State:每个key一个list
    • Map State:每个key一个map

上面两种state的存在方式有两种:raw和managed,一般都是用后者,也推荐用后者(更好的内存管理、不需造轮子)。

State Backends

state backend决定了state如何被存储、访问和维持。它的主要职责是本地state管理和checkpoint state到远程。在管理方面,可选择将state存储到内存还是磁盘。checkpoint方面在C8详细介绍。

MemoryStateBackend, FsStateBackend, RocksDBStateBackend适合越来越大的state。都支持异步checkpoint,其中RocksDB还支持incremental的checkpoint。

  • 注意:As RocksDB’s JNI bridge API is based on byte[], the maximum supported size per key and per value is 2^31 bytes each. IMPORTANT: states that use merge operations in RocksDB (e.g. ListState) can silently accumulate value sizes > 2^31 bytes and will then fail on their next retrieval. This is currently a limitation of RocksDB JNI.

Scaling Stateful Operators

Flink会根据input rate调整并发度。对于stateful operators有以下4种方式:

  • keyed state:根据key group来调整,即分为同一组的key-value会被分到相同的task

  • list state:所有list entries会被收集并重新均匀分布,当增加并发度时,要新建list

  • union list state:增加并发时,广播整个list,所以rescaling后,所有task都有所有的list state。

  • broadcast state

Checkpoints, Savepoints, and State Recovery

Flink’s Lightweight Checkpointing Algorithm

在分布式开照算法Chandy-Lamport的基础上实现。有一种特殊的record叫checkpoint barrier(由JM产生),它带有checkpoint ID来把流进行划分。在CB前面的records会被包含到checkpoint,之后的会被包含在之后的checkpoint。

当source task收到这种信息,就会停止发送recordes,触发state backend对本地state的checkpoint,并广播checkpoint ID到所有下游task。当checkpoint完成时,state backend唤醒source task,后者向JM确定相应的checkpoint ID已经完成任务。

当下游获得其中一个CB时,就会暂停处理这个CB对应的source的数据(完成checkpoint后发送的数据),并将这些数据存到缓冲区,直到其他相同ID的CB都到齐,就会把state(下图的12、8)进行checkpoint,并广播CB到下游。直到所有CB被广播到下游,才开始处理排队在缓冲区的数据。当然,其他没有发送CB的source的数据会继续处理。

最后,当所有sink会向JM发送BC确定checkpoint已完成。

这种机制还有两个优化:

  • 当operator的state很大时,复制整个state并发送到远程storage会很费时。而RocksDB state backend支持asynchronous and incremental的checkpoints。当触发checkpoint时,backend会快照所有本地state的修改(直至上一次checkpoint),然后马上让task继续执行。后台线程异步发送快照到远程storage。
  • 在等待其余CB时,已经完成checkpoint的source数据需要排队。但如果使用at-least-once就不需要等了。但当所有CB到齐再checkpoint,存储的state就已经包含了下一次checkpoint才记录的数据。(如果是取最值这种state就无所谓)

Recovery from Consistent Checkpoints

上图队列中的7和6之所以能恢复,取决于数据源是否resettable,如Kafka,不会因为发送信息就把信息删除。这才能实现处理过程的exactly-once state consistency(严格来讲,数据还是被重复处理,但是在读档后重复的)。但是下游系统有可能接收到多个结果。这方面,Flink提供sink算子实现output的exactly-once,例如给checkpoint提交records释放记录。另一个方法是idempotent updates,详细看C7。

Savepoints

checkpoints加上一些额外的元数据,功能也是在checkpoint的基础上丰富。不同于checkpoints,savepoint不会被Flink自动创造(由用户或者外部scheduler触发创造)和销毁。savepoint可以重启不同但兼容的作业,从而:

  • 修复bugs进而修复错误的结果,也可用于A/B test或者what-if场景。
  • 调整并发度
  • 迁移作业到其他集群、新版Flink

也可以用于暂停作业,通过savepoint查看作业情况。

【推荐系统算法实战】Flink 架构及其工作原理 目录 System Architecture Data Transfer in Flink Event Time Processing State Management Checkpoints, Savepoints, and State Recovery System Architecture 分布式系统需要解决:分配和管理在集群的计算资源、处理配合、持久和可访问的数据存储、... 阅读详情

相关推荐

SpringCloudGateway网关高频20个面试题整理

Spring Cloud Gateway 是基于 Spring 5、Project Reactor 和 Spring Boot 2 构建的 API 网关,提供动态路由、监控、弹性、安全等核心功能。Spring Cloud Gateway 是 Spring Cloud 生态中的 API 网关,用于微服务架构中的请求路由、负载均衡、限流、鉴权等。实现@Component@Override// 逻辑处理。

u012024132的专栏 1115

Flink基本原理剖析讲解

Flink是一个批处理和流处理结合的统一计算框架,其核心是一个提供了数据分发以及并行化计算的流数据处理引擎。它的最大亮点是流处理,是业界最顶级的开源流处理引擎。Flink最适合的应用场景是低时延的数据处理(Data Processing)场景:高并发pipeline处理数据,时延毫秒级,且兼具可靠性。Flink技术栈如下图所示。DataStreamCheckpoint窗口JobPipeline 配置表。

hello.reader 1万+

Java开发的企业排班系统

Java开发的企业排班系统,用了springboot框架,人脸识别是采用百度接口的方式实现

自学网站s.jf3q.com,菜鸟的成长之路 6013

大数据计算域——Flink计算引擎

Apache Flink 是一个强大的开源框架和分布式处理引擎,专门用于对无界和有界数据流进行有状态计算。Flink 支持高吞吐量、低延迟的实时数据流处理,同时也能够高效地处理批处理任务。其核心特点包括事件时间处理、有状态操作、容错机制,以及能够在各种常见的集群环境中运行,如 Hadoop、Kubernetes 和自定义集群。Flink 还具有高度可扩展性,能够处理从小规模到大规模的数据集,同时保持较低的内存占用和高效的计算速度。Flink 常用于实时分析、数据管道、流式 ETL、机器学习等场景,广泛应用于

庄小焱 4678

OpenCV和Halcon分别实现彩色图像的白平衡效果

实现白平衡算法中的灰度世界法,能有效改善图像发红/发蓝/发绿的现象 1、OpenCV #include <opencv2/opencv.hpp> using namespace cv; int main() { Mat g_srcImage,dstImage; vector<Mat> g_vChannels; g_srcImage = im...

$firecat利白的代码足迹$ 2830

在天问block环境下LU-ASR01与51单片机的基础运用

正确步骤将模块和电脑连接后,仍然显示未连接(如4.1中图片中的编译下载后面显示未连接),可以尝试关闭软件后尝试,如果仍然不行换一条type-c线再试;以上方式都不行,可能要怀疑ASR01硬件损坏,跟商家联系!!!

BUG7073的博客 1万+

Flink原理解析

1.Flink是什么 ​Flink是新一代分布式流式计算引擎,用于对海量数据进行实时处理和计算,具备快速容错(支持每条消息恰好处理一次)、流批一体、低延时、支持数据乱序的特点。 ​ ​Flink的主要应用场景如下: 数据分析场景:流式计算平台相对于传统的基于批的计算平台具备最大优点就是实时性,典型的应用场景就是淘宝的双十一大屏和一些实时性要求比较高的数据看班 事件驱动场景:相比于MetaQ或其他消息队列,​Flink可以基于​Flink Sql或者其他API进行一些复杂的计算或者过滤操作,比如基于用户行为

qq_35488769的博客 4565

1. 如何使用扣子工作流一键生成儿童绘本(可以发到小红书,微信公众号图文等)

本文介绍了使用扣子智能体一键生成儿童绘本的工作流。用户只需输入绘本主题,系统会自动生成Markdown格式内容,可直接发布到优快云、小红书等平台,还支持推送至微信公众号草稿箱。操作步骤包括:访问扣子网站导入工作流源代码,输入主题试运行,查看实时进度和最终输出结果。工作流编辑器会显示每个节点的执行状态和耗时,支持通过"查看日志"功能回溯历史运行记录。文末附有工作流源代码下载链接。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1251

Flink-原理+知识点+总结-高手力作,超级经典

Flink四大基石分别是:Time (时间)、Window(窗口)、State (状态)、Checkpoint(检查点)

wuzk2009的专栏 1533

面向 Cloud Foundry、ABAP On-Prem 与 ABAP Cloud 的 SAP Fiori 开发全景:工具、流程与实战案例

SAP Fiori开发实战:基于BAS的一站式工程化方法 摘要: 本文系统梳理SAP Business Application Studio(BAS)在Fiori开发中的核心能力,提供三种典型后端(Cloud Foundry/ABAP On-Prem/ABAP Cloud)的对接方案。BAS通过预置Fiori Tools套件、MTA部署工具等扩展,支持从模板创建、可视化建模到构建部署的全流程。文章包含CAP+RAP服务对接、ABAP标准应用扩展等实战案例,重点解析注解驱动开发、Adaptation Proj

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 257

生成EtherCAT从站XML图片信息方法

详细介绍了EtherCAT从站XML文件中图片元素的生成方法。

kevin1499的博客 2130

Flink 架构及其工作原理

转载自:https://www.cnblogs.com/code2one/category/1331180.html 目录 System Architecture Data Transfer in Flink Event Time Processing State Management Checkpoints, Savepoints, and State Recovery System Architecture 分布式系统需要解决:分配和管理在集群的计算资源、处理配合、持久和可访问...

张伟的专栏 510

【RT-DETR涨点改进】小目标检测、特征融合改进篇 | TGRS 2024 | RT-DETR引入CSFM上下文选择融合模块,适合多模态检测、红外小目标检测、小目标检测、遥感目标检测等任务有效涨点

本文提出了一种改进RT-DETR目标检测模型的方法,通过引入上下文选择融合模块(CSFM)显著提升复杂背景下小目标和低对比度目标的检测精度。CSFM模块采用多尺度特征融合和动态特征选择机制,有效增强模型的全局上下文感知能力,同时保留细节特征。该模块通过通道减少机制和空间信息调节,实现从粗到细的特征聚合,减少误报并提高轮廓检测精度。实验表明,改进后的RT-DETR模型在多个基准测试中表现优异,计算效率与检测精度均优于现有方法。文章还详细介绍了模块配置和yaml文件修改方法,为模型部署提供指导。

Ai缝合怪、助力小伙伴高效发论文 1726

flink运行原理_Flink架构及其工作原理

目录System Architecture分布式系统需要解决:分配和管理在集群的计算资源、处理配合、持久和可访问的数据存储、失败恢复。Fink专注分布式流处理。Components of a Flink SetupJobManager :接受application,包含StreamGraph(DAG)、JobGraph(logical dataflow graph,已经进过优化,如task cha...

weixin_42522964的博客 531

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

01-23-Flink-概念理论及架构

flink-01-23

qq_37171694的博客 246

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Flink(二)—— Flink 架构、原理与部署测试

Apache Flink 是一个面向分布式数据流处理和批量数据处理的开源计算平台,它能够基于同一个 Flink 运行时,提供支持流处理和批处理两种类型应用的功能。 现有的开源计算方案,会把流处理和批处理作为两种不同的应用类型,因为它们所提供的 SLA(Service-Level-Aggreement)是完全不相同的:流处理一般需要支持低延迟、Exactly-once 保证,而批处理需要支持高吞吐、高效处理。 Flink 从另一个视角看待流处理和批处理,将二者统一起来:Flink 是完全支持流处理,也就是

weixin_42073629的博客 582

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Flink常见面试题汇总

1.面试题一:应用架构 问题:公司怎么提交的实时任务,有多少 Job Manager? 解答: 1. 我们使用 yarn session 模式提交任务。每次提交都会创建一个新的 Flink 集群,为每一个 job 提供一个 yarn-session,任务之间互相独立,互不影响, 方便管理。任务执行完成之后创建的集群也会消失。 2. 集群默认只有一个 Job Manager。但为了防止单点故障,我们配置了高可用。 我们公司一般配置一个主 Job Manager,两个备用 Job Manager,然后结合 Zo

weixin_45858664的博客 1516

大数据Flink(六十四):Flink运行时架构介绍

TaskManager 启动之后,JobManager 会与它建立连接,并将作业图(JobGraph)转换成可执行的“执行图”(ExecutionGraph)分发给可用的 TaskManager,然后就由 TaskManager 具体执行任务。从以 Greenplum 为代表的 MPP(Massively Parallel Processing,大规模并行处理)架构,到 Hadoop、Spark 为代表的批处理架构,再到 Storm、Flink 为代表的流处理架构,都是以分布式作为系统架构的基本形态的。

Lansonli(蓝深李)的博客 1965

Flink学习之理论基础

???? 最近谈恋爱了,开心!恋爱归恋爱,学习归学习,今天就步入大数据实时领域吧,实习的这段期间接触较多的就是离线处理,hadoop和spark引擎已经比较熟悉了,而实时调度接触较少,但却有着无限大的发展潜力,一定是未来数据发展的一个趋势,今天我们就从当下最火的实时计算框架Flink开始,开启实时数据的新篇章。给大家推荐几个网址????: flink中文社区: flink中文官方网址. flink英文教程: flink英文官方网址. ????flink就凸显一个计算又快又准,sparkstream运行时

柳小葱的博客 2657
上一篇: Flink 清理过期 Checkpoint 目录的正确姿势
下一篇: 文件操作liunx
王树民
博客等级 码龄10年 912粉丝 560原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值