5.携程架构实践 --- 框架中间件

第5 章 框架中间件 
	中间件的概念来源于操作系统,是指在操作系统的基础上,为其他应用软件提供服务的一种基础软件,被形象的称为"软件胶水"或"数据管道"。例如,
中间件可以简化应用的通信方式,使不同的进程不再需要调用复杂的系统函数也可以完成,甚至以更多的方式进行数据传输;中间件也可以为上层应用提供
更友好的接口,以便应用更方便的操作系统设备。所以,从这个意义来说,"胶水"的概念,一方面是指中间件能够"粘合"不同的应用,另外一方面是指中间件
能够"黏合"应用和操作系统。
	
	互联网领域的中间件,主要是指在分布式系统中被广泛使用的中间件软件,一般用于提供通信和数据管理服务。中间件的优势,主要在于提供了对调用方提供了更好的
封装性:
	1.封装了底层操作的复杂性
		中间件可以根据公司基础设施及用户需求等实际情况,提供经验配置,并使用边界条件的降级策略对调用方进行透明封装,这样,用户就不再需要面对这些复杂性
	了。

	2.封装了公共业务模型的具体实现
		对于调用方来说,很多中间件api和jdk中对应用数据结构的api并无明显区别,它们属于同一种业务模型,学习成本很低。但这种封装性扩大了既有模型的作用
	范围(从线程到进程),提升了调用方的业务逻辑表达能力,降低了用户系统架构改造的成本和负担。

	3.良好的封装性带来了更好的模块性	
		中间件在某种程度上可以被认为是对公共业务逻辑的封装,直接使用定义清晰的中间件接口,一方面可以简化了业务逻辑架构,有利于业务逻辑缺陷更容易的暴露
	出来;另一方面将公共业务逻辑的缺陷封装在中间件中,有利于对这些缺陷进行修复,不再需要全局参与。


	中间件介绍:
		1.服务化
		2.消息队列
		3.配置中心
		4.数据访问
		5.缓存层

5.1 服务化 
		搭建一个服务化的架构体系可以为系统带来独立的扩展性,隔离故障和资源访问,提升系统的可维护性。

	5.1.1 为什么需要服务化中间件框架 
		服务化的架构体系提倡将原本单一的应用程序划分为一组应用程序。每一个应用针对特定的业务场景和需求进行构建,并且能够进行独立的开发,测试,部署和
	升级。同时,为了应对不同的场景和业务流程,这些应用还可能使用不同的技术栈进行搭建。在拆分后,原本应用内的方法调用就需要转变成应用间的远程过程调用。

		提到应用间的远程调用,就不可避免的涉及以下几个问题:
			1.如何将代码中的业务数据转化为远程过程调用时用于传输的数据(即序列化),并且可以在另一端再转化回来(反序列化)。
			2.应用如何获得自己的依赖的服务的调用地址和传输的数据格式。
			3.如何确保应用依赖的服务是高可用的。
			4.服务端应用如何管理自身的工作状态。
			5.如何降低应用用于发起调用的代码量和复杂度。
			6.远程过程调用如何进行认证,授权,流量控制和服务降级。

		为了解决这些问题,我们都需要做什么?
			1.一个对象序列化解决方案。它不仅需要具备准确性和高性能,还需要兼顾平台通信所带来的复杂性。
			2.一个服务注册中心。它可以提供服务的注册和发现功能。
			3.一个服务健康监测机制。它可以使外部实时感知服务的工作状态。
			4.一个高度封装的客户端实现。应用通过其暴露的简单接口就可以发起远程过程调用。
			5.一个服务治理系统。用户可以在系统中管理服务的各项配置。

	5.1.2 服务化中间件框架的基本架构 
		从客户端到服务端再到外围的支持系统,我们可以整理出服务化中间件框架的基本组成部分:服务端框架,客户端框架,服务注册中心和服务治理系统。
			1.服务端框架
				服务端框架工作在服务端应用内,将代码里的一个接口暴露为一个服务并允许其他应用调用。它支持使用不同的数据传输格式(如xml,json,pb)进行
			调用以使用不同的业务场景。它还会与注册中心进行通信,告知其自身的调用地址。同时,它集成了一系列外围服务治理类功能,如健康监测,认证授权,
			限流,熔断,服务降级等。

			2.客户端框架
				客户端框架工作在客户端应用内,将其他应用的一个服务接口映射到应用内,形成一个本地调用。其中封装了支持不同数据传输格式的序列化器,与
			注册中心交互的服务发现机制,在不同服务实例间分配流量的负载均衡器等。此外,它也需要包括客户端错误处理的熔断和服务降级机制。

			3.服务注册中心
				服务注册中心可以说是功能最单一的一个组件。它对外提供服务注册与发现功能,虽然功能简单。服务注册中心需要保证在极大的请求量下仍旧能够
			稳定工作,保证数据的准确性和一致性,并且可以在实时的将服务注册信息的变更推送给客户端框架。

			4.服务治理系统
				服务治理系统可以说是所有组件中最独立的,而且是所有组件中唯一提供用户使用界面的。它为用户提供了服务信息管理,服务实例管理,服务配置
			管理等治理功能。但其内部也会时刻与工作在应用内的框架组件进行交互,推送服务配置信息。

	5.1.3 服务注册中心设计解析 
		第三代服务注册中心,参考了Netflix的Eureka。一个注册中心集群由多个节点组成。各个节点的地位相同,无主次之分。由于服务实例注册信息的动态性,
	这些信息并不会被持久化到后端存储中,而是会被保存到各个节点的内存中。服务的注册和心跳请求会被接收请求的节点分发到其他节点,从而保证集群数据的一致
	性。同时基于内存的读写可以为整个系统提供一个很好的响应性。后端数据库主要用于保存一些服务实例的配置数据。

	5.1.4 服务治理系统功能解析 
		服务治理系统是连接用户与服务化中间件框架的桥梁。用户可以在服务治理系统中 创建服务,管理服务的元数据和相关配置。服务治理系统负责将数据推送给
	相应的框架组件,并在用户中生效。

		xc 的服务治理系统分为两部分:一部分是面向用户的web应用(用户界面),另外一部分是面向框架组件的核心数据接口服务(核心数据API)。
			1.用户界面包括 服务元数据管理,服务运维,服务配置管理,服务运行监控。
				a) 服务元数据管理,负责服务基本信息的创建,编辑与删除。服务的基本信息包括 服务的ID,类型,关联的应用信息,
				归属部门,负责人,访问方式和当前状态等
				
				b) 服务运维,则包括服务在运行时的各项管理功能,如 服务实例管理,服务路由管理等。
					1.服务实例管理可以说是用户最常用的功能。我们将服务实例的状态拆分为4个部分:服务器状态,实例状态,发布状态和健康状态,
					分别对应服务器的维护,服务实例的人为拉入,拉出操作,应用发布操作,健康监测的拉入,拉出操作。
					2.服务路由管理是服务请求分配的一个高级功能。在默认情况下,客户端的请求会在所有服务实例之间进行轮询分配。在某些场景下,如数据
					中心故障,蓝绿发布,应用迁移等,服务负责人需要定制请求分配的规则。这就是自定义服务路由需要实现的功能。服务路由管理支持3种路由
					类型:全局路由,操作路由和请求路由。
						全局路由,是对所有的服务请求的分配方式;
						操作路由,是对某个或某几个特定的服务请求的分配方式;
						请求路由,是允许业务代码在发起请求时告知客户端框架档次请求所使用的分配方式。

					每条路由可以绑定一个或者多个服务实例分组并为其分配权重。所有的路由规则都会被推送至客户端。在发起请求时,客户端会根据选定的路由
				规则和实例分组权重确定最终接收请求的服务实例,并向其发起调用。

				c)服务配置管理,可以是控制框架组件中各个功能的开关与参数,包括前文提到的限流,熔断,降级,认证授权等。限流支持服务和操作两个级别,
				全局,客户端应用和Ip三个维度。熔断的配置的主要内容是相应的阈值参数。认证授权支持配置应用标识和客户端ip地址两种白名单。

				d)服务运行监控,包括请求量,请求和响应数据大小,请求响应时间及请求并发量等。

			2.核心数据API
				负责与内部的客户端和服务端框架,注册中心及外部各个依赖系统对接。API与哦用户界面共享一套数据模型和后端存储,并通过restful API对外
			提供数据访问接口。API主要有以下类型:服务元数据API,服务实例管理API,服务路由管理API,服务配置管理API等。


5.2 消息队列 
	5.2.1 消息队列的特性与使用场景 
		消息队列的特性:
			1.异步
			2.松耦合
			3.数据分发
			4.流量削峰
			5.可靠投递

		下列场景推荐使用消息队列:	
			1.上下游业务解耦
			2.延迟通知
			3.大数据离线分析
			4.缓存同步

	5.2.2 主流消息队列 
		kafka 拥有成熟的生态,活跃的社区和巨大的实例集群。kafka将一个topic分成多个Partition(分片/分区),每一个Partition是一个Broker上的
	物理文件,通过Append Only 的方式实现文件顺序写的高性能,可以线性提高集群中单topic的吞吐量。kafka写入消息时,会有一个潜在的风险:当Broker
	上所有的topic的Partition总和过多时,可能会产生随机写。

	5.2.3 携程消息队列QMQ 
		1.生产消息的可靠投递与事务消息
		2.延迟消息
		3.定时重试
		4.同机房生产与消费
		5.消息检索与追踪

5.3 配置中心 
	5.3.1 为什么需要配置中心 
		配置是一种有形的安排。它依托于某种形式,同时具有控制力。

		配置的属性:
			1.配置是独立于程序的只读变量
				首先,配置是独立于程序的,同一份程序在不同的配置下会有不同的行为;其次,配置对于程序是只读的,程序通过读取配置来改变自己的行为,
			但是程序不应该去改变配置。

			2.配置贯穿应用的整个生命周期
				配置贯穿了应用的整个生命周期,应用在启动的时候读取配置进行初始化,在运行时根据配置调整行为。

			3.配置可以有多种加载方式
				常见的加载方式有程序内部的hard code,配置文件,环境变量,启动参数,基于数据库等。

		配置治理:
			1.权限控制
				由于配置能改变程序的行为,不正确的配置甚至能引起"灾难",所以对配置的修改必须有比较完善的权限控制。
			2.不同环境,集群配置管理
				同一份程序在不同的环境(如开发,测试,生产),不同的集群(如不同的数据中心)中经常需要不同的配置,所以需要有完善的环境,集群配置。
			3.框架类组件配置管理
				框架类组件配置是一类比较特殊的配置。

		为什么需要配置中心?配置需要被治理。

	5.3.2 配置中心的特性 
		除了支持基本的增删查改操作,配置中心还需要哪些特性?
			1.高可用
			2.配置热发布
			3.统一管理不同环境,不同集群的配置
			4.权限管理,操作审计
			5.灰度发布
			6.版本管理
			7.监控
			8.多语言支持

	5.3.3 Apollo 源码部分解析 
		1.客户端缓存
			Apollo配置的可用性主要依托于客户端在磁盘对配置的缓存。
		2.长连接
			Apollo的客户端和服务端会保持一个长连接,能够在第一时间通过长连接的响应获得配置更新的推送。

	5.3.4 配置中心面临的新挑战 
		1.容器化,Serverless,云原生
			这些架构会要求无状态的应用程序不要依赖磁盘,而将数据放到有状态的系统上,如数据库,分布式存储系统等。
		2.扩容
		3.写配置高可用
		4.敏感配置

5.4 数据访问 
	5.4.1 数据访问层概述 
		三层软件系统架构将应用系统划分为表示层,业务逻辑层和数据访问层。数据访问层负责与物理数据库进行交互,是业务逻辑处理和数据持久化之间的桥梁。
	从业务的角度来看,数据访问层应当具备两个基本功能:数据的curd操作和事务支持。

		为了统一不同厂商数据库的访问方式,大部分主流开发语言都定义了各自的数据库访问规范。如Java的JDBC,定义了一组数据库操作接口和类,数据库厂商
	可以将这些接口作为各自的JDBC驱动,用户可以通过标准的sql访问不同的数据库。JDBC是Java访问数据库的基础,但如果直接通过jdbc访问数据库,会不方便。
		1.首先,用户访问数据库需要先获取连接,而创建连接的开销很大,总是创建新连接会导致请求的执行效率低下,还容易增加数据库的压力,所以应当考虑复用
		已经创建的连接;用户还要防止连接泄露,所以应当在使用完毕后及时关闭数据库连接。由此产生了连接池的概念,连接池内部包含物理连接资源,用户只需要
		从连接池中获取连接即可。数据库连接池以数据源(DataSource)的形式暴露给用户,典型的产品有C3P0数据源,Tomcat数据源,Druid数据源等。
		2.其次,JDBC是面向sql的操作,这不符合Java面向对象的思想---用户希望通过对象模型操作数据库。由此产生了对象关系映射---ORM框架,orm框架负责
		将对象数据转化为sql再通过jdbc执行,典型的产品有MyBatis,Hibernate等。

		因此,一个完备的数据访问层应当具有对象关系映射和数据源管理能力,并在此基础上提供便捷的数据curd操作接口和事务处理机制。

	5.4.2 为什么要引入数据访问中间件 
		ORM 框架和数据源对JDBC底层操作进行了一定的封装和操作,使得用户的数据库操作更加便捷。这类公共组件就属于数据访问中间件,而中间件的意义就在于
	封装和抽象。通过数据访问中间件,开发者无需关心数据库访问的底层实现,可以更加关注于业务逻辑本身。具体来说,数据访问中间件可以解决以下问题:
		1.数据资源管理
			合理创建和销毁数据库连接,有助于保持数据库服务端的性能和应用客户端的sql执行效率;数据库连接池配置参数化,有助于根据不同的业务场景配置
		不同的特性的连接池。

		2.数据安全机制
			API校验,防范sql注入;数据库连接字符串集中管理,保护用户名,密码等敏感信息。

		3.读写分离
			对大部分应用而言,读操作的频率往往高于写操作,而数据库的读写锁会限制读操作的性能。对读操作的性能要求较高的场景,可以考虑采用读写分离
		架构。多个数据库意味着应用需要管理多个数据源,还需要根据操作决定使用哪个数据源。数据访问中间件可以对此进行封装,内部可以根据请求的读写类型
		访问不同的数据源,应用则无需关注此实现细节。

		4.分库分表
			通过一定的算法对数据库表的数据进行分组,使得数据均匀的分布在不同的分片上。分片可以是多个不同的数据库实例,也可以是一个数据库实例上的
		多个子表。如果由应用来直接实现分库分表访问,则请求路由控制会是一个比较头疼的问题。数据访问中间件可以集中管理分片算法和不同分片的设置,自动
		对请求的数据进行分析并路由到正确的分片,应用只需要像访问单库单表一样进行数据库操作即可。

		5.数据访问高可用
			数据库故障的自动恢复,快速转移能力。MHA是一个常见的解决方案。在这种情况下,应用端的数据源是一个动态可变的数据源。而数据访问中间件可以
		实现数据源的管理和动态切换功能。

	5.4.3 数据访问中间件的主流方案
		ORM框架+数据源 的方案基本可以满足单数据库的需求。分布式系统中需要考虑数据库的扩展和高可用等问题,因此需要围绕如何解决读写分离,分库分表,
	数据源动态切换这类问题来设计数据访问中间件的主流方案。

		单数据库访问的基本模式:
			1.ORM框架负责将业务对象数据转化为可执行的sql语句;
			2.通过数据源配置目标数据库地址和连接池参数,以正确的执行sql。

		读写分离,分库分表,数据源动态切换等问题的核心是多数据源的管理和切换,因此在上述基本模式下,衍生出了两套多数据库访问的主流方案:
			1.客户端模式
				客户端模式中间件内部为每一个目标数据库维护了一个数据源(连接池),数据源管理,请求路由和结果集合并等功能可以直接在业务应用服务器上
			完成。在这种模式下,业务应用服务器直接与目标数据库建立连接。客户端模式中间件通常会经过封装,抽象并以jar包的形式提供一套sdk给业务应用
			进行对接使用,而业务应用通常需要修改代码来适配。

				优点是:
					1.实现相对简单,本质上是对jdbc驱动的封装;
					2.嵌入业务应用,无需考虑组件的高可用问题;
					3.直接连接数据库,访问链路简单,性能和稳定性更加可控。

			2.代理模式
				代理模式中间件独立部署了一套代理服务,业务应用将sql请求发送给代理服务,再由代理服务进行sql分析处理,目标数据库路由,结果集合并返回
			等操作。在这种模式下,代理服务可以管理访问目标数据库的所有资源,对业务应用是透明的,业务应用只与代理服务进行交互。如果代理服务实现了某种
			或多种数据库通信协议,则业务应用可以像访问单数据库一样的基本模式,而无需通过改变代码来适配。

				优点是:
					1.对业务兼容性好,业务应用可以将代理视作某种数据库直接访问;
					2.跨平台,跨语言支持;
					3.服务端功能维护和升级比较容易。


	5.4.4 携程数据访问中间件功能解析 

5.5 缓存层 
	5.5.1 总体架构 
		1.客户端
			redis客户端会根据收到的路由信息,处理对redis的访问。redis客户端主要有以下几个功能:
				1.提供对redis的访问
				2.提供对redis操作的详细监控信息
				3.动态生效治理中心下发的路由配置

			redis客户端不需要判断redis分片的可用情况,只需要根据服务端下发的路由信息,通过一致性哈希算法定位key所在的分片,对相应的redis进行操作。

		2.控制台
			负责redis集群配置的可视化操作及监控信息采集,以方便用户观察和修改redis配置信息。

		3.治理中心
			负责保证redis集群的高可用,以及下发路由信息。

	5.5.2 分片和路由 
		分片和路由是redis集群的核心概念,所有客户端的访问,都是基于治理中心下发的路由信息及分片配置进行的。分片和路由其实是两个维度的配置。

		1.分片
		CRedis 使用树形结构来维护集群。在实现分片后,如何对redis发起访问呢?一般有两种做法:
			1.以Codis,Twemproxy 为代表的方案,由服务端决策一个key的读取问题,这样做的好处是redis在迁移的时候比较方便,因为proxy层的存在,
		使得客户端不直接与redis进行交互。缺点是,1是,增加一层代理会增加一定的网络开销,在流量大的情况下,这个延迟不可忽略;2是,一个proxy需要
		代理多个redis,一旦proxy出问题,就会引起大面积故障。

			2.设计一个轻量级的服务端,只提供一个基础的redis配置,并由客户端的哈希算法来保障key的读取,这样,即使网络出现问题,客户端无法从服务
		中心拉取redis的配置,依然可以使用本地的缓存对redis进行访问。同时redis服务具备及时推送能力,可以保障系统的稳定性。

		2.路由
			目前,CRedis的路由策略主要有以下几种:
				1.读写Master
				2.写Master,读当前机房的Slave
				3.写Master,读主机房Slave(适用于跨机房同步场景)

			第一种路由策略主要适用于强一致的场景,redis的同步是异步同步,不是一种强一致的同步,写入master的数据可能因为主从切换而丢失。

			第二种路由策略最常见,由slave分担读取的压力,master负责写入,然后master到slave的同步也会存在一定的延迟,所以,该策略不适用于
		立即读取写入的场景。

			第三种策略是基于xc的跨源机房同步产品---XPipe定制的一种策略,面对读操作多,写操作少的业务场景,跨机房读取不仅会带来额外的网络开销,
		也会对机房的专线造成很大的压力。

	5.5.3 高可用 
		一般来说,如果redis的使用规模不大,就可以在客户端直接和哨兵进行通信,从而获取当前可用的redis。但是,这种方法对哨兵具有很强的依赖性,同时,
	客户端的灵活性非常低(一切变更都基于哨兵),当集群达到一定规模的时候,无论是系统的稳定性还是灵活扩展,以及故障修复时的修复能力,都受影响。

		CRedis的治理中心通过监听哨兵的通知来生成修复事件,同时,会触发修复事务对redis的状态进行变更。然后,通过配置下发通知客户端。

	5.5.4 水平拆分 
		垂直扩容:
			内容有限,内存过大会带来运维风险。垂直扩容是物理扩大Group中的Master和Slave的MaxMemory,这种扩容方式不可持续。redis发生故障后,
		会发生主从同步,所以内存越大的实例同步数据越慢,会导致恢复时间越长。

		水平扩容:
			丢数据,适合缓存类场景。水平扩容会增加Group(分片),增加集群内存总容量,但是这样会导致一致性哈希算法的节点数发生变化,需要重新计算key
		在Group的分布。

		拆分扩容:
			拆分扩容是根据树形结构原理,将Group作为树形结构中的节点,分为分支Group和叶子Group两种。拆分的过程是将叶子Group拆分成分支Group。
		叶子Group是物理分配,直接对应redis实例;分支Group是虚拟分片,当哈希算法命中分支Group,但没有找到对应的redis实例,就需要继续往下找,
		直到找到叶子Group为止。

		拆分扩容的步骤:
			1.Group拆分
				是拆分扩容最关键的步骤,包括 数据同步,创建叶子Group,Group角色切换,路由修改,路由推送。
				a)数据同步:同步group拆分中的所有数据,确保数据不丢失,若拆分n个叶子group,则同步n个slave
				b)创建叶子group:在数据同步完成后,创建叶子group,每一个叶子group包含一个同步slave
				c)Group角色切换:将拆分的group切换成分支group,并在分支group节点下挂载新创建的叶子group节点
				d)路由修改:将修改的路由信息持久化存储到数据库中
				e)路由推送:通知客户端更新路由信息

			2.路由切换
				从group角色切换到客户端的路由生效期间,会发生秒级Set数据失败。

			3.数据整理
				清理新增叶子group中的冗余数据,保证group拆分中的所有数据平均分布在新增叶子group中,保证数据不会重复占用空间。

			4.挂在Slave
				在新增叶子group下挂载slave,slave的数量和group拆分中的slave数量一致。在挂载slave成功后,将最新的路由信息推送到客户端。

	5.5.5 跨机房容灾 
		XPipe 是xc 研发的redis多数据中心复制管理系统。基于redis的master-slave复制协议,XPipe 可以实现低延迟,高可用的redis多数据中心复制,
	并且提供一键切换机房,复制监控,异常报警等功能。

		1.XPipe 的整体架构:
			1.Console,用来管理多机房的元信息数据,同时提供用户界面,供用户进行配置和DR切换等操作。
			2.Keeper,负责缓存redis操作日志,并对跨机房传输进行压缩,加密等处理。
			3.Meta Server,管理单机房内所有的Keeper状态,并对异常状态进行修正。

		2.redis数据复制问题
			多数据中心首先要解决的是数据复制问题。我们决定采用伪Slave的方案,即实现redis协议,伪装成redis slave,让redis master推送数据到伪
		slave。我们把这个伪slave叫做 Keeper。

			Keeper的优势:
				1.减少master全量同步
				2.减少多数据中心网络流量
				3.在网络异常时减少全量同步
				4.安全性提升

		3.DR切换
			1.检查是否可以进行DR切换
			2.原主机房master禁止写入
			3.将新主机房的slave提升为master
			4.其他机房向新主机房同步

		4.XPipe高可用


	5.5.6 跨区域同步 
		区域是云计算领域通用的一个概念,一般是指物理上有一定距离的不同地域,具有独立的网络环境。

		1.redis 全量同步
			redis的复制原理可以参考官方文档。

			从本质上来说,在redis master内存中会以Ring Buffer的数据结构缓存一部分增量数据;如果网络瞬间发生闪断,slave就会从上一次中断的位置
		开始同步,如果网络在一段时间内无法连接,就会进行一次全量同步。

		2.数据从上海内网传输到公网
			公网到内网的调用可以使用反向代理软件,因为redis协议基于tcp协议自定义的文本协议,所以我们要使用支持tcp协议的反向代理工具。

			http协议的反向代理可以通过域名,url等信息进行路由,定位到目标服务器;tcp协议的反向代理则通过暴露端口来路由到不同的服务器集群。

		3.公网传输性能
			Long Fat Network.

	5.5.7 双向同步 
		1.技术选型
		2.跨数据中心双向同步问题
			1.复制回源:A->B->A
			2.环形复制:A->B->C->A
			3.数据一致性
			4.同步延迟

		3.redis的问题	
			1.redis的原始复制模型,不支持 Multi-Master理论架构的
			2.redis特殊的同步方式(全量+增量同步),给数据的一致性带来更多的挑战
			3.同时支持Master-Slave架构
			
		4.一致性的解决方案
		5.CRDT
		6.CRDT的数据结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值