微服务面试题

1、Spring Cloud Netflix和Spring Cloud Alibaba包括哪些组件

Spring Cloud Netflix,主要由:EurekaRibbonFeignHystrixZuul|GatewayConfig等组件组成。

Spring Cloud Alibaba,主要由:NacosSentinelSeata等组件组成。

Apollo 携程、SkyWalking 吴晟(shèng)国产开源的

2、Nacos是CP还是AP?(Nacos配置中心和注册中心)

Nacos即能保证CP,也能保证AP,具体看如何配置,但其实只不过是Nacos中的注册中心能保证CP或AP,Nacos中的配置中心其实没什么CP或AP,因为配置中心的数据是存在一个Mysql中的,只有注册中心的数据需要进行集群节点之间的同步,从而涉及到是CP还是AP,默认是AP模式。

3、Nacos作为注册中心应该选择是CP还是AP?

注册中心应该是AP,如果注册中心是CP的,那么表示,当我们向注册中心注册实例或移除实例时,都要等待注册中心集群中的数据达到一致后,才算注册或移除成功,而这是比较耗时的,随着业务应用规模的增大,应用频繁的上下线,那么就会导致注册中心的压力比较大,会影响到服务发现的效率以及服务调用了,而如果注册中心是AP的,那么注册中心集群不管出现了什么情况,都是可以提供服务的,就算集群节点之间数据出现了不一致,对于业务应用而言,可能拉取到了一个已经下线了的服务节点,但是现在一般的微服务框架或组件都提供了服务容错和重试功能,也可以避免这个问题,而如果是AP,对于注册中心而言就不需要消耗太多的资源来实时的保证数据一致性了,保证最终一致性就可以了,这样注册中心的压力会小一点。

4、Nacos如何实现就进访问?

image.png

首先,在Nacos中,一个服务可以有多个实例,并且可以给实例设置cluster-name,就是可以再进一步的给所有实例划分集群,那如果现在某个服务A想要调用服务B,那么Naocs会看调用服务A的实例是属于哪个集群的,并且调用服务B时,那就会调用同样集群下的服务B实例,根据cluster-name来判断两个实例是不是同一个集群,这就是Nacos的就近访问。

5、Nacos底层负载均衡底层原理

通过Ribbon实现的,Ribbon中定义了负载均衡算法,然后基于这些算法从服务实例中获取一个实例为想费方的提供服务

6、Nacos1.x注册中心架构流程

image.png

1、服务启动的时候,通过api发起服务注册,来告诉nacos,我可以提供服务了

2、服务消费者在启动的时候会拉取 自已要用的服务的列表。

(这只是启动的时候拉取,那平常有上下线服务怎么办呢?定时拉取)

3、消费者会每10秒进行拉取一下数据。拉取我可以调用的服务。

(如果我们只用定时任务拉取配置,那注册服务会有一定的滞后性,那怎么让它具有实时性呢?下面udp协议)

4、Nacos服务检测到有异常(服务上下线)就会发送UDP协议给客户端进行更新

(为什么是udp写协议?

因为udp协议相对于tcp协议来说,他并不是可靠的协议,但他的优点就是他非常快,他耗时比较短,不需要跟自己目标服务器建立长连接,在服务注册这种场景,我们服务消费者要比服务提供者要多很多,所以每一次 服务的更新,nacos需要和成千上万的消费者,去建立tcp的话,他的性能一定是受不了的,所以他会选择一个udp协议进行通知,那有的同学会问了,udp他不可靠,他通知失败了怎么办? 大家可以看一下第三部,我们消费者每10秒还会拉取一下服务数据信息

上面说是检测到异常,那Nacos怎样判断异常的呢? 心跳,

5、客户端会每5秒定时发送心跳到服务端,来维持他的一个健康的检查,

6、Nacos定时任务检测:通过每5秒检查一下心跳信息来判断是否超时,就是用当前时间减去上次一心跳的时间,如果超过15秒则将节点设置为非健康状态并进行广播,如果超过30秒则将节点进行移除,说明节点不可用。

7、集群数据同步任务使用协议:Distro(AP) , Raft(CP)

7、Nacos2.X作为注册中心架构流程

image.png

8、Nacos中的Distro协议

  • Nacos 每个节点自己负责部分的写请求。
  • 每个节点会把自己负责的新增数据同步给其他节点。
  • 每个节点定时发送自己负责数据的校验值到其他节点来保持数据一致性。
  • 每个节点独立处理读请求,及时从本地发出响应。
  • 新加入的 Distro 节点会进行全量数据拉取。

(具体操作是轮询所有的 Distro节点,通过向其他的机器发送请求拉取全量数据。)

Nacos1.4.x

image.png

Nacos2.x

1、读取方式不同

Nacos2.x无论是读还是写,都会走与当前客户端建立长连接的节点,不走服务端DistroFilter。读请求不同于1.x会打到不同节点上,一直会打到建立长连接的节点上; TODO

2、责任节点的判断

写请求不同于1.x中可能被二次转发,与客户端建立长连接的节点,就是 责任节点 。不同于1.x,基于Service设置责任节点,2.x基于Client设置责任节点。

3、集群同步

与1.x不同的是,2.x责任节点VERIFY任务不再仅发送服务实例列表的摘要md5给非责任节点。责任节点每5s向非责任节点发送VERIFY续租Client, 包含Client的所有数据,不仅仅是md5 ,这避免了非责任节点反查;非责任节点定时扫描isNative=false的Client数据,如果超过30s没有续租,移除这些非native的Client。

9、Eureka注册中心原理

image.png

1、服务启动进行注册

2、集群内部进行数据同步(这里没有设计到什么协议,就是发送http进行数据同步)

3、客户端定时任务每30s拉取一下数据

4、客户端心跳续约,每30s一次

5、每分钟检查亦喜爱实例是否过期,如果过期就将其移除,如果出现大量掉线就进入自我保护

6、优雅下线

10、Eureka自我保护机制原理

Eureka服务端会每15分钟检查所有Eureka 实例正常心跳占比,如果低于85%就会触发自我保护机制。触发了保护机制,Eureka将暂时把这些失效的服务保护起来,不让其过期,但这些服务也并不是永远不会过期。Eureka在启动完成后,每隔60秒会检查一次服务健康状态,如果这些被保护起来失效的服务过一段时间后(默认90秒)还是没有恢复,就会把这些服务剔除。如果在此期间服务恢复了并且实例心跳占比高于85%时,就会自动关闭自我保护机制。

image.png

11、Eureka和Nacos对比

1、Nacos中注册中心会定时向消费者主动推送信息,Eureka会定时向注册中心定时拉去服务,如果不主动拉去服务,注册中心不会主动推送。

2、Nacos支持CP和AP模式,Eureka支持AP模式

3、Nacos具备服务优雅上下线和流量管理(API+后台管理页面),而Eureka的后台页面仅供展示,需要使用api操作上下线且不具备流量管理功能。

4、Nacos社区活跃,Eureka开源工作已停止,后续不再有更新和维护

5、从部署来看,Nacos整合了注册中心、配置中心功能,把原来两套集群整合成一套,简化了部署维护

12、Nacos配置中心长轮询机制

Nacos 1.4.x

image.png客户端会轮询向服务端发出一个长连接请求,这个长连接最多30s就会超时,服务端收到客户端的请求会先判断当前是否有配置更新,有则立即返回如果没有服务端会将这个请求拿住“hold”29.5s加入队列,最后0.5s再检测配置文件无论有没有更新都进行正常返回,但等待的29.5s期间有配置更新可以提前结束并返回。

Nacos2.x

image.png

1、服务端配置发生变更后,通过长链接通知客户端服务发生变化,客户端再去拉取

2、定时任务每5秒拉取一次

13、引用Nacos配置的定时任务失效(Nacos工作中遇到问题)

1、Schedule执行原理,利用后置处理器ScheduledAnnotationBeanPostProcessor,获取@Schedule标注的方法,然后使用定时任务处理

2、然后使用@RefreshScope标注的对象,@RefreshScope中有个@Scope里面值是Refresh,他创建对象是放到对应的缓存中,我们通过GenericScope#get方法从缓存中获取对应Bean对象,在更新配置刷新容器时候会讲对应的实例对象给清除调用,这样老的数据就会清除调用,我们需要再调用一下这个对应才能创建对应的实例(BeanFactory#getBean 有对应的对象则获取,没有则进行创建),然后对应的定时任务就能正常的运行

3、解决方式:更新配置刷新容器后会发送事件RefreshScopeRefreshedEvent,我们只需监听一下这个事件,之后他就会创建对应的实例,然后定时任务就能正常执行

https://www.mashibing.com/live/2163?r=1370

14、Nacos加载那些配置,这些配置的优先级?

#作用:顺序
# a p p l i c a t i o n . n a m e − {application.name}- application.name{profile}.KaTeX parse error: Expected 'EOF', got '#' at position 44: …nfig-prod.yaml #̲{application.name}.KaTeX parse error: Expected 'EOF', got '#' at position 38: …os-config.yaml #̲{application.name} nacos-config
#extensionConfigs 扩展配置文件
#sharedConfigs 多个微服务公共配置 redis

15、Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗,除此之外我们服务再重启是否可以重启成功,以及它读取配置的过程?

1、可以从内存,客户端获取了配置中心的配置信息后,会将配置信息在本地保存一份。

2、可以

3、先读取本地文件,如果没有,则远程拉取,拉取成功则写入本地快照,拉取失败,则读取本地快照文件

image.png

16、配置中心的技术选型

功能点Spring Cloud ConfigApolloNacos
版本管理支持(Git)支持支持
配置实时推送支持(Spring Cloud Bus)支持(HTTP长轮询1s内)支持(HTTP长轮询1s内 或者 grpc)
配置回滚支持(Git)支持支持
灰度发布支持(调用机器接口)支持不支持
权限管理支持(依赖Git)支持支持
配置生效时间重启实时实时
审计支持(依赖Git)支持不支持
多集群支持支持支持
多环境支持支持支持
client本地缓存不支持支持支持
监听查询支持支持支持
运维成本中等较低
多语言仅Java主流语言,提供了Open API主流语言,提供了Open API
配置项维护基于git统一界面统一界面
社区活跃度1.8k Star27.4k Star24.2k Star

1、社区活跃度

2、自己的技术栈 我们以前 rocketmq 和kafka rocketmq :不支持指定时间的延时消息、不支持事务消息

Java kafka scala ,我们技术栈是java ,所以我们选择rocketmq

3、产品功能 rocketmq或者kafka技术栈是否hold住

17、Feign底层怎样实现不同服务的不同配置

image.png

为不同服务创建不同的Spring上下文,每个服务都对应一个spring上下文,并且以key-value形式存放到Map中key对应服务名称,value对应服务的上下文,我们获取应用的配置时候,就通过服务名称获取对应的上下文,然后从上下文中获取对应的配置

18、生产上为什么Feign第一次调用耗时很长?

ribbon默认是懒加载的,只有第一次调用的时候才会生成ribbon对应的组件,这就会导致首次调用的会很慢的问题。

ribbon:
  eager-load:
    enabled: true
      clients: msb-stock

19、Feign怎样实现认证的传递?

实现接口RequestInterceptor,通过header实现认证传递

package feign;

public interface RequestInterceptor {
  void apply(RequestTemplate template);
}


public class FeignAuthRequestInterceptor implements RequestInterceptor {
private String tokenId;

public FeignAuthRequestInterceptor(String tokenId) {
    this.tokenId = tokenId;
}

@Override
public void apply(RequestTemplate template) {
    template.header("Authorization",tokenId);
}

}

20、Feign底层默认使用什么发送HTTP,有什么问题,我们怎样改进?

Feign底层默认是 JDK自带的HttpURLConnection,它是单线程发送HTTP请求的,不能配置线程池,我们使用Okhttp或者HttpClient来发送http请求,并且它们两个都支持线程池。

常见HTTP客户端

  • HttpClient
    HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 Http 协 议的客户端编程工具包,并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 JDK 自带的 URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得容易,提高了开发的效率。
  • Okhttp
    一个处理网络请求的开源项目,是安卓端最火的轻量级框架,由 Square 公司贡献,用于替代 HttpUrlConnection 和 Apache HttpClient。OkHttp 拥有简洁的 API、高效的性能,并支持多种协议 (HTTP/2 和 SPDY)。
  • HttpURLConnection
    HttpURLConnection 是 Java 的标准类,它继承自 URLConnection,可用于向指定网站发送 GET 请求、 POST 请求。HttpURLConnection 使用比较复杂,不像 HttpClient 那样容易使用。
  • RestTemplate
    RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。

21、谈谈Sentienl中使用的限流算法,Sentinel哪里对这算法进行应用

1、计数器固定窗口算法

计数器固定窗口算法是最简单的限流算法,实现方式也比较简单。就是通过维护一个单位时间内的计数值,每当一个请求通过时,就将计数值加1,当计数值超过预先设定的阈值时,就拒绝单位时间内的其他请求。如果单位时间已经结束,则将计数器清零,开启下一轮的计数。

image.png

但是这种实现会有一个问题,举个例子:

假设我们设定1s内允许通过的请求阈值是100,如果有用户在时间窗口的最后几毫秒发送了99个请求,紧接着又在下一个时间窗口开始时发送了99个请求,那么这个用户其实在一秒显然超过了阈值但并不会被限流。其实这就是临界值问题,那么临界值问题要怎么解决呢?

image.png

2、计数器滑动窗口算法

计数器滑动窗口法就是为了解决上述固定窗口计数存在的问题而诞生。前面说了固定窗口存在临界值问题,要解决这种临界值问题,显然只用一个窗口是解决不了问题的。假设我们仍然设定1秒内允许通过的请求是200个,但是在这里我们需要把1秒的时间分成多格,假设分成5格(格数越多,流量过渡越平滑),每格窗口的时间大小是200毫秒,每过200毫秒,就将窗口向前移动一格。为了便于理解,可以看下图

image.png

图中将窗口划为5份,每个小窗口中的数字表示在这个窗口中请求数,所以通过观察上图,可知在当前时间块(200毫秒)允许通过的请求数应该是70而不是200(只要超过70就会被限流),因为我们最终统计请求数时是需要把当前窗口的值进行累加,进而得到当前请求数来判断是不是需要进行限流。

那么滑动窗口限流法是完美的吗?

image.png

细心观察的我们应该能马上发现问题,滑动窗口限流法其实就是计数器固定窗口算法的一个变种。流量的过渡是否平滑依赖于我们设置的窗口格数也就是统计时间间隔,格数越多,统计越精确,但是具体要分多少格我们也说不上来呀…

3、漏桶算法

上面所介绍的两种算法都不能非常平滑的过渡,下面就是漏桶算法登场了

什么是漏桶算法?

漏桶算法以一个常量限制了出口流量速率,因此漏桶算法可以平滑突发的流量。其中漏桶作为流量容器我们可以看做一个FIFO的队列,当入口流量速率大于出口流量速率时,因为流量容器是有限的,当超出流量容器大小时,超出的流量会被丢弃。

下图比较形象的说明了漏桶算法的原理,其中水龙头是入口流量,漏桶是流量容器,匀速流出的水是出口流量。

image.png

漏桶算法的特点

  • 漏桶具有固定容量,出口流量速率是固定常量(流出请求)
  • 入口流量可以以任意速率流入到漏桶中(流入请求)
  • 如果入口流量超出了桶的容量,则流入流量会溢出(新请求被拒绝)

不过因为漏桶算法限制了流出速率是一个固定常量值,所以漏桶算法不支持出现突发流出流量。但是在实际情况下,流量往往是突发的。

4、令牌桶算法

令牌桶算法是漏桶算法的改进版,可以支持突发流量。不过与漏桶算法不同的是,令牌桶算法的漏桶中存放的是令牌而不是流量。

那么令牌桶算法是怎么突发流量的呢?

最开始,令牌桶是空的,我们以恒定速率往令牌桶里加入令牌,令牌桶被装满时,多余的令牌会被丢弃。当请求到来时,会先尝试从令牌桶获取令牌(相当于从令牌桶移除一个令牌),获取成功则请求被放行,获取失败则阻塞活拒绝请求。

image.png

令牌桶算法的特点

  • 最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃
  • 每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。

令牌桶算法限制的是平均流量,因此其允许突发流量(只要令牌桶中有令牌,就不会被限流)

流控效果:

快速失败(默认):滑动时间窗口、WarmUp:令牌桶算法、 排队等待:漏斗算法

22、谈谈Sentienl服务熔断过程

服务熔断一般有三种状态:

  • 熔断关闭状态(Closed)

    服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。

  • 熔断开启状态(Open)

    后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法。

  • 半熔断状态(Half-Open)

    尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状态。

    image.png

23、请说一下CAP和BASE理论

CAP和BASE是分布式必备理论基础

1、CAP理论

这个定理的内容是指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

  • 一致性(C)

    一致性意思就是写操作之后进行读操作无论在哪个节点都需要返回写操作的值

  • 可用性(A)

    非故障的节点在合理的时间内返回合理的响应

  • 分区容错性(P)

    当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。

在分布式系统中,网络无法100%可靠,分区其实是一个必然现象,如果我们选择了CA而放弃了P,那么当发生分区现象时,为了保证一致性,这个时候必须拒绝请求,但是A又不允许,所以分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。

对于CP来说,放弃可用性,追求一致性和分区容错性,我们的zookeeper其实就是追求的强一致。

对于AP来说,放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,Nacos就是AP模式,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。

2、BASE理论

BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

  • 基本可用

    基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性—-注意,这绝不等价于系统不可用。比如:

    (1)响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒

    (2)系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面

  • 软状态

    软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时

  • 最终一致性

    最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

24、请简述2PC流程以及优缺点

1、2PC

1.1 流程

image.png

1.2 优缺点

优点: 尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL是从5.5开始支持(XA)。

缺点:

  • 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
  • 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
  • 数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。

2、3PC

三段提交(3PC)是对两段提交(2PC)的一种升级优化,3PC2PC的第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前,各参与者节点的状态都一致。同时在协调者和参与者中都引入超时机制,当 参与者各种原因未收到 协调者的commit请求后,会对本地事务进行abort,不会一直阻塞等待,解决了 2PC的单点故障问题,但 3PC 还是没能从根本上解决数据一致性的问题。

2.1 流程

image.png

CanCommit:协调者向所有参与者发送CanCommit命令,询问是否可以执行事务提交操作。如果全部响应YES则进入下一个阶段。

PreCommit协调者向所有 参与者发送 PreCommit命令,询问是否可以进行事务的预提交操作,参与者接收到PreCommit请求后,如参与者成功的执行了事务操作,则返回 Yes响应,进入最终commit阶段。一旦参与者中有向协调者发送了 No响应,或因网络造成超时,协调者没有接到参与者的响应,协调者向所有参与者发送 abort请求,参与者接受abort命令执行事务的中断。

DoCommit: 在前两个阶段中所有参与者的响应反馈均是 YES后,协调者向参与者发送 DoCommit命令正式提交事务,如协调者没有接收到参与者发送的ACK响应,会向所有参与者发送 abort请求命令,执行事务的中断

2.2 优缺点

  • 优点:

1、引入超时机制。解决了事务管理器突然宕机导致资源一直处于阻塞的问题

2、多了一次询问阶段。防止个别参与者不正常的情况下,其他参与者都执行了事务,锁定资源。

  • 缺点:

1、 3PC 用超时机制,同步阻塞问题,但与此同时却多了一次网络通信,性能上反而变得更差,也不太推荐。

2、没有解决数据不一致的问题

25、Seata支持那些事务模式?

概念:Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

这四种模式2PC(两阶段提交)

  • AT模式:提供无侵入自动补偿的事务模式 【这里是基于本地能支持事务的关系型数据库,然后java代码可以通过JDBC访问数据库, 这里的无侵入:我们只需要加上对应的注解就可以开启全局事务】
  • XA模式:支持已实现XA接口的数据库的XA模式【这里一般是需要数据库实现对应的XA模式的接口,一般像 mysql oracle 都实现了XA】
  • TCC模式:TCC则可以理解为在应用层面的 2PC,是需要我们编写业务逻辑来实现。
  • SAGA模式:为长事务提供有效的解决方案

26、简述Seata的AT模式两阶段过程

两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:
    • 提交异步化,非常快速地完成。
    • 回滚通过一阶段的回滚日志进行反向补偿。

第一阶段

业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。核心在于对业务sql进行解析,转换成undolog,并同时入库,这是怎么做的呢?先抛出一个概念DataSourceProxy代理数据源,通过名字大家大概也能基本猜到是什么个操作,后面做具体分析

![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/15647/1662350890004/512b8b8ffd54401a8123ac6af

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JavaHxg

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

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

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

打赏作者

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

抵扣说明:

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

余额充值