基础知识 - Tomcat集群

集群Clustering与负载均衡Load Balancing的区别,可以参考这篇文章:[url=http://standardwisdom.com/softwarejournal/2009/09/clustering-vs-load-balancing-what-is-the-difference/]Clustering vs. Load Balancing – What is the difference?[/url]

使用集群可以分发请求提高吞吐量,应对单机故障、高可用性HA,提供不间断服务。而负载均衡只是集群的第一步,当然也是最重要的,集群还包含很多内容,比如:
[list][*]会话管理Session Management
[*]上传文件同步(可以通过共享文件NFS mount、实时同步rsync、独立文件服务、使用类似于Amazon S3的存储服务等实现)
[*]计划任务Job(可以使用基于数据库的Quartz实现)
[*]War文件自动部署(可以通过Tomat的FarmWarDeployer组件实现,但目前该组件还不稳定)[/list]
最简单的负载均衡是DNS Round Robin,通过DNS域解析提供不同的IP,多于用全球化的系统,依据地理位置不同提供最近的服务器。基于硬件可以实现负载均衡,比如F5;基于软件也有很多可以实现负载均衡,比如:Apache、Nginx、LVS、HAProxy、Varnish等。这里只讨论用目前市场占有率比较高的Apache和Nginx来实现负载均衡以及Tomcat的会话管理。

[b](1)基于Apache的负载均衡[/b]

Apache和Tomcat的连接:
[list][*][url=http://tomcat.apache.org/download-connectors.cgi]mod_jk[/url](mod_jk2已停止更新)
[*][url=http://httpd.apache.org/docs/2.2/mod/mod_proxy_http.html]mod_proxy_http[/url]
[*][url=http://httpd.apache.org/docs/2.2/mod/mod_proxy_ajp.html]mod_proxy_ajp[/url][/list]
mod_proxy_http是Apache的标准模块,可加密httpd和tomcat之间的通讯;mod_jk是非标准模块,支持SSL,可实现负载均衡;mod_proxy_ajp是Apache2的标准模块,mod_jk的标准实现。

AJP(Apache JServ Protocol),目前最新版本1.3,所以一般都说AJP13。它是一个二进制协议,性能比http11快。mod_jk会把接收到的HTTP请求转换为AJP后转发给Tomcat,相应也会把Tomcat的AJP应答转换HTTP应答返回给用户的Web浏览器。而mod_proxy_http并没有什么协议的转换是直接把HTTP请求和Tomcat之间进行转发。虽然mod_proxy_http支持加密,但一般Web服务器都部署在子域下,加密通讯没多大必要。所以目前大多用的是mod_jk或mod_proxy_ajp。性能优先用mod_jk、安装配置简单用mod_proxy_ajp。

[b]Apache HTTP Server与Apache Tomcat的区别[/b]
[quote]・别名:Apache HTTP Server、Apache Web Server、Apache、httpd
・官网:http://httpd.apache.org
・HTTP Web Server,用来HTTP协议的文件服务
・主要处理静态文件,通过扩展支持CGI、PHP、Perl等脚本语言
・除了Java基本上能通过扩展支持任意语言
・处理Request提供Response相应,实现Load Balancing
・C语言实现
・其他类似开源实现Nginx:[url=http://nginx.org/]http://nginx.org/[/url][/quote]
[quote]・别名:Jakarta-Tomcat、Apache Tomcat、Tomcat
・官网:http://tomcat.apache.org
・Servlet Container(叫Catalina),JSP Engine(Jasper)是Java Servlets和JSP的官方实现
・可以处理静态文件,本身也实现了Web server(叫Coyote、不需要Apache),主要处理Servlet/JSP
・可以执行Perl等脚本语言,但是很少用于Java以外的语言
・不是处理Request提供Response相应,提供Servlets和JSP的所有功能
・纯Java语言实现(为了提升性能,TC-Native用C实现了一部分核心功能)
・其他类似服务器:[url=http://rensanning.iteye.com/blog/1695196]http://rensanning.iteye.com/blog/1695196[/url][/quote]

[b]APR和AJP的区别[/b]
[list][*]Apache Portable Runtime (APR) 连接器实现 比如:BIO/NIO/NIO.2/APR
[*]Apache JServ Protocol (AJP) 协议 比如:HTTP, AJP, Websocket, FastCGI[/list]

conf/server.xml
[quote]<Connector port="8080" protocol="HTTP/1.1" .../>[/quote]
protocol默认是HTTP/1.1,安装了tomcat-native的话使用Http11AprProtocol,没安装的话使用Http11Protocol。也可以自己直接指定protocol:
[list][*]org.apache.coyote.http11.Http11Protocol Traditional Connector
[*]org.apache.coyote.http11.Http11AprProtocol Native Connector
[*]org.apache.coyote.http11.Http11NioProtocol NIO Connector
[*]org.apache.coyote.http11.Http11Nio2Protocol NIO2 Connector[/list]

Tomcat默认是开启APR的:
conf/server.xml
[quote]<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />[/quote]
不安装Tomcat APR(Tomcat Native Library)会看到以下的提示:
[quote]The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path:........[/quote]
正确安装后的提示:
[quote]Loaded APR based Apache Tomcat Native library 1.1.32 using APR version 1.5.1.[/quote]

[b]①配置mod_jk[/b]

conf/httpd.conf
# Load module
LoadModule jk_module path/to/apache2/mod_jk.so

# Specify path to worker configuration file
JkWorkersFile /path/to/apache2/conf/workers.properties

# Configure logging and memory
JkShmFile /path/to/desired/log/location/mod_jk.shm
JkLogFile /path/to/desired/log/location/mod_jk.log
JkLogLevel info

# Configure monitoring
JkMount /jkmanager/* jkstatus

<Location /jkmanager>
Order deny, allow
Deny from all
Allow from localhost
</Location>

# Configure applications
JkMount /webapp-directory/* LoadBalancer


conf/workers.properties
# Define worker names
worker.list=jkstatus, loadbalancer, stat

# Create virtual workers
# The status worker allows us to get statistical data
worker.jkstatus.type=status
worker.loadbalancer.type=lb

# Declare Tomcat server workers 1 through n
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009

# ...
worker.worker[n].type=ajp13
worker.worker[n].port=8010
worker.worker[n].host=localhost

# Associate real workers with virtual LoadBalancer worker
worker.loadbalancer.balance_workers=worker1,worker2,…worker[n]


workers.properties的具体参数设置,参考[url=https://tomcat.apache.org/connectors-doc/reference/workers.html]官方文档[/url]

可以通过[url=http://localhost/jkmanager]http://localhost/jkmanager[/url]查看负载均衡的状态。

[b]②配置mod_proxy_ajp[/b]

conf/httpd.conf
# Required Modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule status_module modules/mod_status.so

# Reverse Proxy
<Proxy balancer://mybalancer>
BalancerMember ajp://localhost:8301 retry=30 loadfactor=1 route=jvm1
BalancerMember ajp://localhost:8302 retry=30 loadfactor=1 route=jvm2
BalancerMember ajp://localhost:8303 retry=30 loadfactor=5 route=jvm3
ProxySet lbmethod=bytraffic
ProxySet nofailover=Off
ProxySet stickysession=JSESSIONID
</Proxy>

# Pass All Request
ProxyPass / balancer://mybalancer/
ProxyPassReverse / balancer://mybalancer/

# Forward Proxy
ProxyRequests Off

<Proxy *>
Order deny,allow
Deny from none
Allow from localhost
</Proxy>

# Balancer-manager, for monitoring
<Location /balancer-manager>
SetHandler balancer-manager

Order deny,allow
Deny from all
Allow from 192.168.21.0/24
</Location>


按项目转发:
<Proxy balancer://webapp>
BalancerMember ajp://localhost:8001/webapp route=jvm1 loadfactor=10
BalancerMember ajp://localhost:8002/webapp route=jvm2 loadfactor=10
ProxySet lbmethod=bytraffic
ProxySet nofailover=Off
ProxySet stickysession=JSESSIONID
</Proxy>

<Location /webapp/>
ProxyPass balancer://webapp/ stickysession=JSESSIONID
ProxyPassReverse balancer://webapp/ stickysession=JSESSIONID
</Location>


Tomcat默认使用JSESSIONID,也可以自己定义session的key值。
$CATALINA_HOME/conf/context.xml
[quote]<Context sessionCookieName="yourCookieName">[/quote]

route里设置的jvm1、jvm1、jvm3就是为了实现粘性Session,和Tomat的server.xml里配置一致。
[quote]<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">[/quote]

可以通过[url=http://localhost/balancer-manager]http://localhost/balancer-manager[/url]查看负载均衡的状态。

[b](2)基于Nginx的负载均衡[/b]

Nginx本身并不支持AJP连接,只有HTTP连接。由于Nginx可以keep-alive连接,所以性能不比AJP差。当然也可以使用第三方模块[url=https://github.com/yaoweibin/nginx_ajp_module]nginx_ajp_module[/url]来支持AJP。

conf/nginx.conf
# Defines a group of servers
upstream tomcatcluster {
server 127.0.0.1:8181 weight=2;
server 127.0.0.1:8282 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8383;
keepalive 10;
}

# Pass to the backend servers
server {
location / {
proxy_pass http://tomcatcluster;
}
}


如上定义即可实现负载均衡,但是non-sticky session。在定义Upstream时使用ip_hash可以将相同IP的请求分发到同一Tomcat实现sticky session.
upstream tomcatcluster  {
ip_hash;
server 127.0.0.1:8181;
server 127.0.0.1:8282;
server 127.0.0.1:8383;
}


nginx本身也支持sticky指令,但是用于商业版本。
所以需要用到[url=https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng]nginx-sticky-module[/url]、节点的健康检查[url=https://github.com/yaoweibin/nginx_upstream_check_module]nginx_upstream_check_module[/url]

[b](3)Tomcat的会话管理[/b]

Tomcat的会话共享:粘性会话Sticky Session和会话复制Session Replication。

[b]①粘性会话Session affinity/Sticky Session[/b]
将同一用户的请求转发到同一Tomcat服务器上,如果Tomcat服务器down了,会话信息就丢了。

conf/server.xml
[quote]<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">[/quote]

session-id的值:<随机数>.<jvmRoute的值>
[quote]Cookie:JSESSIONID=40025608F7B50E42DFA2785329079227.jvm1[/quote]

[b]②会话复制Session Replication[/b]
基于会话复制可以自动实现故障转移Session Failover。

IP组播(In Memory Session Broadcasting/IP Multicasting):利用Tomcat的一个模块Tribes支持服务器集群中的组通信SimpleTcpCluster,不是所有操作系统支持IP组播。
[quote]# ifconfig -a
eth0 Link encap:Ethernet HWaddr 00:1D:09:31:69:C6
inet addr:192.168.21.140 Bcast:192.168.21.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
# cat /proc/net/dev_mcast
2 eth0 1 0 01005e000001[/quote]

通过tcpdump可以查看multicast的通讯情况(默认500毫秒通讯一次):
[quote]# /usr/sbin/tcpdump -i eth1 -n ip multicast
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
01:36:00.054028 IP 192.168.21.3.50001 > 228.0.0.104.50001: UDP, length 77
01:36:00.554770 IP 192.168.21.3.50001 > 228.0.0.104.50001: UDP, length 77
01:36:01.055657 IP 192.168.21.3.50001 > 228.0.0.104.50001: UDP, length 77
01:36:01.556332 IP 192.168.21.3.50001 > 228.0.0.104.50001: UDP, length 77[/quote]

[b]4种类型Session Manager:[/b]
[list][*]Standard Manager 默认值,单实例。Tomcat替我们管理会话,Tomcat重启graceful或应用重加载时将HttpSession保存到SESSIONS.ser中复原。
[*]Persistent Manager 一定期间([color=red]不是实时[/color])增量备份会话信息到文件、或经JDBC到DB
[*]Delta Manager 用于小的集群,依靠multicast复制会话信息到其他实例,(全节点会话同步、All-to-All复制)
[*]Backup Manager 用于大的集群,和Delta Manager功能相同,但只是获取一个实例作为backup来存贮会话信息(单节点会话备份 Primary-Backup复制)[/list]

・单实例存储到文件(pathname设置为空时就不保存)
<Manager className="org.apache.catalina.session.StandardManager"
pathname="/var/sessiondata/mysessions.ser"
sessionIdLength="32">
</Manager>

・存储到文件(PersistenceManager + FileStore):
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.FileStore" directory="c:\\cluster\shareddir"/>
</Manager>

・存储到数据库(PersistenceManager + JDBCStore):
需要把postgresql-9.3-1102.jdbc4.jar拷贝到$CATALINA_HOME/lib里。
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.JDBCStore"
connectionURL="jdbc:postgresql://localhost:5432/tomcat?user=tomcat&password=tomcat"
driverName="org.postgresql.Driver"
sessionIdCol="session_id"
sessionValidCol="valid_session"
sessionMaxInactiveCol="max_inactive"
sessionLastAccessCol="last_access"
sessionTable="tomcat_sessions"
sessionAppCol="app_context"
sessionDataCol="session_data"
/>
</Manager>

CREATE USER tomcat WITH PASSWORD 'tomcat';
CREATE DATABASE tomcat WITH OWNER = tomcat ENCODING = 'UTF8';
CREATE TABLE tomcat_sessions
(
session_id character varying(100) NOT NULL,
valid_session character(1) NOT NULL,
max_inactive integer NOT NULL,
last_access bigint NOT NULL,
app_context character varying(255),
session_data bytea,
CONSTRAINT tomcat_sessions_pkey PRIMARY KEY (session_id)
);
ALTER TABLE tomcat_sessions OWNER TO tomcat;
CREATE INDEX idx_tomcat_sessions_app_context ON tomcat_sessions(app_context);

也可以使用datasource:
<Resource name="jdbc/sessions" auth="Container" type="javax.sql.DataSource"
username="tomcat"
password="tomcat"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/tomcat"
maxActive="20"
maxIdle="10"
validationQuery="select 1" />
<Manager className="org.apache.catalina.session.PersistentManager"
distributable="true" processExpiresFrequency="3" maxIdleBackup="1" >
<Store className="org.apache.catalina.session.JDBCStore"
dataSourceName="jdbc/sessions"
sessionIdCol="session_id"
sessionValidCol="valid_session"
sessionMaxInactiveCol="max_inactive"
sessionLastAccessCol="last_access"
sessionTable="tomcat_sessions"
sessionAppCol="app_context"
sessionDataCol="session_data"
/>
</Manager>


[b]2种Tomcat集群:[/b]
[list][*]Static Tomcat Cluster:不需要Multicast,各自定义配置
[*]Dynamic Tomcat Cluster:无需定义配置,通过heartbeat信号[/list]

静态集群的例子:
<Cluster channelSendOptions="8" channelStartOptions="3" className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" />
</Sender>
<Receiver address="192.168.21.88" autoBind="0"
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
maxThreads="6" port="4100" selectorTimeout="5000"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor" staticOnly="true"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
<!-- 静态Member设置 -->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
<Member
className="org.apache.catalina.tribes.membership.StaticMember"
port="4100"
host="192.168.21.89"
uniqueId="{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2}" />
</Interceptor>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor" />
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*

\.css;.*\.txt;" />
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
</Cluster>


动态集群:
<Engine name="Catalina" defaultHost="www.mysite.com" jvmRoute="[worker name]">
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
<Manager
className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership
className="org.apache.catalina.tribes.membershipt.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"
/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Receiver
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"
/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<!-- 不进行会话共享的List Pattern -->
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;" />
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
</Engine>


[b]会话复制的设置:[/b]
・Enable Multicast routing
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

・conf/server.xml <Cluster>
conf/server.xml
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>

・web.xml <distributable /> 让Tomcat知道你的Web应用可以发布到集群里,需要注意的是放入Session里的东西必须实现serializable。
WEB-INF/web.xml
<web-app
.....
<distributable />
</web-app>

或者META-INF/context.xml
[quote]<Context distributable="true"></Context>[/quote]

如果不想修改自己的web.xml,可以修改CATALINA_BASE/conf/context.xml来
[quote]<Context distributable="true" className="org.apache.catalina.ha.context.ReplicatedContext" >[/quote]

・如果使用硬件做负载均衡的话,也可以配置Tomcat实现会话复制!
・Tomcat集群使用JULI(java.util.logging)日志框架,配置logging.properties里的org.apache.catalina.tribes.MESSAGES。
・Tomcat动态集群可以使用mod_cluster!
・如果Tomcat采用AJP连接建议不要开启8080端口启用AJP的8009端口。
・防止会话劫持Session Hijacking:开启HTTPS使用TLS、用户成功登陆后更换SessiongID。

[b]③同一台机器安装多个Tomcat[/b]
只要合理规划Tomcat配置conf/server.xml中的各个端口,就能在一台服务器上起多个Tomcat实例,一般由于开发测试。
[list][*]80xx -> Tomcat Server Shutdown Port
[*]81xx -> Tomcat Connector Port (HTTP)
[*]82xx -> Tomcat SSL Redirect Port
[*]83xx -> Tomcat AJP Port
[*]40xx -> Tomcat tcp receive port for NioReceiver(4000-4100)[/list]

例:
<Server port="8001" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8101" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8201" />
<Connector port="8201" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
<Connector port="8301" protocol="AJP/1.3" redirectPort="8201" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4001" autoBind="100" selectorTimeout="5000" maxThreads="6" />
</Channel>
</Cluster>
</Engine>
</Service>
</Server>


[b](4)基于Cache服务器的会话管理[/b]
[list][*]Sticky模式:每次请求都会被映射到同一台Web Server,直到该Web Server宕机,session会被存放在服务器本地,等到请求处理完成再同步到memcached服务器;再次请求时被映射到其他Web Server可以从后端memcache中恢复session。
[*]Non-Sticky模式:请求映射到哪儿不一定。当请求到来时,从memcache中加载session;当请求处理完成时,将session再写回到memcache。[/list]

①Memcached Session Manager
[url=https://github.com/magro/memcached-session-manager]https://github.com/magro/memcached-session-manager[/url]

②Redis Session Manager
[url=https://github.com/jcoleman/tomcat-redis-session-manager]https://github.com/jcoleman/tomcat-redis-session-manager[/url]

③Hazelcast
[url=https://github.com/hazelcast/hazelcast]https://github.com/hazelcast/hazelcast[/url]

(5)其他

①跨平台的会话管理:Java、PHP、.NET等系统平台之间的会话管理
需要合理安排会话的存储、数据结构、序列化/反序列化等。

②自动部署
[url=http://serverfault.com/questions/298886/farm-deployer-not-working-in-tomcat-cluster]http://serverfault.com/questions/298886/farm-deployer-not-working-in-tomcat-cluster[/url]

③JMX监视
[url=https://tomcat.apache.org/tomcat-8.0-doc/monitoring.html]https://tomcat.apache.org/tomcat-8.0-doc/monitoring.html[/url]

④开源框架
[list][*]Apache Shiro [url=http://shiro.apache.org/]http://shiro.apache.org/[/url]
[*]Spring Security [url=http://projects.spring.io/spring-security/]http://projects.spring.io/spring-security/[/url]
[*]Spring Session [url=http://projects.spring.io/spring-session/]http://projects.spring.io/spring-session/[/url][/list]

参考:
https://tomcat.apache.org/tomcat-8.0-doc/cluster-howto.html
http://www.datadisk.co.uk/html_docs/java_app/tomcat6/tomcat6_clustering.htm
http://examples.javacodegeeks.com/enterprise-java/tomcat/tomcat-clustering-session-replication-tutorial/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值