bea tuxedo中间件入门


§  TUXEDO是什么?

l  客户端/服务器模式的演化

l  基本的客户端/服务器模式

l  多层结构

l  TUXEDO的客户端/服务器方法

l  一个完整的例子

l  应用配置文件:ubbconfig

l  运行应用的步骤


tuxedo是什么?

       BEA TUXEDO是在企业、Internet 这样的分布式运算环境中开发和管理三层结构的客 户/服务器型关键任务应用系统的强有力工具。它具备分布式事务处理和应用通信功能,并提供完善的各种服务来建立、运行和管理关键任务应用系统。开发人员能够用它建立跨多个硬件平台、数据库和操作系统的可互操作的应用系统。BEA TUXEDO是企业、 Internet 分布式应用中的基础主干平台。它提供了一个开放的环境,支持各种各样的客户、数据库、网络、遗留系统和通讯方式。


特点:

²  大量在线用户

²  巨量数据

²  信息访问

²  小事务

²  复杂网络


 

C/S系统的层次结构

以下列出了分布式系统的主要层次:

²  用户界面:被分成表示管理表示逻辑。代表有主机框架的3270仿真终端;UNIX系统的X终端等,最新的Web浏览器界面也是。

²  商业逻辑:包含应用逻辑应用规则

²  数据管理:分为数据访问逻辑(SQL)和数据库管理。

区分C/S结构的类型可以根据以下特性:

客户端和服务端程序间逻辑分布,如何实现层次功能;中间件产品及技术的使用。


 

C/S模式的演化

      上图列举了不同种类的C/S模式。其中大型主机系统仍然统治着最大的OLTP应用;基于X终端和UNIX工作站的应用在80年代后期兴起;廉价的WINTEL机器支持的Windows GUI通常用于2层模式;数据库新技术和OSF带领了3层应用模式。


 

基本C/S模式

       C/S系统是一种分布式系统,由其程序决定其特点:

²  客户端部分执行前端功能,如:提供用户界面,向后台发出用户请求的交易,将结果返回给用户。

²  服务提供一般后端功能,按交易组织,将结果返回前端。

²  交易是分散的,按需求的操作,可以被远程客户端访问的程序。

C/S模式可能会有如下优点:

²  减小客户端程序体积,提高反应速度

²  位置无关

²  模块化

²  扩展性好

 


 

可管理多层C/S模式

 

 

       在可管理多层(Managed Multi-Tier –MMT)C/S模式中,提出了中间件管理。在本书范围内,此点由交易处理(TransactionProcessing –TP)管理完成,提供以下功能:

²  在客户端和服务端之间进行通讯和传输

²  提供良好的系统管理

²  提供交易、配置的分布式管理

它管理服务端从多个客户端收到的数据流,并不是在C/S间建立一对一的关系,而且客户端可以向多个服务发出请求。这种特点保证了TUXEDO可以提供强大的分布式交易处理框架。

由于不必进行通讯和交易管理,数据库引擎可以专注于其特长:管理数据!在这种情况下,数据库成了一个纯RM(Resource Manager)。


 

MMT C/S模式给OLTP应用增加了如下优点:

²  所有C/S模式的优点在MMT模式下都得到了增强。实际上,由于中间件的引入,处理能力得到改善。

²  由于中间件管理了数据流,带来了许多新功能,如:交易路由、服务分布、管道、数据依赖路由等成为可能。

²  统一的数据流控限制了最大交易数,总的数据库过程少了,服务器空闲时间也少了,这就增加了数据库和系统效率。

²  应用代码的设计可以不考虑物理地址和内部数据表示。

²  配置成了一件单纯的管理工作,进一步的,可以通过配置轻易的改变系统结构。服务可以动态的增加、删除和重启动。

 


TUXEDOC/S方案

       构成TUXEDO系统的各部分、工具和其特性组成的MMT C/S模式给应用带来的便利及TUXEDO的实现方法:

MMT C/S模式的优点

TUXEDO的实现技术

模块化

客户端,交易,服务

最大化的处理能力和流量

客户端交易流量控制,分布式配置,快速消息匹配和路由

通讯传输技术

ATMI访问,一种简单且容易定义的接口

实时应用管理

核心配置文件,MIB,管理工具和基于浏览器界面的GAI

名字服务和交易位置无关

公告牌提供目录交易便利

应用数据服务

应用服务位置可以在配置中随意配置

 


 

TUXEDO系统的特点

 

²  TUXEDO /T

n  管理C/S数据流

n  支持3层结构

n  多平台支持

n  协同(BEAConnect, /TxRPC, /OSITP)

n  开发

u  通讯手段

l  同步

l  异步

l  会话

l  广播通知

l  管道

l  事件订阅

u  缓冲数据类型(自动编、解码)

u  事务

n  管理

u  核心配置管理

u  管理信息库(MIB)

u  Web界面管理接口

u  负载平衡

u  网络配置

u  安全

u  数据依赖路由

u  数据压缩

²  BEA JOLT 将TUXEDO中间件框架扩展到Internet和Java客户端

²  TUXEDO /Q

n  通讯保障

n  预定处理

²  TUXEDO /WS

n  多平台支持

n  把BEATUXEDO ATMI API扩展到客户应用程序中

²  TUXEDO /Domains

²  TUXEDO /COBOL


 

一个例子

  一个TUXEDO 应用可以分成3部分

²  发布交易请求的客户端

²  运行响应请求交易的服务

²  描述应用机器和服务信息的配置文件

上图是本例的示意图:

       客户端请求交易“TOUPPER”,数据是“hello world”。交易“TOUPPER”将字符串转成大写,将结果成功返回客户端。


 

客户端范例

#include <stdio.h>

#include “atmi.h”

main()

{

       char      *buf;

       long      len;

       if  (tpinit((TPINIT * )NULL) == -1)

       {

              exit(1);

       }

       if((buf=tpalloc(“STRING”,NULL,80))==NULL)

       {

              tpterm();

              exit(2);

       }

       strcpy(buf, “helloworld”);

       if (tpalloc(“TOUPPER”,buf,0,&buf,&len,0)== -1)

       {

              fprintf(stderr,”servicerequst fail\n”);

              tpfree(buf);

              tpterm();

              exit(3);

       }

       printf(“return string is:%s\n”,buf);

       tpfree(buf);

       tpterm();

       exit(0);

}

包含TUXEDO系统的头文件”atmi.h”,以便引用TUXEDO的函数和变量定义。

客户端调用tpinit()连接应用

用tpalloc()分配一个STRING类型数据缓冲

将”helloworld”拷贝进缓冲

用tpcall()包含数据缓冲,向交易“TOUPPER”发一个同步请求

打印出改变的数据缓冲

调用tpterm()切断与应用的连接

 


 

交易范例

#include <stdio.h>

#include <ctype.h>

#include “atmi.h”

void TOUPPER (TPSVCINFO *rqst)

{

       int i;

       for (i=0;i<rqst->len-1;i++)

              rqst->data[i]= toupper(rqst->data[i]);

       tpreturn(TPSUCCESS,0,rqst->data,0L,0);

}

包含TUXEDO系统头文件”atmi.h”

象所有的TUXEDO交易函数一样,TOUPPER不用直接返回任何值,所以返回类型为void

从客户端收到的数据放在TPSVCINFO结构中,是唯一的入参

交易处理…

用tpreturn()将数据缓冲返回客户端


 

配置文件

       有关应用的信息,如可用交易,交易位置,应用范围等,有必要集中管理于单一资源。事实上,这些信息被集中于文件UBBCONFIG。该文件分7节,主要部分内容描述如下:

*RESOURCES节包含全局信息,如:标识公告牌位置的唯一键值(IPCKEY),主控节点的名字(MASTER),应用类型(MODEL),下文例子中设为SHM表示是一个单节点应用。

*MACHINES节包含节点信息,如:机器物理名,TUXEDO系统位置(TUXDIR),服务码位置(APPDIR),以及本文件的二进制码文件名(TUXCONFIG)。

*GROUPS节包含一些管理用信息,如设定服务或交易的分布式事务处理。

*SERVERS节包含需要启动的交易和其组信息等其他信息。

*SERVICES节包含影响应用操作方式的的必要信息。本节列出的交易都是需要特别配置的,如有特别的优先级,装入参数,数据依赖路由等。


配置文件范例

#file : ubbconfig

*RESOURCES

IPCKEY                    5000

MASTER           SITE1

MODEL              SHM

*MACHINES

gumby                LMID=SITE1

TUXDIR             =”/usr/tuxedo”

APPDIR                    =”/usr/apps/simpapp”

TUXCONFIG     =”/usr/apps/simpapp/simpapp.tux”

*GROUPS

GROUP1           LMID=SITE1     GRPNO=1

*SERVERS

simpserv                   SRVGRP=GROUP1      SRVID=1

*SERVICES

TOUPPER


 

建立应用

       建立一个TUXEDO应用,必须进行以下步骤:

²  设置环境变量。

TUXDIR表示TUXEDO用户目录;TUXCONFIG是二进制配置文件名;在路径中加入TUXEDO的可执行文件路径;如果用到公共库,还必须包含LD_LIBRARY_PATH,指出公共库位置。

TUXDIR=/usr/tuxedo

PATH=$PATH:$TUXDIR/bin

TUXCONFIG=/usr/apps/simpapp/simpapp.tux

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TUXDIR/lib

export LD_LIBRARY_PATH PATH TUXDIR

²  用buildclient命令从源程序编译出客户端可执行文件。

buildclient –f client.c –o client -v

²  用buildserver命令从源程序编译出服务端可执行文件。-s参数列出提供的交易。

Buildserver –f simpserv.c –o simpserv –s TOUPPER -v

²  用tmloadcf命令从文本配置文件ubbconfig生成二进制配置文件tuxconfig。

Tmloadcf –y ubbconfig


 

开发TUXEDO C/S系统的必要步骤

²  配置环境变量

²  编码并编译客户端和服务端的程序

²  由文本的ubbconfig生成二进制TUXCONFIG

²  启动系统和服务

²  测试应用

²  关闭系统

 


 

小结

l  客户端/服务器模式的演化

l  用TUXEDO管理多层客户端/服务器模式

l  一个简单应用:simpapp

 

 

 


客户端开发    Client Development


本节要点

l  客户端在客户端/服务器模式中的作用

l  开发客户端的过程

l  查找错误

tperrno,tperrordetail

tpstrerror(),tpstrerrordetail()

userlog()

l  使用ATMI

进程管理

缓冲管理

请求/响应通讯

l  建立并编译一个TUXEDO客户端应用

buildclient


客户端在C/S模式中的作用

 

 

       为了更好的了解客户端的所有任务以编写客户端应用,有必要重新认识客户端在C/S模式中扮演的角色。

       首先,客户端是用户界面。意思是当用户在系统上用程序进行一次操作的整个过程就是一个客户端过程。前端过程是对客户端的另一个描述。客户端的首要任务就是获得执行操作应该得到的数据。

       一旦客户端得到了应有的信息,应该将数据按服务能够识别并适合传输的格式打包。

       然后,向服务端发送请求并等待回应。

       收到回应数据后,将其按一定格式返回给终端用户。

 

                                                          

 

 

 

 


客户端开发过程

       客户端程序的设计和实现可以被分成2部分考虑:

² 用户处理过程

² TUXEDO功能部分

下文的客户端程序只描述了TUXEDO功能部分。

利用TUXEDO的ATMI API调用可以做到:

——基本的TUXEDO调试技巧(tperrno,tpstrerror,userlog)

——TUXEDO进程管理(tpinit,tpterm)

——基本数据缓冲管理(tpalloc,tprealloc,tpfree)

——基本通讯(tpcall,tpacall,tpgetrply)


 

调试和错误处理

 

       当调用ATMI出错时,返回值为-1,全程变量tperrno被设值,该变量提供系统定义的出错原因。函数tpstrerror()以此变量为参数,返回错误的字符说明信息。

       完整的错误号和文本错误信息存在于文件$TUXDIR/include/atmi.h。

       函数userlog()重定向输出文件为ULOG.mmddyy。使用方法同printf()。该函数每次输出都写硬盘,这样在系统失败时也能保留调试信息。

 

代码范例

 

main()

{

int   ret;

 

ret = [a ATMI call]

if ( ret == -1 )

{

       printf(“Errorin ATMI call\n”);

       usrlog(“ATMIerror logged %d %s”,tperrno,tpstrerror(tperrno));

}else

       printf(“ATMIcall OK\n”);

 

……

 

ret = tpterm();

if ( ret == -1 )

{

       printf(“Errorin exiting application\n”);

       usrlog(“ATMIerror logged %d %s”,tperrno,tpstrerror(tperrno));

}

 

}

 

 

 

tperrno – 全程变量

(char *)tpstrerror(int)

userlog(…) – 语法同printf()

ULOG.mmddyy – 日志文件

 

 

 

 


 

进程管理

 

       为了使客户端能够访问TUXEDO交易,客户端程序必须连接TUXEDO应用并进行登记。这种管理性步骤在切断连接时也要类似执行一次。API如下:

 

inttpinit(TPINIT *tpinfo)

 

客户端通过调用tpinit()与应用连接,进行交互,有以下事件发生:

调用安全接口检查客户端是否需要认证

连接BB,使进一步的ATMI函数得到信息

使BBL了解BB中已经存在请求

建立客户端消息队列使服务可以发回返回信息,系统可以送出广播通知等

 

错误时返回-1,可能由以下原因引起:

²  TPEINVAL         参数错误

²  TPENOENT      BB无空间

²  TPEPERM         无连接权限

²  TPEPROTO      协议错误 – 被服务调用

 

inttpterm()

 

客户端调用tpterm()切断与应用的连接,结束了客户端的TUXEDO进程,该过程发生以下事件:

BB入口删除,使BBL知道客户端已经离开

客户端离开BB,客户端的信号量被移除

客户端消息队列被移除

错误时返回-1,可能由以下原因引起:

TPEPROTO      协议错误 – 被服务调用

TPESYSTEM    /T系统下错误

TPEOS               操作系统错

范例

main()

{

       intret;

       ret=tpinit((TPINIT*)NULL);

       ……

       ret=tpterm();

}


 

数据缓冲管理

       ATMI提供函数分配(tpalloc(),tprealloc())、释放(tpfree())TUXEDO数据缓冲。应用负责将应用数据填入缓冲。应用的数据类型和组织决定应该选择何种数据缓冲。函数type()可以返回正在使用的数据缓冲类型。

       以下是TUXEDO基本的数据缓冲类型:

² STRING      以空值结尾的单域字符数据。

² CARRAY    有长度定义的单域二进制数据,不进行编、解码。

² VIEW           类C结构或COBOL记录的多域组织。

² FML             无固定结构的自定义缓冲。

tpalloc(),tprealloc()在出错时返回空值,可能由以下原因引起:

² TPEOS        操作系统

² TPEINVAL  非法或不正确的类型

² TPESYSTEM    TUXEDO之下的错误


 

范例

main()

{

       intret;

       char*bufptr;

       ret= tpinit((TPINIT *)NULL);

       if( ret == -1)

       {

              printf(“Errorin joining application\n”);

              userlog(“tpiniterror %d %s”,tperrno,tpstrerror(tperrno));

       }else

              printf(“tpinitok\n”);

       bufptr=tpalloc(“STRING”,NULL,1*1024);

       if( bufptr==NULL)

       {

              printf(“Errorin allocating buffers \n”);

              userlog(“tpiniterror %d %s”,tperrno,tpstrerror(tperrno));

              tpterm();

              exit(-1);

       }

       ……

       tpfree(bufptr);

       ret= tpterm();

       if( ret == -1)

       {

              printf(“Errorin exiting application\n”);

              userlog(“tptermerror %d %s”,tperrno,tpstrerror(tperrno));

       }

}

函数用法

(char *)tpalloc((char *)type,(char*)subtype,long size)

(char *)tprealloc((char *)bufptr,longnewsize)

void tpfree((char *)bufptr)

 

 

 

 

 

 

 

 


 

同步通讯

      

函数tpcall()有6个参数。如下:

²  交易名

²  请求数据缓冲

²  请求数据缓冲的长度(仅缓冲类型为CARRY时需要)

²  返回数据缓冲的地址。缓冲大小可以根据收到数据而改变。

²  返回数据缓冲大小的地址

²  标志量

标志量可以是以下值:

²  TPNOTRAN      如有交易不调用

²  TPNOBLOCK   如有阻塞不等待

²  TPNOTIME        愿意等待直到超时

²  TPSIGRSTRT   系统中断信号在TUXEDO完成后再发布

出错返回-1,错误原因如下:

²  TPEINVAL  参数错误

²  TPETRAN  交易相关错误

²  TPETIME    超时

 

代码范例

main()

{

       intret;

       longlen=0,flags=0;

       char*bufptr;

       if  (tpinit((TPINIT * )NULL) == -1)

       {

              exit(1);

       }

       if((buf=tpalloc(“STRING”,NULL,801*1024==NULL)

       {

              tpterm();

              exit(2);

       }

       ret= tpcall(“TOUPPER”,bufptr,len,&bufptr,&len,flags);

       if(ret == -1)

       {

              userlog(“Errorrequesting %d %s”,tperrno,tpstrerror(tperrno));

              tpfree(bufptr);

              exit(-2);

       }

       tpfree(bufptr);

       if(tpterm()==-1)

       {

              userlog(“tptermerror %d %s”,tperrno,tpstrerror(tperrno));

       }

}

int tpcall((char *)servicename,(char*)bufptr,long length,(char **)bufptr,(long *)length,long flags)

      

 

异步通讯I tpacall()

ATMI提供2个异步通讯函数

² tpacall()

² tpgetrply()

tpacall()有4个参数

²  交易名

²  请求数据缓冲

²  请求数据缓冲的长度(仅缓冲类型为CARRY时需要)

²  标志量(同tpcall())

返回值是一个非负描述符(句柄),用于其后的tpgetrply()调用。如果tpacall()调用失败,句柄是-1,错误原因设置在tperrno,可能是以下值:

² TPELIMIT    过多未处理的tpacall()

² TPETIME    超时

tpacall()后总跟随一个tpgetrply(),有4个参数

²  变量地址,可以传入tpcall()所得句柄,或另设一个

²  返回数据缓冲的地址。缓冲大小可以根据收到数据而改变。

²  返回数据缓冲大小的地址

²  标志量

标志量的值可以是如下之一:

² TPGETANY                     取第一个返回值,设句柄

² TPNOCHANGE              取发送请求的匹配的类型的信息

² 参见tpcall()中的值

返回值0表示成功,-1表示失败,错误原因可能如下:

²  TPEINVAL         参数错误

²  TPEOTYPE              返回数据缓冲类型错误

²  TPETIME    超时

main()

{

       if(tpinit((TPINIT *)NULL)== -1)

              ……

       bufptr=tpalloc(“STRING”,NULL,100);

       if(bufptr==NULL)

              ……

       cd=tpacall(“TOUPPER”,bufptr,len,flags);

       if(tpgetrply(&cd,&bufptr,&len,flags)== -1)

              ……

       tpfree(bufptr);

       if(tpterm()== -1)

              ……

}

inttpacall((char *)service,(char *)bufptr,long len,long flags);

flag:TPNOBLOCK,TPSIGRSTRT,TPNOTIME,TPNOTRAN,TPNOCHANGE,TPGETANY

inttpgetrply((int *)handle,(char **)bufptr,(long *)len, long flags);

flag:TPNOBLOCK,TPSIGRSTRT,TPNOTIME,TPNOTRAN


 

编译C语言客户端程序

       当C语言的客户端程序编码完后,需要连接正确的库并编译。使用buildclient可以使该过程容易一点。该命令引用了C语言编译器并按正确的次序连接TUXEDO系统的库。为使用buildclient,应确认客户端程序包含了”atmi.h”,TUXDIR被设了正确的值。命令语法如下:

buildclient[-v] –o executable –f first-file [-f first-file] [-l last-file]

       此处first-file表示的应该是编译器可以识别的文件。缺省的编译器是cc。一般是.c(c源程序),.C(C++源程序),.o(object)或.a(库)文件。

       参数解释如下:

-o    生成的可执行文件名

-f     需要在连接TUXEDO库之前传给编译器的文件名。如有多于一个的文件名,名字应用空格分隔并用引号引起。也可以使用多个-f参数。

-l     需要在连接TUXEDO库之前传给编译器的文件名。语法同上。

-v    编译过程显示。


 

小结

l  不同的客户端/服务器模式

l  用TUXEDO管理多层客户端/服务器模式

l  一个简单应用:simpapp

l  开发TUXEDO客户端

 


服务端开发    Server Development


本节要点

l  服务的运行流程

l  返回控制

l  创建服务

l  规划服务的考虑

l  启动和关闭

l  一个服务中的交易可以调用另一个服务中的交易

l  一个服务可以提供多个交易

l  发布交易


服务的运行流程

       为了更好的了解服务端的所有任务以编写服务端应用,有必要重新认识服务端在C/S模式中扮演的角色。

       首先,服务是系统资源的联系点。例如,一个数据库服务联系实际数据库并对其进行查询和修改。为有效进行,应建立一个数据库连接。

       其次,服务必须发布系统内可以访问的交易,保证客户端可以知道把请求发往何处。

       以上两步结束后,服务进入一个循环——接收请求、处理请求并返回结果。接收请求包括进入消息队列,得到交易请求。处理请求包括检查请求数据缓冲,运行商业规则和逻辑,可能还包括访问数据库和返回结果数据缓冲。

       当系统管理员需要关闭系统,可以通过系统管理工具将关闭系统的消息发给服务。服务完成所有交易,取消交易发布,关闭资源连接然后结束。


 

返回控制

 

       在一般的C程序中,函数通过调用return()将控制返回,函数调用堆栈清空,控制返回调用点。

       TUXEDO系统的交易函数必须结束于将回应返回给客户端或前转到另一交易。函数tpreturn()用来结束交易将回应数据缓冲发给客户端。函数tpforward()将交易前转给另一个交易,由其负责回应原来的客户端。下图是tpreturn()的示意图。

       tpreturn()设计来代替常规的return(),结束绝大多数函数。它将回应数据缓冲返回请求的客户端,控制权返回给服务程序的标准main()(由TUXEDO提供)。

 

       tpreturn()使用下列参数:

 

第一个值表示交易是否成功,有3种可能:

² TPSUCCESS   交易完全成功,如果是一个会话,TPEV_SVCSUCC被生成

² TPFAIL        交易失败,tperrno将被设成TPESVCFAIL。如果是事务模式,事务被标志成abort-only,如果是会话,TPEV_SVCFAIL被生成。

² TPEXIT              与TPFAIL类似,但服务会中断,如果服务设成可以重启动,则可以由TUXEDO系统将其重启动。

第二个值是应用定义返回码,此处使交易可以发送一个整形代码到客户端,给出交易处理结果的详细信息。该值与/T系统无关,通过全程变量tpurcode送到客户端程序。

第三个值是回应数据缓冲的的指针

第四个值是回应数据缓冲的长度(仅缓冲类型为CARRY时需要)

第五个值是标志位,通常不用

 


 

初始化和结束服务

       tpsvrinit()和tpsvrdone()分别用来启动和关闭服务。如果应用不提供这两个函数,可以使用替代函数。tpsvrinit()用tpopen()缺省打开RM连接。tpsvrdone()用tpclose()关闭RM连接。

       tpsrvinit()象标准C语言的main()一样使用参数argc,argv。服务的命令行参数可以传入该函数,被getopt()解析。该部分的用法参见应用配置部分。本函数出错时返回-1,成功返回0。

       tpsvrdone()无参数,无返回值。


 

创建服务

 

当C语言的交易程序编码完后,需要连接正确的库并编译。使用buildserver可以使该过程容易一点。该命令引用了C语言编译器并按正确的次序连接TUXEDO系统的库,连接TUXEDO生成的main()等。buildserver还用-s参数产生合适的交易名/函数名映射表。要使用buildserver,必须先正确设定环境变量TUXDIR,PATH,LD_LIBRARY_PATH。命令语法如下:

buildserver[-v] [–o executable] [-s service2,service3:func] [-f source/object] [-lobject/library file]

例:

buildserver –s DEPOSIT –o TLR –f TL

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值