Spark - 各个组件的RPC是怎么通信的

基本概念 【Spark - 各个组件的RPC是怎么通信的】在开始之前,先说几个概念:

  • RpcEndPoint:RPC分布式的一个实例,他用于指定消息的处理,比如接收消息。
  • RpcEndpointRef:RpcEndPoint的引用,也就是说,他指向的是服务端的RpcEndPoint,所以RpcEndpointRef会有服务端的RPC地址。
Spark - 各个组件的RPC是怎么通信的
文章图片

  • Inbox:除了RpcEndPoint和RpcEndpointRef(下图太挤了这两个没画),还存储InboxMessage的消息队列。
  • EndpointData:包括了RpcEndpoint、NettyRpcEndpointRef及Inbox。
综上,EndpointData的结构是这样:
Spark - 各个组件的RPC是怎么通信的
文章图片

RpcEndPoint注册 RpcEndPoint实例化并通过Dispatcher注册的时候,就会实例化上面那些组件,并封装一个EndpointData。这里需要注意的是,Inbox组件在实例化的时候,会往messages这个消息队列放入OnStart消息。
当拿到EndpointData后,Dispatcher就会把这个Dispatcher扔到receivers队列中。
除此之前,还会存储EndpointData和名称的映射关系、RpcEndPoint和RpcEndPointRef的映射关系。
Spark - 各个组件的RPC是怎么通信的
文章图片

扔到receivers队列的EndpointData肯定不是仅仅就扔在那边的,所以他会有一个线程池来进行处理,这个线程池里会有N个叫做MessageLoop的线程,会监听receivers队列,如果队列里有数据,就会把队列的数据拿出来。
这个数据就是EndpointData,对EndpointData进行处理的,是EndpointData内部的Inbox。
我们上面说过,Inbox里面也维护了一个InboxMessage的消息队列messages,Inbox就会把messages的消息拿出来,一个个进行消费。这里的消息都是有自己的类型,比如RpcMessage、OnStart、OnStop等等。
上面还提了,实例化Inbox的时候,messages会有一个OnStart,所以刚开始Inbox的messages队列就是有东西的,也就是说马上就会执行OnStart里对应的方法,也就是RpcEndpoint的onStart。
所以RpcEndpoint的生命周期的签名部分就是构造->onStart。
Spark - 各个组件的RPC是怎么通信的
文章图片

客户端请求发送 RpcEndpoint注册后,就可以接受请求了,我们现在看看客户端怎么发送请求的。
客户端在发送消息前,会把消息封装成RequestMessage,然后再判断消息发送的地址是否是当前地址,如果是当前地址,那流程和上面一样,把消息放入inbox,然后存入receivers队列,等待线程消费。
如果是不一样,那就需要把消息进行序列化,并封装成OutboxMessage,交给Outbox进行处理。
和Inbox类似,Outbox也有自己的OutboxMessage消息队列,不断的从队列拿出messages,把消息发送出去。
Spark - 各个组件的RPC是怎么通信的
文章图片

服务端接收到请求后,就会把消息进行反序列化,然后扔到receivers队列。
总体流程 1、客户端产生一个消息,然后序列化后放入Outbox的队列中。
2、客户端的一个线程把消息取出来,根据服务端的地址,通过netty进行发送
3、服务端接收到消息,把消息进行反序列号,然后通过客户端给定的信息,找到EndpointData。
4、从EndpointData中取出Inbox,并把消息放入到队列。
5、把EndpointData放入receivers队列。
6、线程池中的线程会从receivers队列取出EndpointData,然后调用Inbox的RpcEndPoint对消息进行处理。
7、如果有返回值,就会通过RpcEndPointRef,把消息序列号后,存入Outbox,服务端的线程就会把这个消息返回给客户端。
8、客户端接收消息后,重复第三步骤对消息进行处理。
Spark - 各个组件的RPC是怎么通信的
文章图片

源码思维导图 RpcEnv的创建
NettyRpcEnv#stop
Dispatcher#postMessage
Dispatcher#stop
客户端发送请求
服务端处理请求

    推荐阅读