消息队列——RabbitMQ消息队列集群

消息队列/中间件

一、前言

  • 在我们秒杀抢购商品的时候,系统会提醒我们稍等排队中,而不是像几年前一样页面卡死或报错给用户
  • 像这种排队结算就用到了消息队列机制,放入通道里面一个一个结算处理,而不是某个时间断突然涌入大批量的查询新增把数据库给搞宕机
  • 所以RabbitMQ本质上起到的作用就是削峰填谷,为业务保驾护航。

在这里插入图片描述
二、MQ简介

  • MQ全称为Message Queue,消息队列。是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间,通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信。队列的使用除去了接收和发送应用程序,同时执行的要求。
  • 在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式,大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量,

三、核心功能

  • 解耦(将不同的系统分离开)将PHP于数据库之间解耦
  • 冗余(存储)
  • 扩展性
  • 削峰
  • 可恢复性
  • 顺序保证
  • 缓存
  • 异步通信

四、MQ的分类

p2p模式(安全,点到点)

point to point (p2p)

  • p2p模式包含的三个角色

消息队列自身(Queue)
发送者(Sender)
接受者(Receiver)
每个消息都被发送到一个特定的队列,接受者从队列中获取消息,队列保留着消费信息,直到他们被消费或超时

  • p2p特点如下:

每一个消息只有一个消费者(Consumer),即一旦被消费,消息就不再消息队列中
发送者和接受者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接受者有没有正在运行它不会影响到消息被发送到队列
接受者在成功接收消息之后,需向队列应答成功
如果希望发送的每个消息都会被成功处理,那么需要p2p模式

Pub/Sub模式(并发)

Publish/Subscribe

  • Pub/Sub模式包含三个角色:主题.( Topic )、发布者( Publisher )、订阅者(Subscriber )。多个者将消息发送到Topic,系统将这些消息传递给多个订阅者。Pub/Sub的特点如下︰

每个消息可以有多个消费者
发布者和订阅者之间有时间上的依赖性。针对某个主题( Topic )的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息
为了消费消息,订阅者必须保持运行的状态
如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型

五、 同类产品

  • Kafka

Kafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache顶级项目。Kafka主要特点是基于Pul的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)要求稍低的场景使用,比如ELK日志收集。

  • ★RabbitMQ★

RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。RabbitMQ比Kafka可靠

  • RocketMQ

RocketMQ是阿里开源的消息中间件,它是纯ava开发,具有高吞吐星、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。

RabbitMQ详解

一、 RabbitMQ简介

  • RabbitMQ是一个在AMQP ( Advanced Message Queuing Protocol )基础上实现的,可复用的企业消息系统。它可以用于大型软件系统,各个模块之间的高效通信,支持高并发,支持可扩展。它支持多种客户端如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX,持久化,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
  • RabbitMQ是使用Erlang编写的一个开源的消息队列,本身支持很多的协议︰AMQP,XMPP, SMTP, STOMP,也正是如此,使的它变的非常重星级,更适合于企业级的开发。它同时实现了一个Broker构架,这意味着消息在发送给客户端时先在中心队列排队,对路由(Routing)、负载均衡(Load balance)或者数据持久化都有很好的支持。

二、RabbitMQ特点

  • 可靠性
  • 灵活的路由
  • 扩展性
  • 高可用性
  • 多种协议
  • 多语言客户端
  • 管理界面
  • 插件机制

三、AMQP介绍

  • AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。

四、RabbitMQ应用场景

对于一个大型的软件系统来说,它会有很多的组件或者说模块或者说子系统( subsystem or Component or submodule )。那么这些模块的如何通信?这和传统的IPC有很大的区别。传统的IPC很多都是在单一系统上的,模块耦合性很大,不适合扩展(Scalability );如果使用socket那么不同的模块的确可以部署到不同的机器上,但是还是有很多问题需要解决。比如:

  • 信息的发送者和接收者如何维持这个连接,如果一方的连接中断,这期间的数据会不会丢失?
  • 如何降低发送者和接收者的耦合度?
  • 如何让优先级高的接收者先接到数据?
  • 如何做到load balance ?有效均衡接收者的负载?
  • 如何有效的将数据发送到相关的接收者?也就是说将接收者subscribe处理不同的数据,如何做有效的filter。
  • 如何做到可扩展,甚至将这个通信模块发到cluster上?
  • 如何保证接收者接收到了完整,正确的数据?AMQP协议解决了以上的问题,而RabbitMO实现了AMOP

五、RabbitMQ架构图示

  • RabbitMQ从整体上来看是一个典型的生产者消费者模型,主要负责接收、存储和转发消息

在这里插入图片描述

六、RabbitMQ常用术语

  • Message:消息,是有消息头和消息体组成的。消息体是不透明的,消息体是由一些可选属性组成的,包括路由键(routing-key)、优先级(priority)、持久性存储(delivery-mode)等
  • publisher:消息生产者,是向交换器发送消息的客户端程序,我们可以简单理解为就是一个Java程序
  • Exchange:交换器,用来接收生产者发送的消息,并将这些消息路由发送给服务器中的队列。消息要先经过交换器(exchange),再到队列中去。
  • Queue:消息队列,用来保存消息,直接发送给消费者,消息的容器,也是消息的终点,一个消息可投入一个或多个队列,消息一致在队列里面,等待消费者连接者连接到这个队列将其取走
  • Bingding:绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键,将交换器和消息队列连接起来的路由规则,所以可以将交换器理解为一个由绑定构成的路由表
  • Virtual Host:虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加h密环境的独立服务器区域,每个vhost本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列、交换器、绑定和权限机制,vhost是AMQP概念的基础,必须再连接时指定,RabbitMQ默认的vhost是"/"
  • Broker:可以理解为rabbitmq的服务器实体,可以理解为在Linux上创建的虚拟机实体
  • Connection:连接,我们可以理解为rabbitmq服务器和app服务建立的TCP的连接。
  • Channel:信道,也可以成为管道。是TCP里的虚拟连接。-条TCP连接,可以包含很多条的Channel。例如∶电缆相当与TCP,信道是—条独立光纤束,一条TCP连接上创建多少条信道是没有限制的,TCP一旦打开,就会创建AMQP信道,无论是发布消息、接收消息、订阅消息,这些动作都是通过信道完成的
  • Consumer:消息的消费者,表示一个从消息队列中取得消息的客户端应用程序,和Producer类似,我们可以简单理解为就是一个Java程序

RabbitMQ单机部署

#环境
192.168.100.10 rabbitmq1
192.168.100.11 rabbitmq2
192.168.100.12 rabbitmq3

#域名解析
[root@rabbitmq1 ~]# vim /etc/hosts
192.168.100.10 rabbitmq1
192.168.100.11 rabbitmq2
192.168.100.12 rabbitmq3
[root@rabbitmq1 ~]# scp -r /etc/hosts 192.168.100.11:/etc/
[root@rabbitmq1 ~]# scp -r /etc/hosts 192.168.100.12:/etc/
  • 安装Erlang
    RabbitMQ是由Erlang语言编写
[root@rabbitmq1 ~]# yum -y install erlang-21.3.8.6-1.el7.x86_64-2.rpm
  • 安装RabbitMQ
[root@rabbitmq1 ~]# yum -y install rabbitmq-server-3.7.13-1.el7.noarch.rpm
  • 修改配置文件
#将默认示例配置文件复制至工作路径下作为配置文件
[root@rabbitmq1 ~]# cp /usr/share/doc/rabbitmq-server-3.7.13/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
[root@rabbitmq1 ~]# vim /etc/rabbitmq/rabbitmq.config
61    {loopback_users, []}                                                  #配置文件61行去除%%和,开通默认测试账号
  • 安装插件并启动服务
[root@rabbitmq1 ~]# rabbitmq-plugins enable rabbitmq_management             #WEB界面管理工具
[root@rabbitmq1 ~]# systemctl restart rabbitmq-server                       #插件安装完成后重启
[root@rabbitmq1 ~]# rabbitmqctl cluster_status                              #rabbitmq集群查看工具,查看节点信息
Cluster status of node rabbit@rabbitmq1 ...                                 #集群状态在这个节点上rabbit@rabbitmq1
[{nodes,[{disc,[rabbit@rabbitmq1]}]},
 {running_nodes,[rabbit@rabbitmq1]},                                        #运行
 {cluster_name,<<"rabbit@rabbitmq1">>},                                     #集群名称
 {partitions,[]},                                                           #集群位置
 {alarms,[{rabbit@rabbitmq1,[]}]}]                                          #单机部署,如集群部署会显示多个主机
  • 访问测试
http://192.168.100.10:15672

在这里插入图片描述

  • 登陆后主页面
    在这里插入图片描述

  • 设置虚拟主机与添加用户
    在这里插入图片描述

  • 命令行添加用户,设置tags(标签)

[root@rabbitmq1 ~]# rabbitmqctl list_queues                                      #查看所有队列
[root@rabbitmq1 ~]# rabbitmqctl reset                                            #清除所有队列
[root@rabbitmq1 ~]# rabbitmqctl add_user pakho pakho                             #添加用户及密码
[root@rabbitmq1 ~]# rabbitmqctl set_user_tags pakho administrator                #设置用户为超管
[root@rabbitmq1 ~]# rabbitmqctl add_vhost vhost_pakho                            #新增虚拟主机
[root@rabbitmq1 ~]# rabbitmqctl set_permissions -p vhost_pakho pakho "." "." "." #将虚拟主机授权给新用户(后面3个点代表拥有配置、写、读全部权限)

RabbitMQ集群部署

  • 消息中间件RabbitMQ,一般以集群方式部署,主要提供消息的接受和发送,实现各微服务之间的消息异步,
  • 以下将以Rabbit+HA方式进行部署
  • Cookie:RabbitMQ底层是通过Erlang架构来实现的,所以rabbitmqctl会启动Erlang节点,并基于Erlang节点来使用Erlang系统连接RabbitMQ节点,在连接过程中需要正确的Erlang Cookie和节点名称,Erlang节点通过交换Erlang Cookie以获得认证来实现布式,所以部署Rabbitmq分布式集群时要先安装Erlang,并把其中一个服务的cookie复制到另外的节点。
  • 内存节点和磁盘节点:RabbitMQ集群中,各个RabbitMQ为对等节点,即每个节点均提供给客户端连接,进行消息的接收和发送。节点分为内存节点和磁盘节点,一般都建立为磁盘节点,为了防止机器重启后的消息消失
  • 普通模式和镜像模式:RabbitMQ的Cluster集群模式一般分为两种,普通模式和镜像模式。消息队列通过RabbitMO HA镜像队列进行消息队列实体复制。
#环境
所有节点需要在同一局域网内
所有节点需要有相同的erlang cookie,否则不能通信,为了实现cookie一致,采用scp方式
#域名解析:略 同上

#将软件包使用scp拷贝至其余两台服务器
[root@rabbitmq1 ~]# scp -r erlang-21.3.8.6-1.el7.x86_64-2.rpm rabbitmq2:/usr/src/
[root@rabbitmq1 ~]# scp -r rabbitmq-server-3.7.13-1.el7.noarch.rpm rabbitmq2:/usr/src/

[root@rabbitmq1 ~]# scp -r erlang-21.3.8.6-1.el7.x86_64-2.rpm rabbitmq3:/usr/src/
[root@rabbitmq1 ~]# scp -r rabbitmq-server-3.7.13-1.el7.noarch.rpm rabbitmq3:/usr/src/
  • 安装Erlang
[root@rabbitmq2 src]# yum -y install erlang-21.3.8.6-1.el7.x86_64-2.rpm
[root@rabbitmq3 src]# yum -y install erlang-21.3.8.6-1.el7.x86_64-2.rpm
  • 安装RabbitMQ
[root@rabbitmq2 src]# yum -y install rabbitmq-server-3.7.13-1.el7.noarch.rpm
[root@rabbitmq3 src]# yum -y install rabbitmq-server-3.7.13-1.el7.noarch.rpm
  • 修改配置文件
#rabbitmq2
[root@rabbitmq2 ~]# cp /usr/share/doc/rabbitmq-server-3.7.13/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
[root@rabbitmq2 ~]# vim /etc/rabbitmq/rabbitmq.config
61    {loopback_users, []}                                                  #配置文件61行去除%%和,开通默认测试账号
 
#rabbitmq3
[root@rabbitmq3 ~]# cp /usr/share/doc/rabbitmq-server-3.7.13/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
[root@rabbitmq3 ~]# vim /etc/rabbitmq/rabbitmq.config
61    {loopback_users, []}                                                  #配置文件61行去除%%和,开通默认测试账号
  • 安装插件
[root@rabbitmq2 ~]# rabbitmq-plugins enable rabbitmq_management             #WEB界面管理工具

[root@rabbitmq3 ~]# rabbitmq-plugins enable rabbitmq_management             #WEB界面管理工具
  • 设置Erlang运行节点并运行
    一定要保证三台机器的cookie内容一致
    • 找到erlang cookie文件的位置,源码包部署一般会存在.erlang.cookie文件; rpm包部署一般是在/var/lib/rabbitmq/).erlang.cookie。将node1的该文件使用rsync或者是scp复制到node2、node3,文件权限需要是400。
[root@rabbitmq1 ~]# scp -r /var/lib/rabbitmq/.erlang.cookie rabbitmq2:/var/lib/rabbitmq/.erlang.cookie
[root@rabbitmq1 ~]# scp -r /var/lib/rabbitmq/.erlang.cookie rabbitmq3:/var/lib/rabbitmq/.erlang.cookie
cat /var/lib/rabbitmq/.erlang.cookie                                         #拷贝完成后检查对比是否一致 
[root@rabbitmq2 ~]# systemctl restart rabbitmq-server
[root@rabbitmq3 ~]# systemctl restart rabbitmq-server

#关闭rabbitmq2和rabbitmq3集群功能
[root@rabbitmq2 ~]# rabbitmqctl stop
[root@rabbitmq3 ~]# rabbitmqctl stop

#设置2.3独立运行节点,warning提示不用理会
[root@rabbitmq2 ~]# rabbitmq-server -detached
[root@rabbitmq3 ~]# rabbitmq-server -detached
Warning: PID file not written; -detached was passed.

#查看各节点状态
[root@rabbitmq1 ~]# rabbitmqctl cluster_status                               #查看node1节点,其他节点也均为独立
Cluster status of node rabbit@rabbitmq1 ...
[{nodes,[{disc,[rabbit@rabbitmq1]}]},
 {running_nodes,[rabbit@rabbitmq1]},
 {cluster_name,<<"rabbit@rabbitmq1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbitmq1,[]}]}]                                           #每台主机只有一个server信息未组成集群
  • 添加用户并设置密码
    加入集群
  • 由于guest这个用户,只能在本地访问,所以我们要在每个节点上,新增一个用户并赋予对/的所有权限,然后添加到管理员组中,让此用户能够远程访问
[root@rabbitmq1 ~]# rabbitmqctl add_user admin admin
[root@rabbitmq1 ~]# rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"#为admin用户设置虚拟主机并将整个/赋予admin身份可读可写可配置
[root@rabbitmq1 ~]# rabbitmqctl set_user_tags admin administrator          #设置admin为管理员身份,但仅限本机登陆

需要给node2和node3配置相同的账号

[root@rabbitmq2 ~]# rabbitmqctl add_user admin admin
[root@rabbitmq2 ~]# rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
[root@rabbitmq2 ~]# rabbitmqctl set_user_tags admin administrator
[root@rabbitmq3 ~]# rabbitmqctl add_user admin admin
[root@rabbitmq3 ~]# rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
[root@rabbitmq3 ~]# rabbitmqctl set_user_tags admin administrator
  • 访问测试
http://192.168.100.11:15672/                                               #均使用root账号登录
http://192.168.100.12:15672/

在这里插入图片描述
在这里插入图片描述

  • 组成集群
    • rabbitmq-server启动时,会一起启动:节点和应用,它预先设置RabbitMQ应用为standalone (脱机)模式。要将一个节点加入到现有的集群中,你需要停止这个应用并将节点设置为原始状态。如果使用rabbitmqctl stop,应用和节点都将被关闭。所使用rabbitmqctl stop_app仅仅关闭应用。(停应用,不停止节点)
#将node2和node3加入node1中组成集群

#                               <!--           使用磁盘节点              -->

#rabbitmq2
[root@rabbitmq2 ~]# rabbitmqctl stop_app                                   #关闭节点应用
[root@rabbitmq2 ~]# rabbitmqctl join_cluster rabbit@rabbitmq1              #连接集群rabbitmq1
Clustering node rabbit@rabbitmq2 with rabbit@rabbitmq1                     #群集节点rabbit@rabbitmq2 与rabbit@rabbitmq1
[root@rabbitmq2 ~]# rabbitmqctl start_app                                  #启动节点应用

#rabbitmq3
[root@rabbitmq3 ~]# rabbitmqctl stop_app                                   #关闭节点应用
[root@rabbitmq3 ~]# rabbitmqctl join_cluster rabbit@rabbitmq1              #连接集群rabbitmq1
Clustering node rabbit@rabbitmq3 with rabbit@rabbitmq1                     #群集节点rabbit@rabbitmq2 与rabbit@rabbitmq1
[root@rabbitmq3 ~]# rabbitmqctl start_app                                  #启动节点应用
  • 在任意节点查看集群状态
#node1
[root@rabbitmq1 ~]# rabbitmqctl cluster_status                              
Cluster status of node rabbit@rabbitmq1 ...
[{nodes,[{disc,[rabbit@rabbitmq1,rabbit@rabbitmq2,rabbit@rabbitmq3]}]},
 {running_nodes,[rabbit@rabbitmq3,rabbit@rabbitmq2,rabbit@rabbitmq1]},
 {cluster_name,<<"rabbit@rabbitmq1">>},                                     #集群名称rabbit@rabbitmq1
 {partitions,[]},
 {alarms,[{rabbit@rabbitmq3,[]},{rabbit@rabbitmq2,[]},{rabbit@rabbitmq1,[]}]}]

#node2
[root@rabbitmq2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbitmq2 ...
[{nodes,[{disc,[rabbit@rabbitmq1,rabbit@rabbitmq2,rabbit@rabbitmq3]}]},
 {running_nodes,[rabbit@rabbitmq3,rabbit@rabbitmq1,rabbit@rabbitmq2]},
 {cluster_name,<<"rabbit@rabbitmq1">>},                                     #集群名称rabbit@rabbitmq1
 {partitions,[]},
 {alarms,[{rabbit@rabbitmq3,[]},{rabbit@rabbitmq1,[]},{rabbit@rabbitmq2,[]}]}]

#node3
[root@rabbitmq3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbitmq3 ...
[{nodes,[{disc,[rabbit@rabbitmq1,rabbit@rabbitmq2,rabbit@rabbitmq3]}]},
 {running_nodes,[rabbit@rabbitmq1,rabbit@rabbitmq2,rabbit@rabbitmq3]},
 {cluster_name,<<"rabbit@rabbitmq1">>},                                     #集群名称rabbit@rabbitmq1
 {partitions,[]},
 {alarms,[{rabbit@rabbitmq1,[]},{rabbit@rabbitmq2,[]},{rabbit@rabbitmq3,[]}]}]
  • 设置镜像队列策略
http://192.168.100.10:15672

在这里插入图片描述设置管理员pakho
在这里插入图片描述

http://192.168.100.11:15672                                                   #node2

在这里插入图片描述

http://192.168.100.12:15672                                                  #node3

在这里插入图片描述

在OpenStack中,RabbitMQ是消息代理的核心组件,负责处理各种消息,包括API请求、队列通信和通知。为了保证高可用性和可扩展性,我们可以将RabbitMQ部署为集群。 以下是在OpenStack中部署RabbitMQ集群的步骤: 1. 安装RabbitMQ软件包 在每个节点上安装RabbitMQ软件包。可以使用以下命令进行安装: ``` sudo apt-get install rabbitmq-server ``` 2. 配置RabbitMQ 在每个节点上,编辑RabbitMQ配置文件/etc/rabbitmq/rabbitmq-env.conf,指定以下环境变量: ``` NODE_IP_ADDRESS=<本地IP> NODE_PORT=5672 CLUSTER_WITH=<其他节点IP> ``` 其中,NODE_IP_ADDRESS是本地IP地址,CLUSTER_WITH是其他节点的IP地址。这些变量将用于RabbitMQ节点间通信。 3. 启用RabbitMQ插件 在每个节点上启用RabbitMQ集群插件。可以使用以下命令启用插件: ``` sudo rabbitmq-plugins enable rabbitmq_management rabbitmq_peer_discovery_aws ``` 4. 启动RabbitMQ节点 在每个节点上启动RabbitMQ节点。可以使用以下命令启动节点: ``` sudo rabbitmq-server -detached ``` 5. 将节点加入集群 在任何一个节点上,执行以下命令将节点加入集群: ``` sudo rabbitmqctl stop_app sudo rabbitmqctl join_cluster rabbit@<其他节点名称> sudo rabbitmqctl start_app ``` 其中,<其他节点名称>是集群中的其他节点的名称。 6. 验证集群状态 在任何一个节点上,执行以下命令验证集群状态: ``` sudo rabbitmqctl cluster_status ``` 如果输出包含所有节点的信息,则集群已成功部署。 注意:在部署RabbitMQ集群时,需要确保集群节点之间的网络连接正常,并且防火墙已正确配置。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

611#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值