Dubbo 面试题及答案

10.Dubbo支持的通信协议?Dubbo调用RPC有几种协议可以用?

==============================================================================================

  • Dubbo 协议

  • 默认就是 Dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。以及消费者远大于提供者。

  • 为了要支持高并发场景,一般是服务提供者就几台机器,但是服务消费者有上百台,可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就 100 个连接。然后后面直接基于长连接 NIO 异步通信,可以支撑高并发请求。

  • rmi 协议

  • 采用JDK标准的rmi协议实现,传输参数和返回参数对象需要实现Serializable接口,使用java标准序列化机制,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议TCP。

  • Java 二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。

  • hessian 协议

  • hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。

  • 集成Hessian服务,基于HTTP通讯,采用Servlet暴露服务,Dubbo内嵌Jetty作为服务器时默认实现,提供与Hession服务互操作。

  • http 协议

  • json 序列化。

  • 基于Http表单提交的远程调用协议,使用Spring的HttpInvoke实现。多个短连接,传输协议HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器JS调用;

  • webservice

  • SOAP 文本序列化。

  • 基于WebService的远程调用协议,集成CXF实现,提供和原生WebService的互操作。多个短连接,基于HTTP传输,同步传输,适用系统集成和跨语言调用;

  • memcache

  • 基于memcached实现的RPC协议

  • redis

  • 基于redis实现的RPC协议

  • thrift 协议

  • 当前 Dubbo 支持的 thrift 协议是对 thrift 原生协议 [2] 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。

  • RESTful

  • 基于标准的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的简写)实现的REST调用支持。

11.Dubbo支持服务多协议吗?

============================================================================

Dubbo允许配置多协议,在不同的服务上支持不同协议或者同一服务器上同时支持多种协议。

12.Dubbo服务器、Zookeeper服务器各有几个角色节点?

============================================================================================

Dubbo

  • Provider:暴露服务的服务提供方

  • Consumer:调用远程服务的服务消费方

  • Registry:服务注册与发现的注册中心

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

  • Container:服务运行容器

zookeeper

  • Leader(领导者):Leader 服务器是整个 ZooKeeper 集群工作机制中的核心

  • 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。

  • 集群内部各服务器的调度者。

  • Follwer(跟随者):

  • 处理客户端非事务请求,转发事务请求给 Leader 服务器。

  • 参与事务请求 Proposal 的投票。

  • 参与 Leader 选举投票。

  • ObServer(观察者):

  • 接收客户端连接,将写请求转发给leader节点,但ObServer不参与投票,只填报leader的状态。

  • ObServer目的是为了扩展系统,提高读取速度。

13.Dubbo框架图?Dubbo架构图?画一画服务注册与发现的流程图?

===============================================================================================

在这里插入图片描述

具体的流程过程参考文章:Dubbo的架构设计

15.Dubbo默认使用什么注册中心,还有别的选择吗?

======================================================================================

推荐使用 Nacos 作为注册中心,还有Zookeeper, Redis、Multicast、Simple 注册中心,但不推荐。Zookeeper是强一致性,会出现服务不可用的情况。

16.在 Provider 上可以配置的 Consumer 端的属性有哪些?

=================================================================================================

  • timeout:方法调用超时

  • retries:失败重试次数,默认重试2次

  • loadbalance:负载均衡算法,默认随机

  • actives:消费者端,最大并发调用限制

17.Dubbo启动时如果依赖的服务不可用会怎样?

====================================================================================

Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,默认check=“true”,可以通过 check=“false” 关闭检查。

18.注册了多个同一样的服务,如果测试指定的某一个服务呢?

========================================================================================

可以配置环境点对点直连,绕过注册中心,将以服务接口为单位,忽略注册中心的提供者列表。

19.服务提供者能实现失效踢出是什么原理?

================================================================================

基于Zookeeper的临时节点原理

20.说说 Dubbo 服务暴露的过程

==============================================================================

  • Dubbo会在Spring实例化完bean之后,在刷新容器最后一步发布ContextRefreshEvent事件的时候,那么 Dubbo 通过DubboBootstrapApplicationListener 监听类来接收 ContextRefreshedEvent 事件。(这是 Dubbo 与 Spring 整合的切入点)

  • 当监听器接收到 ContextRefreshedEvent 事件后,Dubbo 会做以下几点事情:暴露服务、导出元数据、注册本地服务实例、初始化远程调用代理对象。导出元数据(在 Dubbo2.7.x 中元数据和配置信息是分离的这里仅仅导出的是元数据)、注册本地实例(在 JVM 导出服务)、初始化远程代理对象(其实就是创建一个调用远程的 Invoker 代理对象)。

  • 通知实现了ApplicationListener 的 ServiceBean 类进行回调 onApplicationEvent 事件方法,Dubbo会在这个方法中调用ServiceBean父类ServiceConfig的export方法,而该方法真正实现了服务的(异步或者非异步)发布。

详细过程见下图:

在这里插入图片描述

  1. ServiceConfig类拿到对外提供服务的实现类ref(如:HelloWorldImpl)

  2. 通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化。

  3. 接下来就是Invoker转换到Exporter的过程。Dubbo处理服务暴露的关键就在Invoker转换到Exporter的过程(如上图中的红色部分)。 Dubbo协议的Invoker转为Exporter发生在DubboProtocol类的export方法,它主要是打开socket侦听服务,并接收客户端发来的各种请求,通讯细节由Dubbo自己实现。

@Override

public Exporter export(Invoker invoker) throws RpcException {

checkDestroyed();

URL url = invoker.getUrl();

// export service.

String key = serviceKey(url);

DubboExporter exporter = new DubboExporter(invoker, key, exporterMap);

//export a stub service for dispatching event

Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);

Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);

if (isStubSupportEvent && !isCallbackservice) {

String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);

if (stubServiceMethods == null || stubServiceMethods.length() == 0) {

if (logger.isWarnEnabled()) {

logger.warn(new IllegalStateException(“consumer [” + url.getParameter(INTERFACE_KEY) +

“], has set stubproxy support event ,but no stub methods founded.”));

}

}

}

openServer(url);

optimizeSerialization(url);

return exporter;

}

21.Dubbo服务注册与校验的过程

=============================================================================

在上面的我们了解了Dubbo服务暴露的过程,服务暴露完了以后,当我们需要暴露一个服务给其他应用端调用时那么应用端是怎样知道我们的服务在哪里?暴露了什么接口?通讯端口是多少?这些信息客户端是怎样获取的呢?这些都需要我们的服务提供者向我们的注册中心注册,消费端获取这些注册元信息再进行远程调用。那么服务提供者向注册中心注册元信息的过程就是服务注册。整个服务注册过程大致包括了:参数检测,URL 组装、本地服务导出,远程服务导出、注册中心服务注册。

22.服务消费者消费一个服务的详细过程

==============================================================================

在这里插入图片描述

  1. ReferenceConfig类的init方法调用Protocol的refer方法生成Invoker实例(如上图中的红色部分),这是服务消费的关键。

  2. 把Invoker转换为客户端需要的接口(如:HelloWorld)。这里获取invoker的时候,如果是注册中心同一个服务有多个提供者,消费者会根据配置的负载均衡来选择出一个作为提供者来使用。并且也有各种容错处理机制。

以DubboProtocol为例,使用refer方法生成Invoker实例的示例代码如下:

@Override

public Invoker refer(Class type, URL url) throws RpcException {

checkDestroyed();

return protocolBindingRefer(type, url);

}

@Override

public Invoker protocolBindingRefer(Class serviceType, URL url) throws RpcException {

checkDestroyed();

optimizeSerialization(url);

// create rpc invoker.

DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);

invokers.add(invoker);

return invoker;

}

23.如何解决服务调用链过长的问题?

=============================================================================

Dubbo可以使用zipkin或者Pinpoint和Apache Skywalking(Incubator)实现分布式服务追踪

24.Dubbo服务之间的调用是阻塞的吗?

================================================================================

默认是同步等待结果阻塞的,支持异步调用。

Dubbo是基于NIO的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个Future对象。

异步调用流程图如下:

在这里插入图片描述

25.Dubbo的超时设置

========================================================================

  • 针对方法设置超时时间

  • 在服务方设置超时时间

  • 在调用方设置超时时间

详细的参考文章:Dubbo的超时和重试

26.Dubbo的重试机制

========================================================================

  • Dubbo在调用服务不成功时,默认会重试2次。加上初始一次,总共调用提供者3次

  • Dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本次尝试,所以Dubbo的重试机制也能一定程度的保证服务的治理。

  • 如果不合理的配置重试次数,当失败时会进行重试多次,这样在某个时间点出现性能问题,调用方再连续重复调用,系统请求变为正常值的retries倍,系统压力会大增,容易引起服务雪崩,需要根据业务情况规划好如何进行异常处理,何时进行重试。

<dubbo:reference id=“xx” interface=“xx” retries=“2” timeout=“3000”/>

  1. timeout=“3000” ,服务调用的超时时间,调用服务的过程中如果达到3秒就会报超时异常,超时异常后客户端会进行尝试设定的“retries”次调用。有一个需要注意的地方,timeout只有在超时异常才有效,如果是其他异常导致dubbo服务调用抛异常,会立即进入下一次尝试。

  2. retries=“2” ,即重试两次,如果失败就抛出异常。

27.Dubbo有哪几种集群容错方案,默认是哪种?

====================================================================================

  • Failover Cluster:失败自动切换,自动重试其他服务器。默认容错方案

  • Failfast Cluster:快速失败,立即报错,只发起一次调用。

  • Failsafe Cluster:失败安全,出现异常时,直接忽略。

  • Failback Cluster:失败自动恢复,记录失败请求,定时重发。

  • Forking Cluster:并行调用多个服务器,只要一个成功即返回。

  • Broadcast Cluster:广播逐个调用所有提供者,任意一个报错则报错。

28.服务读写推荐的容错策略是怎样的?

==============================================================================

读操作建议使用Failover失败自动切换,默认重试两次其他服务器。

写操作建议使用Failfast快速失败,发一次调用失败就立即报错。

29.Dubbo有哪几种负载均衡策略,默认是哪种?Dubbo的负载均衡原理?

=================================================================================================

  • Random LoadBalance:随机,按权重设置随机概率。默认

  • RoundRobin LoadBalance:轮询,按公约后的权重设置轮询比率。

  • LeastActive LoadBalance:最少活跃调用数,相同活跃数的随机。

  • ConsistentHash LoadBalance:一致性Hash,相同参数的请求总是发到同一提供者。

具体的负载均衡原理,可以参考以下文章:Dubbo-负载均衡原理解析

30.Dubbo支持服务降级吗?

===========================================================================

  • Dubbo 2.2.0 以上版本支持。

  • Dubbo提供了mock配置,可以很好的实现Dubbo服务降级。mock只在出现非业务异常(比如超时,网络异常等)时执行。

  • mock的配置支持两种

  • Mock Class降级处理:boolean值,默认的为false。如果配置为true,则缺省使用mock类名,即类名+Mock后缀;

  • Mock Null降级处理:配置"return null",可以很简单的忽略掉异常。

31.Dubbo如何优雅停机?

==========================================================================

Dubbo是通过JDK的ShutdownHook来完成优雅停机的,所以如果使用kill -9 PID等强制关闭指令,是不会执行优雅停机的,只有通过kill PID时,才会执行。

32.Dubbo可以对结果进行缓存吗?

==============================================================================

可以,Dubbo提供了声明式缓存,用于加速热门数据的访问速度,以减少用户加缓存的工作量。

33.了解哪些rpc框架?你还了解别的分布式框架吗?

=====================================================================================

  • Thrift:

  • thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

  • Dubbo:

  • 国内最早开源的 RPC 框架,仅支持 Java 语言。

  • Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。 Dubbo是阿里巴巴内部的SOA服务化治理方案的核心框架,Dubbo自2011年开源后,已被许多非阿里系公司使用。

  • Spring Cloud:

  • Spring Cloud由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案。

  • Spring Cloud基于Spring Boot, 使得开发部署极其简单。

  • gRPC:

  • 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。

  • Motan:

  • 微博内部使用的 RPC 框架,于 2016 年对外开源,仅支持 Java 语言。

  • Tars:

  • 腾讯内部使用的 RPC 框架,于 2017 年对外开源,仅支持 C++ 语言。

34.Dubbo的原理?Dubbo是基于什么实现的?底层是怎么实现的?

==============================================================================================

Dubb的底层通信是利用netty来实现的。

具体实现的源码,参考资料:https://zhuanlan.zhihu.com/p/97762356

35.Dubbo的线程模型

========================================================================

在这里插入图片描述

Dubbo线程模型包括线程模型策略和Dubbo线程池策略两个方面。

线程模型策略


Dubbo默认的底层网络通信使用的是Netty,服务提供方NettyServer使用两级线程池,其中EventLoopGroup(boss)主要用来接收客户端的链接请求,并把完成TCP三次握手的连接分发给EventLoopGroup(worker)来处理,注意把boss和worker线程组称为I/O线程,前者处理IO连接事件,后者处理IO读写事件。

Dubbo中根据请求的消息类是直接被I/O线程处理还是被业务线程池处理,Dubbo提供了下面几种线程模型:

在这里插入图片描述

  • all(AllDispatcher类):所有消息都派发到业务线程池,这些消息包括请求、响应、连接事件、断开事件等,响应消息会优先使用对于请求所使用的线程池。

  • direct(DirectDispatcher类):所有消息都不派发到业务线程池,全部在IO线程上直接执行。

  • message(MessageOnlyDispatcher类):只有请求响应消息派发到业务线程池,其他消息如连接事件、断开事件、心跳事件等,直接在I/O线程上执行。

  • execution(ExecutionDispatcher类):只把请求类消息派发到业务线程池处理,但是响应、连接事件、断开事件、心跳事件等消息直接在I/O线程上执行。

  • connection(ConnectionOrderedDispatcher类):在I/O线程上将连接事件、断开事件放入队列,有序地逐个执行,其他消息派发到业务线程池处理。

Dubbo线程池可选模型较多,下面以DirectDispatcher类进行分析:

public class DirectChannelHandler extends WrappedChannelHandler {

public DirectChannelHandler(ChannelHandler handler, URL url) {

super(handler, url);

}

@Override

public void received(Channel channel, Object message) throws RemotingException {

ExecutorService executor = getPreferredExecutorService(message);

if (executor instanceof ThreadlessExecutor) {

try {

executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));

} catch (Throwable t) {

throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);

}

} else {

handler.received(channel, message);

}

}

}

DirectDispatcher类重写了received方法,注意 ThreadlessExecutor 被应用在调用 future.get() 之前,先调用 ThreadlessExecutor.wait(),wait 会使业务线程在一个阻塞队列上等待,直到队列中被加入元素。很明显,provider侧调用getPreferredExecutorService(message)返回的不是ThreadlessExecutor,所以会在当前IO线程执行执行。

其他事件,比如连接、异常、断开等,都是在WrappedChannelHandler中默认实现:执行在当前IO线程中执行的,代码如下:

@Override

public void connected(Channel channel) throws RemotingException {

handler.connected(channel);

}

@Override

public void disconnected(Channel channel) throws RemotingException {

handler.disconnected(channel);

}

@Override

public void sent(Channel channel, Object message) throws RemotingException {

handler.sent(channel, message);

}

@Override

public void received(Channel channel, Object message) throws RemotingException {

handler.received(channel, message);

}

@Override

public void caught(Channel channel, Throwable exception) throws RemotingException {

handler.caught(channel, exception);

}

Dubbo线程池策略


Dubbo处理流程,为了尽量早地释放Netty的I/O线程,某些线程模型会把请求投递到线程池进行异步处理,那么这里所谓的线程池是什么样的线程池呢?

其实这里的线程池ThreadPool也是一个扩展接口SPI,Dubbo提供了该扩展接口的一些实现,具体如下:

  • FixedThreadPool:创建一个具有固定个数线程的线程池。

  • LimitedThreadPool:创建一个线程池,这个线程池中的线程个数随着需要量动态增加,但是数量不超过配置的阈值。另外,空闲线程不会被回收,会一直存在。

  • EagerThreadPool:创建一个线程池,在这个线程池中,当所有核心线程都处于忙碌状态时,将创建新的线程来执行新任务,而不是把任务放入线程池阻塞队列。

  • CachedThreadPool:创建一个自适应线程池,当线程空闲1分钟时,线程会被回收;当有新请求到来时,会创建新线程。

知道了这些线程池之后,那么是什么时候进行SPI加载对应的线程池实现呢?具体是在Dubbo 线程模型获取对应线程池时进行SPI加载的,具体逻辑在方法 org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository#createExecutor中:

private ExecutorService createExecutor(URL url) {

return (ExecutorService) extensionAccessor.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);

}

@SPI(value = “fixed”, scope = ExtensionScope.FRAMEWORK)

public interface ThreadPool {

/**

  • Thread pool

  • @param url URL contains thread parameter

  • @return thread pool

*/

@Adaptive({THREADPOOL_KEY})

Executor getExecutor(URL url);

}

从代码来看,默认的线程池策略是fixed模式的线程池,其coreSize默认为200,队列大小为0,其代码如下:

/**

  • Creates a thread pool that reuses a fixed number of threads

  • @see java.util.concurrent.Executors#newFixedThreadPool(int)

*/

public class FixedThreadPool implements ThreadPool {

@Override

public Executor getExecutor(URL url) {

String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME));

int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);

int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);

return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,

queues == 0 ? new SynchronousQueue() :

(queues < 0 ? new LinkedBlockingQueue()
new LinkedBlockingQueue(queues)),

new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));

}

}

36.Dubbo需要 Web 容器吗?

==============================================================================

不需要,如果硬要用 Web 容器,只会增加复杂性,也浪费资源。

37.Dubbo内置了哪几种服务容器?

==============================================================================

Dubbo 的服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。

  • Spring Container

  • Jetty Container

  • Log4j Container

38.当一个服务接口有多种实现时怎么做?

===============================================================================

当一个接口有多种实现时,可以用 group 属性来分组,服务提供方和消费方都指定同一个 group 即可。

39.服务上线怎么兼容旧版本?

==========================================================================

可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似。

40.Dubbo支持分布式事务吗?

============================================================================

  • 目前暂时不支持。可与通过 tcc-transaction 框架实现

  • tcc-transaction 是开源的 TCC 补偿性分布式事务框架 。

  • Git 地址:https://github.com/changmingxie/tcc-transaction

  • TCC-Transaction 通过 Dubbo 隐式传参的功能,避免自己对业务代码的入侵。

41.Dubbo telnet 命令能做什么?

==================================================================================

dubbo 通。过 telnet 命令来进行服务治理。具体使用看这篇文章《dubbo服务调试管理实用命令》。

42.Dubbo必须依赖的包有哪些?

=============================================================================

Dubbo 必须依赖 JDK,其他为可选。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
)41.Dubbo telnet 命令能做什么?

==================================================================================

dubbo 通。过 telnet 命令来进行服务治理。具体使用看这篇文章《dubbo服务调试管理实用命令》。

42.Dubbo必须依赖的包有哪些?

=============================================================================

Dubbo 必须依赖 JDK,其他为可选。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-5XBasYZd-1714760477739)]

[外链图片转存中…(img-sRgdseTa-1714760477740)]

[外链图片转存中…(img-aLW6EjoE-1714760477740)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值