本文共 4880 字,大约阅读时间需要 16 分钟。
Dubbo里两个主要参与实体是provider和consumer,两者都是相对服务而言,前者是服务的具体实现,后者是服务的消费者。
服务在客户端被异化成提供同质的服务的逻辑集群,消费者的服务请求最终都会通过集群select出一个invoker进行远程调用,整个过程对用户是非感知的。
上图是客户端引用服务的完整过程,其中主要涉及5个实体:为了叙述方便,后文提到集群如果不做特殊说明则是指集群invoker。
Reference最终按以下结构将以上实体聚合起来
引用和集群(非invoker)是依赖关系,前者依赖后者生成集群invoker。集群invoker和普通invoker(图里假设应用协议是dubbo)都实现了接口invoker,但前者通过目录聚集了后者的。调用链路上,reference将请求委派给集群,集群从目录下list所有invoker再由负载均衡select,最后由被选出的invoker发起remoting,如果失败则遵循当次集群HA协议重试或者快速失败等等。
前面几章都或多或少提到了注册中心,注册中心是以URL流转驱动的。提供者将其配置以及地址信息通过注册中心送达到消费者,消费者再基于该配置生成对提供者的引用。理解注册中心必须首先理解URL,对URL还有疑问的可以参考上篇里的整体架构章节。
注册中心对订阅和发布的匹配是以path,以及version,group和classifier三个参数作为依据。因此提供者可以通过group和classifier提供差异化服务,消费者也可以通过这两者指定差异化服务。
接下来再以zookeeper注册中心举例说明一下注册中心的工作原理。
RPC组件分成6层,自上而下分别是应用层,交换层,传输层,转码/序列化层、事件处理层、事件分发层。
Dubbo抽象出端(endpoint)的概念,端是个点,点对点之间可以进行双向传输。在此基础上扩展出通道(Channel),客户端(Client),服务端(Server)三个概念。在传输层,客户端和服务端更多体现为语义上的区别,并不区分请求和应答职责,二者拥有的都是发送能力。但客户端拥有体现其特有职责的重连能力,连接肯定都是由客户端发起,它一般是在连接超时时由心跳任务发起。客户端没有显式的连接以及断连语义,在客户端被初始化出来时就默认开启并建立与服务端连接,且通过定时任务维护通道的连接状态。因此客户端和服务端除了重连以外都只有close和send两个影响网络出书的动作。
服务端和客户端以通道作为传输桥梁,通道和客户端是一一对应的关系,但和服务端是多对一的关系。因为客户端和通道一对一的关系,所以在设计层面可以处理成客户端继承通道。但不管是客户端还是服务端,它们和通道的关系都体现为聚合,通过聚合的通道进行消息传输。那为什么要让客户端继承通道,只能请带着疑问继续往下看。交换层在传输层基础上扩展了请求和应答语义,体现信息交换的含义。它增加了三个实体,分别是ExchangeChannel,ExchangeClient和ExchangeServer。交换层通道增加request动作特指客户端到服务端的请求。交换层客户端和服务端也是点,但是有方向的点,区分明确的请求和应答职责。
客户端有4个动作,分别是open, connect, disconnect和close,与之相应的有5个状态,除了4个动作伴随的状态以外还有halt,客户端本身并不体现该状态,该状态只是出于对心跳超时的描述需要加入。交换层有定时任务做心跳探测,超时会发起重连。
服务端状态相对简单很多,服务端不会主动发起连接或者断连,因此只有open和close动作,相应的也只有opened和closed两个状态。服务端也有心跳任务,心跳超时只会自动关闭server。
ChannelHandler用于网络事件流式处理,它支持5种事件:connected,sent,received,disconnected,caught。分别对应5种网络事件:连接,发送消息,接收消息,断连和异常捕获。需要注意一点,它们对应的是网络事件,并不是端的动作或者状态,只能说端的动作会引起某个网络事件的触发,例如客户端发起connect,引起服务端connected事件。但并不是每个端动作都会触发网络事件,比如open,比如close。
处理器实现是个包装类,通过多层包装编排处理器执行顺序,对事件进行流式处理。它内聚在传输层client或者server里,在收到相应网络事件后被及时触发,并依次执行。它的执行顺序和客户端恰好相反,由下而上,自传输层到应用层。
ExchangeHandler为交换层扩展了reply语义,它并不是一种状态,只是一个动作特指服务端到客户端的应答,它发生在触发的received事件处理执行到交换层时。
下图是处理器在通用情况下的逻辑结构
介绍完事件处理实体和端后再回过头来看端的静态结构图,AbstractPeer是Server和Client各种实现的父类,它既是端又是事件处理实体,因此无论是Server还是Client的实现都有双重角色。
RPC子域以Invoker为根,它由Protocol组件基于动态决策生成,各组件按分层结构依次聚合。在端的一节提过客户端同时也是个通道,交换层通道聚合的传输层通道就可以是传输层客户端。这样处理的必要性在于语义的完整性,请求在交换层经由客户端到通道,通道又依赖传输层通道的传输能力传输。如果不这么处理,则请求直接通过传输通道发送出去而不会经由客户端。
如果交换通道内聚传输客户端或者交换传输端,这两者不管哪种设计都破坏了端和通道的场景语义,也破坏了交换通道本身的独立性,端的任何变化都可能会引起它的变化。Client会默认启动连接状态检查的定时任务,通道状态非连接时会使用新通道连接。最后以thrift应用协议以及netty传输协议举例,描绘一下整个RPC的交互。
转载地址:http://fhaex.baihongyu.com/