我的面试题准备(项目篇)

什么是Dubbo?谈谈你对Dubbo的理解

Dubbo是阿里巴巴开源的,基于Java的高性能RPC远程调用分布式服务框架,提供了高性能透明化远程服务调用方案,以及SOA服务治理方案
这也是为什么我们需要用到Dubbo的原因,随着服务化的进一步发展,服务越来越多,服务之间的调用和依赖关系也越来越负载。传统的单一应用架构、垂直应用框架满足不了要求,诞生了SOA。Dubbo也这样产生了。
核心部分包括:

  1. 远程通讯:提供多种基于长连接的NIO框架抽象封装,包括多种线程模型、序列化,以及“请求-相应”模式的信息交换方式。
  2. 集群容错:基于接口方法的透明远程调用,包括多协议支持、负载均衡、动态配置等集群支持。
  3. 自动发现:基于注册中心服务目录,使服务消费方能动态查找服务提供方,使地址透明。
  • 什么是SOA服务治理方案?
    SOA:面向服务的架构,是一种设计方法,包括多个服务,服务之间通过相互依赖关系提供一系列的功能。
    特点:有序 高效 复用

  • SOA和微服务的区别?
    SOA组件大块业务逻辑(粗粒度),微服务组件单独、小块业务逻辑(细粒度)。
    SOA通常松耦合,微服务总是松耦合。
    SOA着重中央管理,微服务去中性化管理。
    SOA轻量级通信,微服务企业服务总线充当服务之间通信的角色。

为什么使用Dubbo?

1.(从产业现状说)因为是阿里的开源项目,现在国内外有很多互联网公司都在使用,这已经经过了很多线上考验。内部使用了ZooKeeper、Netty,保证了高性能高可用性。
2.(从应用场景说)使用Dubbo可以将核心业务提取出来,作为独立的业务,逐渐形成稳定的服务中心,用于提高业务复用灵活扩展,应用就可以更好的相应多变的市场需求。
3.(从分布式说)分布式架构可以承受更大规模的并发流量。

除了Dubbo,你还用过哪些Rpc框架?

  1. Thrift:用来进行可扩展且跨语言的服务的开发。
  2. gRPC:Google开发。
  3. Spring Cloud:基于Spring Boot,提供搭建分布式系统以及微服务常用的工具。

分布式系统的CAP定理了解吧?

  1. C一致性,所有节点访问同一份最新的数据副本。
  2. A可用性,非故障的节点在合理的时间内返回合理的响应,不出现错误或者超时。
  3. P分区容错性,分布式系统出现网络分区的时候仍然能对外提供服务。
  4. 在前提是系统发生了分区的情况下,CAP只能满足CP或者AP。但是没有发生分区情况,节点间网络通信正常,不存在P,可以同时保证C和A。

说说你这项目对CAP定理是如何实现的?

因为我这个RPC项目是使用了ZooKeeper作为其注册中心,所以保证了CP。ZooKeeper任何时刻对其访问请求都能得到一致性的数据结果,同时系统对网络分割具有容错性,但是它不能保证每次服务的可用性。
在实际情况下,使用ZooKeeper获取服务列表时候,如果zk正在选举或者zk集群中半数以上的机器不可用,那么将无法获取数据,因此,zk不能保证可用性,即只实现了CP。

那如果你想实现AP,该如何实现?/ 谈一谈CAP的实际案例?

使用Eureka作为注册中心,Eureka保证的是AP,它在设计的时候就是首先保证可用性,因为在Eureka中不存在什么Leader节点,每个节点都是一样平等的。因此不同于ZooKeeper,Eureka不会出现选举过程中或者半数以上的机器不可用就是不可用的情况。Eureka保证即使大多数节点挂掉也不会影响正常服务,只要一个节点是可用就可以。
Nacos不仅支持CP,也支持AP。(原理待扩充)

你的项目中用到了ZooKeeper,谈一谈你对这个的理解 / ZooKeeper相关问题

ZooKeeper是什么?

ZK是一个开源的分布式协调服务,设计目标是将那些复杂且容易出错的分布式一致性服务(C)封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
ZK为我们提供一系列高可用、高性能、稳定的分布式数据一致性解决方案。

ZooKeeper的特点?

  1. 顺序一致性:同一客户端发送的事务请求,按照严格的顺序被应用到ZK中。
  2. 原子性:所有的事务请求在整个集群上所有机器的处理结果都是一致的,要么都成功应用了某个事务,要么都没有应用。
  3. 单一系统映像:无论客户端连到哪一个ZK服务器上,其看到的服务端数据类型都是一致的(C)。
  4. 可靠性:一旦一次更改请求被应用,更改的结果就会被持久化,知道下一次更改所覆盖。

ZooKeeper的应用场景?/ 你知道哪些地方使用了ZooKeeper嘛?/ ZooKeeper都有哪些功能?

ZK经常被用来实现诸如:数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理Master选举分布式锁和分布式队列统一配置管理等功能。(加粗的必须记下,其它的尽量多记)

  1. 命名服务:可以通过ZK的顺序节点生成全局唯一ID。
  2. 数据发布/订阅:通过Watcher机制方便实现数据发布/订阅。当把数据发布到ZooKeeper被监听的节点上,其它机器可以通过监听ZK上节点的变化来实现配置的动态更新。
  3. 分布式锁:创建唯一节点获取分布式锁,获得锁的一方执行完相关代码或者挂掉后就释放锁。分布式锁的实现也需要用到Watcher机制
  4. Master选举(主节点选举):主节点挂掉之后可以从备用的节点开始新一轮的选举选出主节点。ZK可以协助完成这个过程。
  5. 统一配置管理:在一个集群中,所有的节点配置信息是一致的,满足CP的ZK当然可以运用到分布式系统配置文件管理和同步中。

顶级的开源项目也用到了ZK,如Kafka、Hadoop、Hbase等。

ZooKeeper负载均衡和ngnix负载均衡的区别? / 你还用过什么实现过负载均衡?

Ngnix是著名的反向代理服务器,ZK是分布式协调服务框架,都可以用来做负载均衡。

ZK的负载均衡可以调控,ngnix只能调权重,其它需要可控的都需要自己写控件。

ngnix的吞吐量比zk大很多!

ZooKeeper重要概念

数据模型

ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都可以存储数据,这些数据可以是数字、字符串或者是二级制序列。每个数据节点在 ZooKeeper 中被称为 znode,它是 ZooKeeper 中数据的最小单元。并且,每个 znode 都一个唯一的路径标识。

注:ZooKeeper 主要是用来协调服务的,而不是用来存储业务数据的,所以不要放比较大的数据在 znode 上,ZooKeeper 给出的上限是每个结点的数据大小最大是 1M。

Znode

Znode分为4大类:

  • 持久节点: 一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
  • 临时节点: 临时节点的生命周期是与 客户端会话绑定的,会话消失则节点消失 。并且,临时节点只能做叶子节点 ,不能创建子节点
  • 持久顺序节点: 除了具有持久节点的特性之外, 子节点的名称还具有顺序性。
  • 临时顺序节点: 除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。

每个 znode 由 2 部分组成:

  • stat :状态信息。
  • data : 节点存放的数据的具体内容。

版本

对应于每个 znode,ZooKeeper 都会为其维护一个叫作 Stat 的数据结构,Stat 中记录了这个 znode 的三个相关的版本:

  • dataVersion :当前 znode 节点的版本号。
  • cversion : 当前 znode 子节点的版本。
  • aclVersion : 当前 znode 的 ACL 的版本。

ACL

ACL(Access Control List)访问控制列表,是Linux系统中的访问权限控制系统。可用于解决Linux基本文件权限系统中权限分配空白问题。类似与Windows的文件权限设置,可以独立设置除了所有者和所属组以外的某个特定组或特定用户的访问权限。

ZooKeeper 采用 ACL(AccessControlLists)策略来进行权限控制,类似于 UNIX 文件系统的权限控制。5种权限:创建、读、写、删除、设置ACL权限的权限。

注:其中删除和创建权限是针对子节点的权限控制。

会话Session

Session 可以看作是 ZooKeeper 服务器与客户端的之间的一个 TCP 长连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向 ZooKeeper 服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的 Watcher 事件通知。

Session 有一个属性叫做:sessionTimeout ,sessionTimeout 代表会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。

另外,在为客户端创建会话之前,服务端首先会为每个客户端都分配一个 sessionID。由于 sessionID是 ZooKeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个 sessionID 的,因此,无论是哪台服务器为客户端分配的 sessionID,都务必保证全局唯一。

Watcher(非常重要的特性)

Watcher(事件监听器),是 ZooKeeper 中的一个很重要的特性。ZooKeeper 允许用户在指定节点上注册一些 Watcher,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。该机制是 ZooKeeper 实现分布式协调服务的重要特性。
在这里插入图片描述

工作机制

  1. 客户端注册Watcher
  2. 服务吨处理Watcher
  3. 客户端回调Watcher

Watcher特性

  • 一次性:无论是服务端还是客户端,一旦一个 Watcher 被 触 发 ,Zookeeper 都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。

  • 客户端串行执行:客户端 Watcher 回调的过程是一个串行同步的过程。

  • 轻量

    • Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。
    • 客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。
  • watcher event 异步发送 watcher 的通知事件从 server 发送到 client 是异步的,这就存在一个问题,不同的客户端和服务器之间通过 socket 进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用 Zookeeper 不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性,而无法保证强一致性。

ZooKeeper 集群 / 数据一致性

为了保证高可用,最好是以集群形态来部署 ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障,这也就不满足A了),那么 ZooKeeper 本身仍然是可用的。

集群间通过 ZAB 协议(ZooKeeper Atomic Broadcast)来保持数据的一致性。

当 Zookeeper 客户端连接到 Zookeeper 集群的一个节点后,若客户端提交的是读请求, 那么当前节点就直接根据自己保存的数据对其进行响应;如果是写请求且当前节点不是 Leader,那么节点就会将该写请求转发给 Leader,Leader 会以提案的方式广播该写操作,只 要有超过半数节点同意该写操作,则该写操作请求就会被提交。然后 Leader 会再次广播给 所有订阅者,即 Learner,通知它们同步数据。

提一嘴最典型集群模式: Master/Slave 模式(主备模式)。在这种模式中,通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。

ZooKeeper 集群角色

在 ZooKeeper 中没有选择传统的 Master/Slave 概念,而是引入了 Leader、Follower 和 Observer 三种角色。如下图所示:
在这里插入图片描述

ZooKeeper 集群中的所有机器通过一个 Leader 选举过程 来选定一台称为 “Leader” 的机器,Leader 既可以为客户端提供写服务又能提供读服务,同时也负责投票的发起和决议,更新系统状态。除了 Leader 外,Follower 和 Observer 都只能提供读服务。Follower 和 Observer 唯一的区别在于 Observer 机器不参与 Leader 的选举过程,也不参与写操作的“过半写成功”策略,因此 Observer 机器可以在不影响写性能的情况下提升集群的读性能。

Leader 选举过程

当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,就会进入 Leader 选举过程,这个过程会选举产生新的 Leader 服务器。

这个过程大致是这样的:

  1. Leader election(选举阶段):节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。
  2. Discovery(发现阶段) :在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
  3. Synchronization(同步阶段) :同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后准 leader 才会成为真正的 leader(也就是准备工作做好之后,完成同步正式“登基”)。
  4. Broadcast(广播阶段) :到了这个阶段,ZooKeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。

ZooKeeper 集群中的服务器状态有下面几种:

  • LOOKING:寻找Leader。
  • LEADING:Leader状态对应节点为Leader。
  • Following:Follower状态。
  • Observer状态:对应节点为Oberver。

分布式集群中为什么会有Master主节点?

在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行 leader 选举。

集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?

集群规则为 2N+1 台,N>0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。

ZooKeeper 选举的过半机制防止脑裂,如何理解?

集群脑裂: 对于一个集群,通常多台机器会部署在不同机房,来提高这个集群的可用性。保证可用性的同时,会发生一种机房间网络线路故障,导致机房间网络不通,而集群被割裂成几个小集群。这时候子集群各自选主导致“脑裂”的情况。

ZooKeeper 的过半机制导致不可能产生 2 个 leader,因为少于等于一半是不可能产生 leader 的,这就使得不论机房的机器如何分配都不可能发生脑裂。

举例说明:比如现在有一个由 6 台服务器所组成的一个集群,部署在了 2 个机房,每个机房 3 台。正常情况下只有 1 个 leader,但是当两个机房中间网络断开的时候,每个机房的 3 台服务器都会认为另一个机房的 3 台服务器下线,而选出自己的 leader 并对外提供服务。若没有过半机制,当网络恢复的时候会发现有 2 个 leader。仿佛是 1 个大脑(leader)分散成了 2 个大脑,这就发生了脑裂现象。脑裂期间 2 个大脑都可能对外提供了服务,这将会带来数据一致性等问题。

ZAB 协议

Paxos 算法应该可以说是 ZooKeeper 的灵魂了。但是,ZooKeeper 并没有完全采用 Paxos 算法 ,而是使用 ZAB 协议作为其保证数据一致性的核心算法。另外,在 ZooKeeper 的官方文档中也指出,ZAB 协议并不像 Paxos 算法那样,是一种通用的分布式一致性算法,它是一种特别为 Zookeeper 设计的崩溃可恢复的原子消息广播算法。

什么是ZAB 协议?

ZAB(ZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。 在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。

什么是ZAB 协议两种基本的模式?

  • 崩溃恢复 :当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB 协议就会进入恢复模式并选举产生新的 Leader 服务器。当选举产生了新的 Leader 服务器,同时集群中已经有过半的机器与该 Leader 服务器完成了状态同步之后,ZAB 协议就会退出恢复模式。其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和 Leader 服务器的数据状态保持一致。
  • 消息广播 :当集群中已经有过半的 Follower 服务器完成了和 Leader 服务器的状态同步,那么整个服务框架就可以进入消息广播模式了。 当一台同样遵守 ZAB 协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个 Leader 服务器在负责进行消息广播,那么新加入的服务器就会自觉地进入数据恢复模式:找到 Leader 所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。

Paxos算法

投票选举那一套(细节待补充

ZAB 和 Paxos 算法的联系与区别?

相同点:

  1. 两者都存在一个类似于 Leader 进程的角色,由其负责协调多个 Follower 进程的运行

  2. Leader 进程都会等待超过半数的 Follower 做出正确的反馈后,才会将一个提案进行提交

  3. ZAB 协议中,每个 Proposal 中都包含一个 epoch 值来代表当前的 Leader周期,Paxos 中名字为 Ballot

不同点:

ZAB 用来构建高可用的分布式数据主备系统(Zookeeper),Paxos 是用来构建分布式一致性状态机系统

ZooKeeper 怎么保证主从节点的状态同步?或者说同步流程是什么样的?

Zookeeper 的核心是原子广播机制,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式和广播模式。(详细见上)

ZooKeeper 是如何保证事务的顺序一致性的?

zookeeper 采用了全局递增的事务 Id 来标识,所有的 proposal(提议)都在被提出的时候加上了 zxid,zxid 实际上是一个 64 位的数字,高 32 位是 epoch( 时期; 纪元; 世; 新时代)用来标识 leader 周期,如果有新的 leader 产生出来,epoch会自增,低 32 位用来递增计数。当新产生 proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 server 发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。

ZooKeeper提供了什么?

ZooKeeper 底层其实只提供了两个功能:① 管理(存储、读取)用户程序提交的数据;② 为用户程序提供数据节点监听服务。

1.文件系统 2.通知机制

ZooKeeper如何实现分布式锁?

首先,zk在高并发的情况下保证节点创建的全局唯一性!!!

因为在集群环境下,如果follower节点接收到了添加/修改/删除等操作指令,会将这些指令发给leader节点来统一执行,在通过上述代码,做到节点唯一。

因为创建节点的唯一性,我们可以让多个客户端同时创建一个临时节点,创建成功的就说明获取到了锁 。然后没有获取到锁的客户端也像上面选主的非主节点创建一个 watcher 进行节点状态的监听,如果这个互斥锁被释放了(可能获取锁的客户端宕机了,或者那个客户端主动释放了锁)可以调用回调函数重新获得锁。

zk 中不需要向 redis 那样考虑锁得不到释放的问题了,因为当客户端挂了,节点也挂了,锁也释放了。

ZooKeeper如何用作注册中心? (本项目用到)

zookeeper 天然支持的 watcher 和 临时节点能很好的实现这些需求。我们可以为每条机器创建临时节点,并监控其父节点,如果子节点列表有变动(我们可能创建删除了临时节点),那么我们可以使用在其父节点绑定的 watcher 进行状态监控和回调。
在这里插入图片描述
为了实现注册中心,让 服务提供者 在 zookeeper 中创建一个临时节点并且将自己的 ip、port、调用方式 写入节点,当 服务消费者 需要进行调用的时候会 通过注册中心找到相应的服务的地址列表(IP端口什么的) ,并缓存到本地(方便以后调用),当消费者调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从地址列表中取一个服务提供者的服务器调用服务。

当服务提供者的某台服务器宕机或下线时,相应的地址会从服务提供者地址列表中移除。同时,注册中心会将新的服务地址列表发送给服务消费者的机器并缓存在消费者本机(当然你可以让消费者进行节点监听)。
在这里插入图片描述

RPC框架原理? / 你实现的RPC框架的原理?

客户端和服务端都可以访问到通用的接口,但是只有服务端有这个接口的实现类,客户端调用这个接口的方式,是通过网络传输,告诉服务端我要调用这个接口,服务端收到之后再找到这个接口的实现类,并且执行,将执行结果返回给客户端,作为客户端调用接口方法的返回值。
在这里插入图片描述

说一下项目模块组成

首先项目代码中,大的模块分为五种,

  1. api模块:里面主要实现了一些通用接口
  2. common模块:里面主要实现了实体对象、工具类等共用类。具体来说,有实体对象比如客户端向服务端传输的对象和服务端返回的对象。还有一些工具类比如枚举类、创建线程池工具类等等。
  3. core核心功能模块:这里面就是框架的核心实现,比如实现负载均衡、实现序列化、实现网络传输等功能的代码。
  4. client模块:作为测试用的消费侧
  5. server模块:作为测试用的提供侧

你实现了这么多序列,请问这些序列有什么区别?

  1. Kryo序列化后的数据相比Hessian小很多,Hessian使用固定长度存储int和long,而kryo使用变长的int和long保证这种基本数据类型序列化后尽量小
  2. Kryo使用不需要实现Serializable接口,Hessian则需实现
  3. Kryo进行序列化的时候,需要传入完整类名或者利用 register() 提前将类注册到Kryo上,其类与一个int型的ID相关联,序列中只存放这个ID,因此序列体积就更小,而Hessian则是将所有类字段信息都放入序列化字节数组中,直接利用字节数组进行反序列化,不需要其他参与,因为存的东西多处理速度就会慢点
  4. Kryo使用不需要实现Serializable接口,Hessian则需实现
  5. Kryo数据类的字段增、减,序列化和反序列化时无法兼容,而Hessian则兼容,Protostuff是只能在末尾添加新字段才兼容
  6. Kryo不是线程安全的,要通过ThreadLocal或者创建Kryo线程池来保证线程安全,而Protostuff则是线程安全的
  7. Protostuff和Kryo序列化的格式有相似之处,都是利用一个标记来记录字段类型,因此序列化出来体积都比较小

谈谈你对SPI的理解?

JAVA SPI 提供一个机制:为某个接口寻找服务实现的机制。类似IoC的思想,将装配的控制权移交到了程序之外。

SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。

我的理解是:专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。

SPI和API的区别?

当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 API ,这种接口和实现都是放在实现方的。当接口存在于调用方这边时,就是 SPI ,由接口调用方确定接口规则,然后由不同的厂商去根据这个规则对这个接口进行实现,从而提供服务。

Java的SPI实现机制?

Java 中的 SPI 机制就是在每次类加载的时候会先去找到 class 相对目录下的 META-INF 文件夹下的 services 文件夹下的文件,将这个文件夹下面的所有文件先加载到内存中,然后根据这些文件的文件名和里面的文件内容找到相应接口的具体实现类,找到实现类后就可以通过反射去生成对应的对象,保存在一个 list 列表里面,所以可以通过迭代或者遍历的方式拿到对应的实例对象,生成不同的实现。

你的SPI实现机制有什么改进?

  • 配置文件改为键值对形式,可以获取任一实现类,而无需加载所有实现类,节约资源;
  • 增加了缓存来存储实例,提高了读取的性能;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值