Dubbo源码分析2

这是本人对于Dubbo源码分析的系列二,对于系列一,主要是对Dubbo的初始化、请求-响应过程的分析,并没有详细分析每个细节,接下来的系列主要是分析Dubbo中的各个细节,以供更加准确的了解这个支持SOA的RPC分布式框架,本系列介绍Dubbo中的编码解码与序列化反序列化逻辑及消费者、提供者、注册中心之间的连通性设计,当然,这里只是介绍,具体代码细节不会涉及。

一、Dubbo中编码解码与序列化反序列化逻辑
a) 序列化反序列化
i. 顾名思义,序列化的过程就是将内存中对象格式化成可以存储、远程传输等的过程,而反序列化的过程恰好相反。Dubbo中序列化就是对象与流之间转化的过程,且提供了dubbo、hessian、java、json、nativejava多种序列化支持。如dubbo:
public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
        return new GenericObjectOutput(out);
    }
public ObjectInput deserialize(URL url, InputStream is) throws IOException {
        return new GenericObjectInput(is);
}
序列化是RPC中不可缺少的一部分,也是Dubbo中数据传输的基础,是对编码解码功能的支持。
b) 编码解码
i. NIO中数据的传输是通过缓存器ByteBuffer进行读写,而Netty中使用的是ChannelBuffer缓存器,此处暂不详细说明此缓存器,因为这属于Dubbo底层逻辑。Dubbo在使用Netty进行通信时,没有直接使用ChannelBuffer数据结构,而是使用Request、Response这样我们认识的数据结构,由于Nio底层框架不认识此数据结构,所以我们需要对其进行编码与解码,这个过程可以说是缓存器ChannelBuffer与对象之间的转化,但注意的是,提供者与消费者之间进行传输的是RpcInvocation与Result,不同的是接受到的是DecodeableRpcInvocation与DecodeableRpcResult,这也就是DecodeHandler的作用,实现延迟解码。Request与Response都是在提供者端与消费者端根据具体数据进行创建的。主要有以下编码解码(注意编码解码的逻辑贯穿在请求-响应中):
1. 消费者发起请求:消费者创建封装了RpcInvocation的Request,经过序列化编码后发送给提供者,步骤如下:
InvokerInvocationHandler.invoker()、HeaderExchangerChannel()创建封装RpcInvocation的Request,再由NettyHanlder.writeRequested()、NettyCodecAdapter.encode()进行序列化编码后发送给提供者,此过程中消费者可以暴露一个提供者回调服务,也就是Callback,值得注意的是,此回调服务没有真正的开启Server绑定IP,而是只是export()了,再使用回调服务所在接口的通信通道Channel。
2. 提供者接受请求:提供者接受ChannelBuffer后,对其进行反序列化再编码后生成消费者的Request,由NettyHanlder.messageReceived()、NettyCodecAdapter.decode()触发完成逻辑处理,此过程中提供者可以引用一个消费者提供的回调服务,也就是Callback。
3. 提供者响应请求: 提供者处理消费者的请求后进行响应,即创建Response进行回应,也由NettyHanlder.writeRequested()、NettyCodecAdapter.encode()进行序列化编码后发送给消费者。
4. 消费者接受响应:消费者接受ChannelBuffer后,对其进行反序列化再编码后生成提供者发来的Response,由NettyHanlder.messageReceived()、NettyCodecAdapter.decode()触发完成逻辑处理。
总结:这块属于Dubbo底层通信逻辑,贯穿在请求-响应中,有必要了解下,且对于回调服务的暴露与引用都在这里处理,对于回调服务的了解非常重要。还有Dubbo在编码解码时,不是简单的发送的数据体,还发送了一些头信息,比如:Magic High、Magic Low、Serilazation Id、event、Two Way、Req/Res、response status、request id、data length.
这个与HTTP PROTOCOL相似!

二、Dubbo中消费者、提供者、注册中心之间的连通性设计

a) 三者之间均是长连接,且提供者、消费者一般只在初始化时与注册中心交互或在服务变更时交互
b) 消费者断开时,提供者会马上检测到断开事件,并且关闭与这个消费者之间的连接;注册中心也会马上检测到断开事件,并且马上与它断开连接,还有会通过ondisconnect方法进行清除这个消费者在注册中心注册与订阅的所有服务。
c) 提供者断开时,消费者会马上检测到断开事件,并且关闭与这个提供者之间的连接, 会无限次重连提供者;注册中心也会马上检测到断开事件,并且马上与它断开连接,还有会通过ondisconnect方法进行清除这个提供者在注册中心注册与订阅的所有服务,且通知订阅服务的消费者进行重新对比引用服务列表。此时,消费者会无限次重连提供者、提供者无限次重复注册订阅服务及无线重连注册中心,或由心跳机制探测后进行重连注册中心(心跳机制是为了防止那些意外断线而消费者无法检测到FIN事件,从而无法触发重新连接等一系列操作,也不能感知注册中心是否真的存活)
d) 注册中心断开时,提供者与消费者会马上检测到断开事件,并且关闭与注册中心的连接,由心跳机制完成重连注册中心,提供者与消费者会收集所有注册的服务与所有订阅的服务到失败列表,进行定时重新注册与订阅。
e) 提供者断开时,此时如果消费者只有这个提供者提供服务,则不可以进行服务的远程调用;注册中心断开时,消费者还可以通过本地缓存的提供者列表进行通信,但不可以进行新服务注册与订阅。
f) 当提供者、消费者、注册中心之间无法检测中断开事件时(如:网络断线、网络抖动等),可以通过心跳机制探测连接是否可用,若不可用,提供者端执行断开连接操作、消费者端执行重连操作、注册中心也会进行服务的销毁操作。
g) 数据库注册中心健壮性分析与设计(非集群):
i. 每个消费者、提供者(对于注册中心来说它们都是消费者)在向注册中心注册、订阅服务之前,都在其JVM中缓存了一份列表,以备突发情况,当注册中心宕机或不可用时,所有提供者与消费者会重新尝试注册、订阅所有服务及无限次重连注册中心。
ii. 注册中心接保存着提供者与消费者的注册与订阅服务信息,其中订阅覆盖服务,提供者订阅提供者、覆盖、路由服务信息,而注册中心是通过反向代理推送服务信息给他们。注册中心在其JVM中保存一份所有注册、订阅的服务信息,当数据库不可用或宕机时,注册中心正常对外提供服务,只是暂时无法将服务信息持久化到数据库,当数据库服务器恢复时,注册中心重试程序会自动将JVM中的所有服务信息写回到数据库保存。
iii. 当注册中心宕机重启时,所有消费者提供者会重连此注册中心,这样对注册中心造成的冲击非常大,Dubbo建议的方案是重连时间设置成随机几分钟内。
iv. 另外再说一点,其实在消费者也保存一份提供者服务信息在JVM与本地磁盘上,当注册中心宕机时,不会影响消费者正常使用,甚至可以重启消费者,只是消费者无法发现服务变更与新提供者

你可能感兴趣的:(Dubbo序列化与反序列化,连通性设计)