java面试八股文--分布式/微服务

CAP理论,BASE理论

Consistency(一致性):

即更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致

对于客户端来说.一致性指的是并发访问时更新过的数据如何获取的问题

从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致

Availability(可用性):

即服务一直可用,而且是正常响应时间.系统能够很好地为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况

Partition Tolerance(分区容错性)

即分布式系统在遇到某结点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务.分区容错性要求能够使应用虽然是一个分布式系统,而看上去缺好像是在一个可以运转正常的整体.比如现在的分布式系统这哪个有一个或者几个机器宕掉了,其他剩余的机器还能够正常运转满足系统需求,对于用户而言并没有什么体验上的影响.

CP和AP:分区容错是必须保证的,当发生网络分区的时候,如果要继续服务,那么强一致性和可用性只能二选一.

BASE是Basically Available(基本可用),Soft  state(软状态)和Eventually consistent(最终一致性)

BASE理论是对CAP中一致性和可用性权衡的结果,其来源对大规模互联网分布式时间的总结,是基于CAP定理逐步演化而来的.BASE卢纶的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

基本可用:

  • 响应时间上的损失:正常情况下,处理用户请求需要0.5s返回结果,但是由于系统出现故障,处理用户请求的时间变为3s
  • 系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,系统的部分非核心功能无法使用。

软状态:数据同步允许一定的延迟

最终一致性:系统中的所有数据副本,在经过一段时间的同步后,最终能达到一个一致的状态,不要求实时

负载均衡算法、类型

1.轮询法

将请求按照顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载

2.随机法

通过系统的随机算法,根据后端服务器的列表大小值来选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,其实际效果越来越接近平均分配调用量到后台的每一台服务器,也就是轮询的结果

3.源地址哈希法

源地址哈西德思想是根据获取客户端的IP地址,通过哈希函数计算得到一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客户端要访问的服务器的序号。采用源地址哈希法进行负载均衡,同一个IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。

4.加权轮询法

不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高,负载低的机器配置更高的权重,让其处理更多的请求;而配置低,负载高的机器,给其分配比较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配给后端

5.加权随机法

与加权轮询法一样,加权随机发也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是感召权重随机请求后端服务器,而非顺序

6.最小连接数法

最小连接数算法灵活只能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用率,将负责合理地分流到每一台服务器。

类型:

DNS方式实现负载均衡

硬件负载均衡:F5和A10

软件负载均衡:

Nginx、HAproxy、LVs 其中的区别:

  • Nginx:七层负载均衡;支持HTTP、E-mail协议,同时也支持4层负载均衡;
  • HAproxy:支持七层规则的,性能也很不错。OpenStack默认使用的负载均衡软件就是HAproxy
  • LVs:运行在内核态,性能上软件负载均衡中最高的,严格来说工作在第三层,所以更通用一些,适用各种应用服务。

分布式架构下,Session共享有什么方案

1.采用无状态服务,抛弃Session

2.存入Cookie(有安全风险)

3.服务器之间进行Session同步,这样可以保证每个服务器上都有全部的Session信息,不过当前服务器数量比较多的时候,同步是会有延迟甚至同步失败

4.IP绑定策略

使用Nginx(或其他复杂均衡软硬件)中的IP绑定策略,同一个IP只能在指定的同一个机器访问,但是这样做失去了复杂均衡的意义,当挂掉一台服务器的时候,会影响一批用户的使用,风险很大

5.使用Redis存储

把Session放到Redis中存储,虽然架构上变得复杂,并且需要多访问一次Redis,但是折中方案带来的好处也是很大的

  • 实现了Session共享
  • 可以水平拓展(增加Redis服务器)
  • 服务器重启Session不丢失(不过也要注意Session在Redis中的刷新/失效机制)
  • 不仅可以跨服务器Session共享,甚至可以跨平台(例如网页端和APP端)

简述你对RPC,RMI的理解

RPC:在本地调用远程的函数,远程过程调用,可以跨语言实现 httpClient

RMI:远程方法调用,java中用于实现RPC的一种机制,RPC的java版本,是J2EE的网络调用机制,跨JVM调用对象的接口,面向对象的思维方式

直接或间接实现接口 java.rmi.Remote 称为存在于服务器端的远程对象,共客户端访问并提供一定的服务

远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时,该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为“存根”,而福诶器端本身自己已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通信,而骨架也可认为是服务器端的一个代理,用于接受客户端的请求之后调用远程方法来响应客户端的请求。

 

 

分布式ID生成方案

uuid

数据自增序列 

Leaf-segment 

基于Redis,mongodb,zk等中间件生成

雪花算法 

分布式所解决方案 

需要这个锁独立于每一个服务之外,而不是在服务里面

数据库:利用主键冲突控制一次只有一个线程能获取锁,非阻塞,不可重入,单点,失效时间

Zookeeper分布式锁:

zk通过临时节点,解决了死锁的问题,一旦客户端获取到锁之后突然挂掉(Session连接断开),那么这个临时节点就会自动删除掉,其余客户端自动获取锁.临时顺序节点解决惊群效应。

Redis分布式锁:setNX,单线程处理网络请求,不需要考虑并发安全性

所有服务节点设置相同的key,返回为,则锁获取失败

setnx

问题:

1.早起版本没有超时参数,需到单独设置,存在死锁问题(中途宕机)

2.后期版本提供加锁与设置时间原子操作,但是存在任务超市,锁自动释放,导致并发问题,加锁与释放锁不是同一线程问题

删除锁:判断线程唯一标志,再删除

可重入性及锁延期没有实现,通过redisson解决(l类似AQS的实现,看门狗监听机制)

redlock:意思的机制都只操作单节点,即使Redis通过sentinel保证高可用,如果这个master结点由于某些原因发生了主从切换,那么就会出现锁丢失的情况(redis同步设置可能数据丢失)。redlock从多个节点申请锁,当一半以上节点获取成功,锁才算获取成功,redission有相应的实现

分布式事务解决方案

XA规范:分布式事务规范,定义了分布式事务模型

四个角色:事务管理者(协调者TM)、资源管理器(参与者RM),应用程序AP,通信资源管理器CRM

全局事务L一个横跨多个数据库的事务,要么全部提交,要么全部回滚

JTA事务时java对XA规范的实现,对应JDBC的单库事务

两阶段协议:

第一阶段(prepare):每个参与者执行本地事务但不提交,进入ready状态,并通知协调者已经准备就绪。

第二阶段(commit):当协调者确定每个参与者都ready后,通知参与者进行commit操作;如果有参与者fail,则发送rollback命令,个参与者做回滚

问题:

  • 单点故障:一旦事务管理器发生故障,整个系统不可用(参与者都会阻塞住)
  • 数据不一致:在阶段二,如果事务管理器只发送了部分commit消息,此时网络发生异常,那么只有部分参与者收到了commit消息,也就是说只有部分参与者提交了事务,使得系统数据不一致
  • 响应时间较长:参与者和协调者资源都被锁住,提交或者混回滚之后才能释放
  • 不确定性:当协事务管理器发送commit之后,并且此时只有一个参与者收到了commit,你们当该参与者与事务管理器同时宕机之后,重新选举的事务管理器无法确定该条消息是否成功提交 

三阶段协议:主要是针对两阶段的优化,解决了2PC单点故障的问题,但是性能问题和不一致问题仍然没有根本解决

引入了超时机制解决参与者阻塞的问题,超时后本地提交,2PC只有协调者有超时机制

  • 第一阶段:CanCommit,协调者询问事务参与者,是否有能力完成此次事务

        如果都返回yes,则进入第二阶段

        如果有一个返回no或等待响应超时,则中断事务,并向所有参与者发送absort请求

  • 第二阶段:PCCommit阶段,此时协调者会向所有的参与者发送PreCommit请求,参与者收到后开始执行事务操作。参与者执行完事务操作后(此时属于未提交事务状态),就会向协调者返回“Ack”表示我已经准备好提交了,并等待协调者的下一步指令。
  • 第三阶段:DoCommit阶段,在阶段二中如果所有的参与者结点都反悔了Ack,那么系统挑着就会从"预提交状态"转变为"提交状态".然后向所有的参与者结点发送"doCommit"请求,参与者节点在受到请求后就会各自执行事务提交工作,并向协调者节点反馈"Ack"消息,协调者受到所有的参与者的Ack消息后完成事务.相反,如果有一个参与者结点未完成PreCommit的反馈或反馈超时,那么协调者都会向所哟肚饿参与者结点发送abort请求,从而中断事务。

TCC(补偿事务):Try、Confirm、Cancel

针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作

Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的操作即回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试

TCC模型对业务的侵入性较强,改造的难度较大,每个操作都需要try,confirm,cancel三个接口实现

confirm和cancel接口还必须实现幂等性

消息队列的事务消息:

  • 发送prepare消息到中间件
  • 发送成功,执行本地事务

        如果事务执行成功,则commit,消息中间件将消息下发至消费端(commit前,消息不会被消费)

        如果事务执行失败,则回滚,消息中间件将这条prepare消息删除

  • 消息端接收到消息进行消费,如果消费失败,则不断重试

如何实现接口的幂等性

  • 唯一id。每次操作,都根据操作和内容生成唯一的id,在执行之前先判断id是否存在,如果不存在则执行后续操作,并且报错到数据库或redis等
  • 服务端提供发送token的接口,业务调用接口前先获取token,然后调用业务接口请求时,把token携带过去,服务器判断token是否存在redis中存在表示第一次请求,可以继续执行业务,执行业务完成后,最后需要把redis中的token删除
  • 建去重表。将业务中有唯一标识的字段保存到去重表,如果表中存在,则表示已经处理过了
  • 版本控制。增加版本号,当前版本号符合时,才能更新数据
  • 状态控制。例如订单有状态已支付 未支付 支付中 支付失败,当处于未支付的时候才允许修改为支付中等

简述ZAB协议

ZAB协议是分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议,实现分布式数据一致性

所有客户端的请求都是写入到Leader进程中,然后,由Leader同步到其他节点,称为Follower。在集群数据同步的过程中,如果出现Follower节点崩溃或者Leader进程崩溃时,都会通过ZAB协议来保证数据一致性

ZAB协议包括两种基本的模式:崩溃恢复和消息广播。

消息广播:

集群中所哟肚饿事务请求都由Leader结点来处理,其他服务器为Follower,Leader将客户端的事务请求转换为事务Proposal,并且将Proposal分支给集群中其他所有的Follower

完成广播之后,Leader等待Follower反馈,当有过半数的Follower反馈信息后,Leader将再次向集群内Follower广播Commit信息,Commit信息就是确认将之前的Proposal提交

Leader结点的写入是一个两步操作,第一步使广播事务操作,第二部是广播提交操作,其中过半数指的是反馈的结点数>=N/2 + 1,N是全部的Follower节点数量

崩溃恢复:

  • 初始化集群,刚刚启动的时候
  • Leader崩溃,因为故障宕机
  • Leader失去了半数的及其支持,与急群众超过一般的节点断连

此时开启新一轮Leader选举,选举产生的Leader会与国办的Follower进行同步,使数据一致,当与过半的机器同步完成后,就退出恢复模式,然后进入消息广播模式

整个ZooKeeper集群的一致性保证就是在上面两个状态之前切换,当Leader服务正常时,就是正常的消息广播模式;当Leader不可用,则进入崩溃恢复模式,崩溃恢复阶段会进行数据同步,完成以后,重新进入消息广播阶段

Zxid是Zab协议的事务编号,Zxid是一个64位的数字,其中低32位是一个简单的单调递增计数器,针对客户端每一个事务请求,计数器+1;而高32位则代表Leader周期年代的编号

Leader周期(epoch),可以理解为当前集群所处的年代或者周期,每当有一个新的Leader选举出现时,就会从这个Leader服务器上取出其本地日志中最大事务的Zxid,并且从中读取epoch值,然后加1,以此作为新的周期ID.高32位代表了每台Leader的唯一性,低32位则代表了每代Leader中事务的唯一性.

zab结点的三种状态:

following:服从leader的命令

leading:负责协调事务

election/looking:选举状态

zk的数据模型和节点类型

数据模型:树形结构

zk维护的数据是主要有:客户端的会花(session)状态及数据节点(dataNode)信息

zk在内存中构造了个DataTree的数据结构,维护者path到dataNode的映射以及dataNode间的树状层级关系,为了提高读取性能,集群中每个服务节点都是将数据全量存储在内存中。所以,zk最适于读多写少且轻量级的数据的应用场景。

数据仅存储在内存是很不安全的,zk采用事务日志文件及快照文件的方案老落盘数据,保障数据在不丢失的情况下能快速恢复。

树中的每个结点被称为--Znode

Znode兼具文件和目录两种特点。可以做路径标识,也可以存储数据,并可以具有子Znode。具有增删改查等操作

Znode具有原子性操作,读操作将获取与节点相关的所有数据,写操作也将替换掉结点的所有数据,另外,每一个几点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作

Znode存储数据大小有限制,每个Znode的数据大小至多1M,常规使用中应该远小于此值。

Znode通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此之外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在Zookeeper中,路径由Uniclde字符串组成,并且有一些限制。字符串“/zookeeper”用以保存管理信息,比如关键配额信息

持久节点:一旦创建,该数据节点会一直存储在zk服务器上,及时创建该节点的客户端与服务端的会话关闭了,该节点也不会被删除

临时节点:当创建该节点的客户端会话因超时或发生异常而关闭时,该节点也相应的在zk上被删除

有序结点:不是一种单独种类的接地那,而是在持久结点和临时节点的基础上,增加了一个节点有序的性质

简述zk的命名服务,配置管理,集群管理

命名服务:

通过指定的名字来获取资源或者服务地址。Zookeeper可以创建一个全局唯一的路径,这个路径就可以作为一个名字。被命名的实体可以是集群中的机器,服务的地址,或者是远程的对象等。一些分布式服务框架(RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应能够根虎特定的名字来获取资源的实体、服务地址和提供者信息等

配置管理:

实际项目开发中,经常使用.properties或者xml需要配置很多信息,如数据库连接信息,fps地址端口等。程序分布式部署时,如果把程序的这些配置信息先保存在zk的znode节点下,当你要修改配置,即znode会发生变化时,可以通过改变zk中讴歌目录节点的内容,利用watcher通知各个客户端,从而更改配置。

集群管理:

集群管理包括集群监控和集群控制,就是监控集群机器状态,剔除机器和加入机器。zookeeper可以方便集群机器的管理,它可以实时监控znode节点的变化,一旦发现有机器挂了,该机器就会与zk断开连接,对应的临时目录节点会被删除,其他所有机器都收到通知。新机器加入也是类似

讲下Zookeeper watch 机制

客户端,可以通过在znode上设置watc,实现实时监听znode的变化

Watch时间是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端

  • 父节点的创建,修改,删除都会出发Watch事件
  • 子节点的创建,删除会触发Watcher事件

一次性:一旦被出发就会移除,再次使用需要重新认证,因为每次变动都需要通知所有客户端,一次性可以减轻压力,3.6.0默认持久递归,可以触发多次

轻量:只通知发生了事件,不会告知事件内容,减轻服务器和带宽压力

Watcher机制包括三个角色:客户端线程/客户端的WatchManager以及Zookeeper服务器

  1. 客户端向Zookeeper服务器注册一个Watcher监听
  2. 把这个监听信息存储到客户端的WatchManager中
  3. 当ZokkKeeper中的节点发生变化时,会通知客户端,客户端会调用相应Watcher对象中的回溯算法.watch回调是串行同步的

zk和eureka的区别

zk:CP设计(强一致性),目标是一个分布式的协调系统,用于进行资源的统一管理

当节点crash后,需要进行leader的选举,在这个期间内,zk服务是不可用的

eureka:AP设计(高可用),目标是一个服务注册发现系统,换门用于微服务的服务发现注册

Eureka各个节点是平等的,几个节点挂掉不会影响正常节点的工作,生育的节点依然可以提供注册和查询服务.而Eureka的客户端在向某个Eureka注册时如果发现连接失败,会自动切换到其他节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)

同时当eureka的服务端发现85%以上的服务都没有心跳的话,它就会认为自己的网络出了问题,就不会从服务列表中删除这些失去心跳的服务,同时eureka的客户端也会缓存服务信息。eureka对于服务注册发现来说是非常好的选择。

Spring Cloud 和 Dubbo的区别

底层协议:spring Cloud基于http协议,dubbo基于Tcp协议,决定了dubbo的心梗相对会比较好

注册中心:Spring Cloud使用的eureka,dubbo推荐使用Zookeeper

模型定义:dubbo将一个接口定义为一个服务,SpringCloud则是将一个应用定义为一个服务

SpringCloud是一个生态,而Dubbo是SpringCloud生态中关于服务微调的一种解决方案(服务治理)

什么是Hystrix?简述实现机制

分布式容错框架

  • 组织故障的连锁反应,实现熔断
  • 快速失败,实现优雅降级
  • 提供实时的监控和警告

资源隔离:线程隔离,信号量隔离

  • 线程隔离:Hystrix会给每一个Command分配一个单独的线程池,这样在进行单个服务调用的时候,就可以在独立的线程池里面进行,而不会对其他线程造成影响
  • 信号量隔离:客户端需向依赖服务发起请求时,首先要获取一个信号量才能真正发起调用,由于信号量的数量优先,当并发请求量超过信号量个数时,后续的请求都会直接拒绝,进入fallback流程。信号量隔离主要是通过控制并发请求量,防止请求线程大面积阻塞,从而达到限流和防止雪崩的目的

熔断和降级:调用服务失败后快速失败

熔断是为了方式异常不扩散,保证系统的稳定性

降级:编写好调用失败的不就措施,然后队伍赴直接停止运行,这样这些借口就无法正常调用,但又不至于直接报错,知识服务水平下降

  • 通过HystrixCommand或者HystrixObsableCommand将所有的外部系统(或者称为依赖)包装起来,整个包装对象是单独运行在一个线程之中(这就是典型的命令模式)
  • 超时请求应该超过你定义的阈值
  • 为每个依赖关系维护一个小的线程池(或信号量);如果它变满了,那么依赖关系的请求将立即被决绝,而不是排队等待
  • 统计成功,失败(由客户端抛出的异常),超时和线程拒绝
  • 打开断路器可以在一段室内停止对特定服务的所有请求,如果服务的错误百分比通过阈值,手动或自动的关闭断路器
  • 当请求被拒绝,连接超时或断路器打开,直接执行fallback逻辑
  • 近乎实时监控指标和配置变化

springcloud核心组件及其作用

 Eureka:服务注册与发现

注册:每个服务都向Eureka登记自己提供服务的元数据,包括服务的ip地址、端口号、版本号、通信协议等。eureka将各个服务维护在了一个服务清单中(双层Map,第一层key是服务名,第二层key是实例名,value是服务地址加端口)。同时对服务维持心跳,剔除不可用的服务,eureka集群各节点相互注册每个实例都有一样的服务清单

发现:eure注册的服务之间调用不需要指定服务地址,而是通过服务名向注册中心咨询,并获取所有的服务实例清单(缓存到本地),然后实现服务的请求访问

Ribbon:服务器发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台(被调用方的服务地址有多个),Ribbon也是通过发起http请求,来进行的调用,只不过是通过调用服务名的地址来实现的。虽然说Ribbon不用去具体请求服务实例的ip地址或域名了,但是每调用一个接口都还要手动去发起http请求

Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求,简化服务间的调用,在Ribbon的基础上进行了进一步的封装。单独抽出了一个组件,就是Spring Cloud Feign。在引入Spring Cloud Feign后,我们只需要创建一个接口并用注解的方式来配置它,及可完成对服务提供方的接口绑定。

调用远程就像调用本地服务一样

 

 

Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不通的线程池,实现不同服务调用的隔离,通过统计接口超时次数返回默认值,实现服务熔断和降级

Zuul:如果前端,移动端要调用后端系统,同一从Zuul网关进入,由Zuul网关转发请求给对应服务,通过与Eureka进行整合,将自身注册为Eureka下的应用,从Eureka下获取所有服务的实例,来进行服务的路由。Zuul还提供了一套过滤器机制,开发者可以自己指定哪些规则的请求需要执行逻辑校验,只有通过校验逻辑的请求才会被路由到具体服务实例上,否则返回错误提示。

Dubbo的整体架构设计及分层

 五个角色:

注册中心registry:服务注册与发现

服务提供者provider:暴露服务

服务消费者consumer:调用远程服务

监控中心monitor:统计服务的调用次数和调用时间

容器container:服务允许容器

调用流程:

  1. container容器负责启动,加载。运行provider
  2. provide在启动时,想registry中心注册自己提供的服务
  3. consumer在启动时,向registry中心订阅自己所需的服务
  4. registry返回服务器提供者列表给consumer,如果有变更,regi将基于长连接推送变更数据给consumer
  5. consumer调用provider服务,基于负载均衡算法进行调用
  6. consumer调用provider的统计,基于短链接定时每分钟一次统计到monitor

分层:

接口服务层(Service):面向开发者,业务代码,接口,实现等

配置层(Config):对外配置接口,及ServiceConfig和ReferenceConfig为中心

服务代理层(Proxy):对生产者和消费者,dubbo都会产生一个代理类封装调用细节,业务层对远程调用无感

服务注册层(Registry):封装服务地址的注册和发现,以服务URL为中心

路由层(Cluster):封装多个提供者的路由和复杂均衡,并桥接注册中心

监控层(Monitor):RPC调用次数和调用时间监控

远程调用层(Protocal):封装RPC调用

信息交换层(Exchange):封装请求响应模式,同步转异步

网络传输层(Transport):抽象mina和netty为统一接口,统一网络传输接口

数据序列化层(Serialize):数据传输的序列化和反序列化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值