美团-外卖客户端容器化架构的演进

整个外卖容器化架构可以按照从下到上,从左到右的视角进行解读:

最底层是系统服务,因为我们采用了H5和RN这样跨端的技术栈,使得iOS系统和Android系统成为了最底层。

系统服务之上是集团基于Native建设的基建,全公司通用,覆盖了研发工程中方方面面的基础服务。

在基建之上是我们定义的容器层。我们尝试用单一技术栈解决所有问题。但经过我们的探索,觉得不太可能实现。好的架构要匹配业务形态,业务的诉求决定了我们不能选择唯一的技术栈去解决所有问题,细分外卖的业务场景可得到以下3个方向的页面分类:

  • 高PV主流程页面,例如首页、金刚页、商家页等,这类页面的PV远高于其他页面。这类页面的特点是,交互强、曝光度高、多团队参与建设和维护。针对这类页面,为了给用户提供极致的体验,是无法采用H5或RN实现的,即使性能上能够满足,但是这些页面是多团队共同参与建设,如果用H5或RN实现,那么单一团队改动,都会造成全页面受到无法预料的影响,其他未改动的业务方肯定是无法接受的。所以这类页面,我们采用的是局部动态化+页面模块化的方案,我们针对页面进行容器化改造,将页面变成容器,容器承载模块。每个模块归不同的业务方进行维护,从模块的维度进行解耦,每个模块都可以动态配置和下发。
  • 低PV辅助页面,例如帮助、足迹、收藏等,这类页面的PV低,但胜在数量多,都用原生实现成本比较高,如果都用H5来实现,不少页面和原生的关联还是比较近,不是非常适合。针对这类页面,我们采用集团提供的MRN基建去承载,MRN作为跨端的技术栈,我们已经在之前的RN混合架构的时候,建设了较为丰富的组件,针对自定义的MRN容器做了比较丰富的建设。同时,MRN具备动态下发的能力,满足业务的诉求。
  • 向外投放的运营活动页面,例如圣诞节活动,时效性比较强。这类页面由H5技术栈去完成,一方面可以满足时效性,另一方面H5的动态下发能力也是最强的,这样的特性能够充分的满足业务的诉求。我们使用集团提供的Titans基建,通过建设业务自定义的Titans容器,支撑业务的发展。

再往上,就是垂直的业务,外卖目前有流量业务、交易业务、商家业务、商品业务、广告业务、营销业务、闪购业务等。业务都是垂直向下依赖,直接可见容器,可见基建,能够很好地获取到各种已经建设的能力去完成业务的需求。

最上面是承载的App端,目前有四端,包括外卖、点评、美团、闪购等等。

右侧是测试发布和线上监控,相对于常规的移动端App架构而言,容器化架构的测试发布和监控是更为精细化的。不仅仅要关注端本身的可用性,还需要关注容器、容器承载的模块、模块展示的模板,模板里面的样式这些的可用性。

2.4 容器化的挑战

容器化架构相对常规的移动端架构而言,它从管理移动端的代码转变成管理移动端的容器建设代码和业务远端开发代码,多出了容器和业务远端下发。这不仅仅是对技术上的挑战,对长期做客户端开发同学,也需要一个思维转变的跳转。

一致性的挑战:容器需要在多个宿主应用之中运行,宿主应用的环境一致性直接影响了容器的一致性。我们的策略是两手准备,一方面利用外卖业务的优势推动宿主应用的环境对齐;另一方面将容器建设成SDK,通过SDK将长期保持容器的一致性,也通过SDK内部的设计屏蔽应用之间的差异;对于Android和iOS平台,我们通过分层的设计,尽可能屏蔽平台的差异。综上所述,一致性的挑战在于(1)容器运行的宿主应用的环境一致性;(2)不同应用不同版本容器的一致性;(3)Android和iOS平台容器的对业务的一致性。

动态发布的挑战:长期以来,客户端同学的开发概念里面只有App版本的概念,而当我们逐渐把业务代码做成远端下发时,将会新增一个线上动态发版的概念。当我们在发布业务的时候,相对以往的工作,多出需要去考虑这个业务的版本,可以运行的容器对应的App上下界版本。另外,发版的周期也会新增业务的发版周期,不仅仅是App的发版周期。这两者在一起将会产生新的火花,业务的版本和App的版本如何适配的问题,业务动态发版的周期和App的发版周期如何适配的问题。外卖这边的解决方式是建设主版本迭代+周迭代的模型。

监控运维的挑战:以往的移动端架构,我们更加关注的是端本身的可用性,然而当我们演进到容器化架构的时候,仅仅关注端的可用性已经远远不能确定业务是可用的了。我们需要从端的可用性延伸出下载链路、加载链路,使用链路上的可用性,针对每个重要的环境,都做好监控运维。

3. 外卖跨端容器建设

3.1 MRN容器

3.1.1 MRN容器简介

React Native框架本身只是一个运行时环境中的渲染引擎,可以将同一套JS代码分别在Android和iOS系统上最终以Native的方式渲染页面,从而为App提供了基础的跨端能力。但从工程化的角度来看,如果想在App中大规模地应用RN技术,除了RN框架本身外,还需要在开发、构建、测试、部署、运维等诸多方面的配合。MRN(Meituan React Native)是美团基于React Native框架改造并完善而成的一套动态化方案,在RN的基础上提供了容器化能力、动态化能力、多端复用能力和工程化保障。MRN在开发效率、稳定性、性能体验、动态化和监控运维等多方面进行了能力升级和扩展,满足了美团RN开发工程化的需要。目前,MRN已接入美团40多个App,核心框架及生态工具有超过100位内部代码贡献者,总PV超过4亿。

3.1.2 Roo组件库

下面再介绍一下外卖建设的两个UI相关的技术项目,Roo组件库和组件样式动态配置。

  • Roo组件库:外卖在RN及MRN框架提供的UI组件基础之上,又扩展了适用于外卖业务的标准化UI组件库。UI组件库一方面统一了我们的设计规范和开发规范,提高了UI一致性;另一方面,组件封装也提升了RN页面的开发效率和质量。
  • 组件样式动态配置:有了标准的Roo组件,我们进一步给标准组件的动态添加了样式动态配置能力。在使用组件时,很多样式是支持动态下发的,例如字体、圆角、背景色等,方便我们进行UI的适配和改版。

外卖在2018年底开始试验MRN容器在外卖业务上的应用,并在2019年上半年进行了大面积的页面落地。目前,外卖已有近60个RN页面上线,占外卖页面比例超80%,其中包括Tab页面“我的”、提单选择红包页、订单评价页等高PV页面。MRN容器的接入,给外卖App的容器化、动态化、人效提升、包大小瘦身等方面都做出了不小的贡献。

3.2 Titans容器

3.2.1 Titans容器简介

Titans容器是美团系App统一的Web容器组件,基于苹果提供的WebView组件,将WebView容器化,统一了WebView的UI展示和交互方式,规范了桥协议的使用范式,同时预置了诸多基础能力和业务能力。Titans容器大大提高了Web页面的开发效率和用户体验上的一致性。

  • Web容器:Titans容器提供了统一的UI展示和自定义样式,例如导航栏样式、页面Loading状态、进度条样式等;还有统一的交互方式,例如页面跳转、Scheme协议的解析等。
  • KNB统一桥服务:Web容器虽然在动态化和Android、iOS双端复用上很好地弥补了Native的不足,但在很多地方体验上又难以达到Native的标准。因此,KNB桥应运而生,KNB定义了Native和JS通信的标准方式,方便开发时进行桥协议扩展,同时KNB也内置众多的Native基础能力,极大地提高了Titans容器的用户体验和开发效率。
  • Web业务增强能力:Titans容器中预置了丰富的基础业务页面,例如登录页面、分享弹窗等。
  • 提供链路增强:提供了长连接、离线化等方式来提高网络请求的速度和成功率。
  • WebView预加载:在App启动之后,用户点击网页入口之前,提前加载好HTML主文档和基础库,这样当用户点击页面入口时,App直接使用已准备好的WebView,仅需加载少量的业务代码。从而达到白屏时间短、加载页面迅速的效果。

Titans容器在外卖业务中的使用场景非常丰富,其中最重要的使用场景是各种运营页和活动页,例如点击首页顶部Banner的广告落地页、为你优选、限时秒杀等活动运营页等;还有客服页、帮助反馈页、商家入驻页、美团公益页等功能性页面;作为一级入口页面的美团会员页面,也是一个基于Enlight的Titans容器。

4. 外卖页面容器建设

外卖容器化建设,首先需要要区分的是核心页面和非核心页面。外卖业务中对核心页面的定义是页面DAU>美团DAU的5%或者是下单关键路径。为什么要先按照是否为核心页面进行拆分呢?重点就在于改造的成本。核心页面的业务复杂度决定了它不容易做全页面的动态化,它比较适合做局部的动态化方案。核心页面的复杂度在于业务本身复杂,最重要的是核心页面往往会有多个垂直业务团队共同的开发维护,大家各自有重点关注的模块,做全页面的动态化,无法做到有效的物理隔离。

而对于非核心页面,业务功能和交互相对简单,组织关系也较为确定,更适合做标准的MRN和Titans容器化。所以我们的策略是核心页面做到支撑页面模块级别的业务动态和复用,非核心页面可以做到页面级别的动态化和复用。页面容器化的核心含义就是把一个页面划分为若干个模块,每个模块成为一个业务容器,每个容器的填充既可以用Native的方式实现,也可以用Mach实现(Mach是外卖自研的页面局部动态化技术),可以支持iOS/Android/小程序三端跨平台运行。页面本身则化身为容器的管理者,负责子容器的编排和布局,并支持其动态化。

4.1 页面容器化设计思路

页面容器化设计中主要分为三个阶段,模块有序化、模块编排化、渐进式业务落地。

  • 模块有序化:将耦合的外卖业务代码按模块维度进行拆分,建立标准化的模块间组合和交互方案,降低模块内改动对其他模块的影响。这个阶段我们同时完成了Native原生模块和局部动态化模块的标准化改造。
  • 模块编排化:页面容器化的一个特点是页面具备编排模块的能力,在这个阶段我们在客户端增加了对业务模块结构编排能力的支持,同时我们跟后端的同学共建了配置平台,通过配置化的方式打通了AB实验平台、统一数据服务等多个平台。在标准化的数据协议的支撑下,页面支持了AB实验动态上线,大大降低了客户端在AB实验方面的开发成本,做到了客户端零成本,后端低成本,高效地支撑了外卖首页六周年的大改版。
  • 渐进式业务落地:页面容器化后最明显的收益是支持业务需求的动态上线,只有页面容器中的业务模块具备动态能力才能实现这个目标,这会涉及Native模块迁移为Mach模块的过程。在这个方面,我们的思路是跟随业务需求渐进式落地。在模块编排阶段,我们设计了Native模块向Mach模块迁移的能力,同时设计了覆盖模板维度和API接口维度的三重降级方案,来保障模块动态化迁移的稳定性。

4.2 业务构建模块标准化

从App页面开发的角度看,一个完整的页面可以按照不同的功能及不同业务属性划分出多个不同的模块。

业务构建泛指由多个业务模块组合拼装为一个业务页面的过程,涉及页面本身(UIViewController/Activity)以及各个业务模块的构造过程,前后端业务数据以及页面和业务模块之间的数据交互过程,业务模块内部的数据处理以及视图刷新流程。

模块标准化指的将业务构建涉及到的多个过程通过规范化的方式确定下来,形成唯一的标准。模块标准化一方面能够在解决业务共性问题的基础上提供业务难点专项解决方案,另一方面能够在框架基础上形成能力约束,减少重复建设、低质量建设的问题。

业务构建模块标准化中我们抽象了四层,下面将分别进行解读。

  • 最底层是框架能力层,是外卖业务团队自研的符合外卖业务特点的双端模块化框架。框架解决了不同页面场景下的共性问题,对典型的业务痛点也进行了支持。它是一种页面框架设计在iOS、Android双端对齐的实现方案,这种双端对齐的能力为页面容器化设计的双端一致性提供了保障。
  • 统一接口层是对框架能力层的标准化抽象,它可以保证任一模块调用的能力在各个业务场景下的实现都是一致的,有了这一层抽象任一模块都可以直接在各个场景下复用。
  • 在往上就是App全局的模块复用层,标准化后的模块可以通过唯一标示向模块复用池注册模块,这种中心化的注册方式可以让业务模块在跨业务库的场景下可以灵活地复用。
  • 最上层就是外卖的核心业务场景层,每一个场景都对应了一个标准化的页面容器,页面容器通过实现容器化接口来完成页面容器的构建。

通过业务构建模块标准化的建设,业务模块已经是标准化的了,可以在跨页面间自由组合,这为页面容器化打下了基础。

在页面容器化中最基础的能力有以下几点:页面中业务模块可编排能力,动态上线前端AB实验的能力,增量上线动态模块的能力。实现这些能力最重要的就是进行前后端数据协议标准化建设。客户端根据数据协议中的模块唯一标识匹配并构造业务模块,在完成模块数据的填充后会根据数据协议中的模块布局信息完成模块的布局。针对Mach动态模块,我们创建了基于模板ID的模块匹配和数据填充流程,可以支持Mach动态模板的增量上线。在数据协议中针对前端AB实验我们预留了AB实验和通参字段,在数据填充阶段通过容器化接口传入动态模块中,用于支持AB实验的动态上线。

在容器化上线的过程中属于接口的大版本升级,为了保证容器的高可用性,客户端从模块级别和API级别实现了两套降级容灾方案。

模块级别的降级方案主要针对Mach动态模块,与Native模块不同,Mach动态模块需要预先下载动态模板才能正常地完成模块的载入和渲染。为了保证动态模块的加载成功率,我们一方面在接口上线前利用Eva(美团内部系统)对Mach模板的下载进行预热。另一方面,我们设计了动态模块的主动降级方案,针对动态模块的动态上线使用Native模块进行兜底降级,对于跟版动态模块使用App内置模板的方案进行兜底降级。

API级别的容灾方案主要为了保障客户端在新接口不稳定的情况下可以自行降级到旧接口。针对这个问题,我们对线上老接口设计了数据结构映射方案,在客户端通过配置化的方式可以把老接口的数据结构映射为新接口的数据结构。这样在上层业务无感知的情况下,可以做到容灾方案的上下线。

4.3 小结

通过页面容器化,使得页面只需要关心页面级的构造方式,而无需关心某一模块内部如何实现动态化的。把页面与页面的模块分离,也符合目前外卖客户端的组织结构,有利于业务组间的协作。同时,页面容器化使得外卖核心页面具备了符合外卖业务场景下的动态能力,渐进式把Native静态模块过渡到具备动态能力的模块,从模块的维度使整个页面具备了动态能力。这种渐进式的迁移方案把容器迁移跟业务模块的迁移分隔开,大大降低了页面容器化改造的风险。

5. 外卖容器化架构的衡量指标

5.1 容器化架构衡量指标的特点

质量和性能指标是衡量我们App开发质量和用户体验的重要依据,是我们一直都非常关注的重点数据。在非容器化时代,我们大多数的指标都和App的使用环节紧密相关,因为在非容器化时代,逻辑链路相对简单,例如我们打开一个新页面时,我们首先创建页面实例,然后发起网络请求,同时页面会经历一系列生命周期方法,最后渲染。这时我们可能会关注网络请求的成功率和请求时间,页面的渲染时间,和过成功是否发生Crash,这个过程相对更短暂,指标更少,所以监控起来也更容易。

外卖的容器化大大提升了外卖业务的复用能力、动态能力、模块化和开发效率,但同时也带来了更长的逻辑链路,链路从时间维度上划分是:下载链路、加载链路、使用链路。例如我们在使用MRN容器的时候,会涉及到bundle的启动下载或预热下载,bundle解压缩,MRN容器引擎初始化,bundle加载,JS的加载、执行,页面渲染等步骤,其中的每个步骤都可能存在性能问题和质量风险。因此,我们需要升级我们的衡量指标系统来应对容器化带来的新的挑战。

5.2 链路指标

  • 下载链路:在下载链路中下载容器所需的各种资源,在MRN和Mach中主要是bundle的下载任务,只有bundle下载成功,才能进行后面的各项操作。所以bundle下载的成功率是下载链路中最重要的指标,同时bundle下载的时机也很重要。外卖业务中有各种bundel上百个,如果在启动时拉取所有bundle,对冷启动时间会造成极大的影响。我们采用了bundle预热的方法,发布bundle是给bundle打上相应的Tag,在适当的时机去下载,避免集中下载。
  • 加载链路:在加载链路中重要工作是对下载链路中下载的资源的解压和解析。例如在用PGA加载页面时,会进行模块的解析、模块匹配、模块降级、数据模型生成等步骤。在MRN中会进行bundle解压、引擎初始化、bundle加载等步骤。加载链路往往是比较消耗计算资源的步骤,对页面打开和加载时间影响较大,所以我们会比较关注加载链路的性能指标。
  • 使用链路:使用链路和非容器化的使用阶段基本相同,会主要关注页面的加载时间、Crash率、页面页面FPS、页面卡顿等指标。除此之外,还会关注和容器本身特性相关的一些指标,例如在MRN容器中,我们还会关注JS错误率、JS渲染时间、白屏率等指标。

5.3 关键指标

因为容器化的使用形成了一个串行的链路,所以如果某个关键节点失败,会导致容器功能不可使用,关键指标的任务就是从上述众多的指标当中筛选出这些关键节点。例如在下载链路中bundle下载的成功率和API的成功率,加载链路中bundle加载的成功率和模块匹配的成功率,下载或加载失败都无法再进行链路中的后续步骤,针对上面的成功率指标,我们会添加分钟级别的实时监控告警,做到及时发现,快速响应和紧急修复。

在使用链路中模块渲染的成功率、Native Crash率、JS错误率也属于关键指标,这些任务的失败也会导致容器的不可用,针对这些指标我们也会采用实时监控措施,并且添加降级手段,例如回滚bundle版本,或者把MRN和Mach容器降级为Native容器。

6. 外卖容器化架构的监控运维

上面讲到了容器化架构的各项衡量指标,那么把这些指标具体落到实处的工作就是线上的运维监控工作。工欲善其事,必先利其器,对于监控运维工作,一定要有合适的监控工具辅助配合才能事半功倍,公司内有很多优秀的监控统计工具可供使用,这里的难点就是如何根据监控的需要判断选择合适的工具。还有就是合理的划分监控维度和数据指标的优先级,例如对于能够影响到链路稳定性的关键指标,我们需要做到分钟级的监控,一旦出现问题就能及时收到告警,对于非关键指标,则通过生成日报的方式,方便开发者的统计和分析。

工具的使用上主要分为大盘工具、具体异常工具、灰度降级工具、告警工具等(以下是美团内部使用的工具)。

  • 大盘工具:主要使用CAT、天网、Crash平台等工具。这些工具收集、统计大盘数据,然后生成可视化的图标和曲线。方便开发者了解大盘的整体情况和变化趋势。
  • 具体异常工具:使用Sniffer、Logan等工具。这些工具可以用来获取发生异常时的上下文和设备信息,回捞用户行为日志,方便定位排查具体问题。
  • 灰度降级工具:使用ABTest平台、Horn等工具。用于下发配置,以进行灰度控制或开关控制。
  • 告警工具:告警小助手。将告警通过IM软件及时发送到个人或群组,做到及时发现及时处理。

业务覆盖维度监控可以分为全局监控和局部(单业务)监控。

  • 全局监控:监控各项容器化质量指标、性能指标,生成每日报表,方便跟踪监控容器化的整体质量。
  • 局部(单业务)监控:实时监控每个页面、每个容器的线上数据,做到有问题及时发现,及时定位,及时处理。

时间维度监控:可以按天、小时、分钟的时间维度。天级别的监控主要是一些非关键路径指标,例如一些性能指标,页面加载时间、页面FPS、JS渲染时间等,我们可以按天维度的生成数据报表,已邮件的数据发送日报。当App灰度上线时,我们会开始小时级别的监控,每过半小时通过IM软件向广播一些关键指标,方便开发者跟踪线上数据的稳定性。分钟级别的监控则是针对关键指标,观察分钟维度上的变化,如果关键指标超过阈值,或者波动过大,就会及时产生告警。

下面我们以一个开发者的视角去看一下外卖容器化架构的监控运维系统。从获取信息的方式上可以分为主动查询和被动推送,开发者可以通过监控工具监控全局和局部数据的变化趋势,也可以分析具体异常Case;也可以从IM工具,邮件等收到相关的推送数据,以便及时响应。在控制运维上,开发者可以通过Eva、Horn等美团内部的灰度系统进行灰度发布,当灰度期发现问题的时候,可以及时地通过停止灰度,版本回滚,关闭入口的方式进行降级容灾处理。

7. 外卖容器化架构的发布能力

7.1 容器化架构发布体系

容器化使外卖业务具备了强大的动态化能力,但动态化能力又和需要相应的发布能力来支持,发布能力是我们业务开发质量和效率的重要保障,也是我们容器化建设工作过程中的重点环节,这一节主要介绍一下外卖容器化的发布能力。

从发布能力类型的角度看主要可以分为三种类型:(1)容器内容的发布,包括发布整个页面或者发布页面中的局部模块;(2)配置下发,通过API或其他配置平台,下发布局协议、AB测试、样式配置、功能配置、模板配置、容器配置等,大大提高了业务的灵活度和线上验证能力;(3)灰度、降级下发,通过UUID,用户画像等信息做到灰度发布,降级回滚等控制能力。

从发布资源的的角度看主要分为两种:一种是普通的资源,例如发布一个Web页面,或者通过发布新版API来控制页面局部容器的展示与否和展示的位置,同时我们也可以进行一些AB Test操作;另一种是bundle资源,主要是针对MRN容器和Mach容器,每个MRN容器和Mach容器的资源都会先被打包成一个bundle,然后通过发布系统下发到终端,然后终端解析bundle中的代码和资源,最终渲染页面。

从发布阶段的角度看,可以分为测试阶段、上线阶段、灰度阶段和全量阶段,其中上线阶段是最终的环节,我们增加了很多校验和保护手段来尽量保证上线操作的正确性。

7.2 跟版本发布流程

虽然我们具随时备动态发布能力,但正常的版本迭代还是会存在中,所以外卖这边的节奏是周动态迭代+双周版本迭代,这保证了我们的开发工作有个一清晰的周期。在动态发布阶段中最关键的阶段操作上线阶段。以MRN为例,目前外卖业务中应有70多个bundle,再算上测试环境的bundle就有接近150个bundle,只是管理这些bundle就是一个复杂的工作,况且在进行上线操作时还是涉及发布的目标App、App版本的上下界、MRN版本的上下界等,一不小心就会造成操作失误,所以进行上线操作时需要非常谨慎。

我们针对操作上线阶段进行了事务流水线,通过流水线建立保护措施,一个bundle的上线要经历一个流水线的若干操作。首先,操作人根据上线SOP手册进行若干检查操作,同时编写标准格式的发布说明,然后周知相关核心人员后在操作系统上发起上线申请,Leader和QA收到申请后会进行检查并审批,审批通过后还要避开App使用的高峰期或节假日上线,上线后通过灰度发布观察各项数据指标,指标正常后全量发布。

7.3 bundle资源发布

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

img

我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

我们搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

[外链图片转存中…(img-e2ZeLIRV-1726060024760)]

我们在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值