“
大家好,我是吴小飞,目前是开源社区的贡献者。从我毕业开始参加工作,Apache DolphinScheduler就一直伴随着我,算是一个长期的开源社区践行者。今天我将分享关于Apache DolphinScheduler RPC的源码剖析和二次开发的实战案例。
”
文|吴小飞
编辑整理|曾辉
讲师介绍
吴小飞
社区贡献者
1
RPC核心架构的演进设计
首先,让我们来讨论RPC核心架构的演进设计。RPC本身就是一种常用的技术,无论是Java开发还是大数据开发,都会经常使用,尽管不同的人可能使用方式不同。
简单来说,RPC的目标就是让远程方法调用变得像调用本地方法一样简单。与常用的HTTP等协议相比,RPC的本质区别在于它可以基于自定义的通信协议实现直观的远程过程调用,是一种简化调用的通信框架而非简单理解的协议。
在RPC的演进过程中,首先要实现从客户端到服务端的调用,需要具备网络通信信道和通信协议。通信协议可以自定义以标准化请求和响应的数据。
在具备通信和协议之后,需要对数据进行编解码,以实现原始数据的序列化和反序列化,以便通过网络传输序列化后的二进制数据。
在有了编解码后,可以基于编解码实现数据的发送和接收,客户端需要具备网络客户端,服务端需要具备相应的接收端。通过这些基础构建的网络通信框架,可以实现最基本的网络编程。
然后,为了简化开发者的使用,RPC框架一般会使用动态代理机制,将客户端与服务端的网络通信进行屏蔽,并提供同步、单向调用、异步调用和回调等机制。通过动态代理,开发者不需要手动编写客户端连接服务端的网络通信代码。
常用的RPC框架,如Dubbo,都是基于这种模式,在这种基础上,通过动态代理实现了对请求协议对象的屏蔽,并借助反射机制调用真实的业务方法。
通过这个演进过程,基本上实现了一个完整的RPC框架。然而,开发者不需要手动编写客户端与服务端的网络通信代码,因此引入服务治理的功能,即注册中心。服务提供者启动时,会将自身的元数据注册到注册中心,并及时通知给客户端,客户端订阅服务列表,通过负载均衡策略发现服务端并进行调用。
上述内容是RPC框架的一般设计模式,不论是gRPC还是Dubbo等RPC框架都遵循这样的设计思路,核心是客户端和服务端的通信、序列化和负载均衡。下面我们将看一下Apache DolphinScheduler是如何实现这些模块的。
在Apache DolphinScheduler中,整个调用链路可以分为多个抽象的RPC模块。首先,我们可以看到网络传输、协议和编解码这三个部分都是在远程模块中实现的。编解码使用常见的序列化框架,如Python的pickle或者Protobuf,通过网络通信传输数据。客户端的网络请求屏蔽使用了动态代理的方式,尽管没有使用JDK的动态代理,可能是基于字节码的动态代理,如ByteBuddy、CGLIB、Javaassist等。注册中心的实现也是通过SPI的方式,支持不同的注册中心,例如ZooKeeper、Etcd等。
负载均衡也是类似的,通过SPI扩展机制,选择合适的负载均衡策略。在DolphinScheduler中,默认的负载均衡策略是基于服务端的资源消耗情况,如CPU和内存负载来选择相应的工作节点。
通过这些模块的实现,Apache DolphinScheduler构建了一个完整的RPC框架。
2
RPC代码剖析
我们将看到调用链路的具体组成以及涉及的RPC模块。主要包括入口的执行调度器、主机管理器、执行器管理器、Netty通信、编解码等。
这些模块在不同的组件中进行协同工作,形成了一个闭环,实现了RPC的调用和执行逻辑。
根据我们对RPC框架设计的认知,再次回顾Apache DolphinScheduler中的负载均衡设计。我们将看到它的设计遵循了RPC框架的基本原则。
例如,负载均衡模块的设计遵循了统一的UML结构,首先实现自定义的选择器,选择不同的负载均衡策略,然后根据实时的主机信息进行综合判断,计算得出权重,并选择最小权重的主机进行调度。
通过这样的设计,Apache DolphinScheduler实现了高效的负载均衡。
3
二开实战案例
一般来说,针对Apache DolphinScheduler的二次开发主要包含两个方面的工作,即核心组件和数据源插件。
领取专属 10元无门槛券
私享最新 技术干货