Dubbo内部使用的ChannlHandler组件的原理还不懂吗?不懂的快来学

本文详细解析了Dubbo框架中ChannelHandler的工作原理,包括其在Netty通信框架中的应用,以及Dubbo中各种Handler的作用和协作。重点介绍了核心Handler、线程模型、请求响应处理和心跳机制,为理解DubboRPC调用提供了全面视角。
摘要由CSDN通过智能技术生成

ChannelHandler

如果读者熟悉Netty框架,那么很容易理解Dubbo内部使用的ChannlHandler组件的原理,Dubbo框架内部使用大量Handler组成类似链表,依次处理具体逻辑,比如编解码、心跳时间戳和方法调用Handler等。因为Netty每次创建Handler都会经过ChannelPipeline,大量的事件经过很多Pipeline会有较多的开销,因此Dubbo会将多个Handler聚合为一个Handler。

在详细讲解ChannelHandler之前,我们先弄清楚Dubbo有哪些常用的Handler,它们之前是如何关联及如何协作的。

核心Handler和线程模型

在讲解核心Handler之前,我们先通过表6-5看一下Dubbo中Handler (ChannelHandler)的5种状态。

Dubbo针对每个特性都会实现对应的ChannelHandler,在讲解Handler的职责前,我们先通过表6-6快速浏览已经支持的Handler。

Dubbo中提供了大量的Handler去承载特性和扩展,这些Handler最终会和底层通信框架做关联,比如Netty等。一次完整的RPC调用贯穿了一系列的Handler,如果直接挂载到底层通信框架(Netty ,因为整个链路比较长,则需要触发大量链式查找和事件,不仅低效,而且浪费资源。

图6-5展示了同时具有入站和出站ChannelHandler的布局,如果有一个入站事件被触发,比如连接或数据读取,那么它会从ChannelPipeline头部开始一直传播到Channelpipeline的尾端。出站的I/O事件将从ChannelPipeline最右边开始,然后向左传播。当然,在ChannelPipeline传播事件时,它会测试入站是否实现了 ChannellnboundHandler接口,如果没有实现则会自动跳过,出站时会监测是否实现ChannelOutboundHandler,如果没有实现,那么也会自动跳过。在Dubbo框架中实现的这两个接口类主要是NettyServerHandler和NettyClientHandler0Dubbo通过装饰者模式层包装Handler,从而不需要将每个Handler都追加到Pipeline中。在NettyServer 和 NettyClient 中最多有 3 个 Handler,分别是编码、解码和 NettyServerHandler或 NettyClientHandlero

讲解完Handler的流转机制后,我们再来探讨RPC调用服务方处理Handler的逻辑,在DubboProtocol中通过内部类继承自ExchangeHandlerAdapter,完成服务提供方Invoker实例的查找并进行服务的真实调用,如代码清单6-13所示。

代码清单6-13服务方ExchangeHandlerAdapter内部类实现

代码清单6-13中给出的Handler实现是触发业务方法调用的关键,在服务暴露时服务端已经按照特定规则(端口、接口名、接口版本和接口分组)把实例Invoker存储到HashMap中,客户端调用过来时必须携带相同信息构造的key,找到对应Exporter然后调用。在①中查找当前已经暴露的服务,后面会继续分析这个方法实现。在②中主要包含实例的Filter和真实业务对象,当触发invoker#invoke方法时,就会执行具体的业务逻辑。在DubboProtocol中,我们继续跟踪getlnvoker调用,会发现在服务端唯一标识的服务是由4部分组成的:端口、接口名、接口版本和接口分组。服务端Invoker查找如代码清单6-14所示。

为了理解关键原理,特意移除了异步参数回调逻辑,这部分内容会单独在第9章高级特性中探讨。在①中主要获取协议暴露的端口,比如Dubbo协议默认的端口为20880。在②中获取客户端传递过来的接口名称(大部分场景都是接口名)。在③中主要根据服务端口、接口名、接口分组和接口版本构造唯一的key。④:简单从HashMap中取出对应的Exporter并调用Invoker属性值。分析到这里,读者应该能理解RPC调用在服务端处理的逻辑了。

Dubb。为了编织这些Handler,适应不同的场景,提供了一套可以定制的线程模型。为了使概念更清晰,我们描述的I/O线程是指底层直接负责读写报文,比如Netty线程池。Dubbo中提供的线程池负责业务方法调用,我们称为业务线程。如果一些事件逻辑可以很快执行完成,比如做个标记而已,则可以直接在I/O线程中处理。如果事件处理耗时或阻塞,比如读写数据库操作等,则应该将耗时或阻塞的任务转到业务线程池执行。因为I/O线程用于接收请求,如果I/O线程饱和,则不会接收新的请求。

我们先看一下Dubbo中是如何实现线程派发的,如图6-6所示。

在图6-6中,Dispatcher就是线程池派发器。这里需要注意的是,Dispatcher真实的职责是创建具有线程派发能力的 ChannelHandler,比如 AllChannelHandler > MessageOnlyChannelHandler和ExecutionChannelHandler等,其本身并不具备线程派发能力。

Dispatcher属于Dubbo中的扩展点,这个扩展点用来动态产生Handler,以满足不同的场景。目前Dubbo支持以下6种策略调用,如表6-7所示。

具体业务方需要根据使用场景启用不同的策略。建议使用默认策略即可,如果在TCP连接中需要做安全加密或校验,则可以使用
ConnectionOrderedDispatcher策略。如果引入新的线程池,则不可避免地导致额外的线程切换,用户可在Dubbo配置中指定dispatcher属性让具体策略生效。

Dubbo 请求响应 Handler

在Dubbo框架内部,所有方法调用会被抽象成Request/Response,每次调用(一次会话)都会创建一个请求Request,如果是方法调用则会返回一个Response对象。HeaderExchangeHandler用来处理这种场景,它主要负责以下4种事情。

(1) 更新发送和读取请求时间戳。

(2) 判断请求格式或编解码是否有错,并响应客户端失则的具体原因。

(3) 处理Request请求和Response正常响应。

(4) 支持Telnet调用。

我们首先看一下HeaderExchangeHandler#received实现,如代码清单6-15所示。

①:负责响应读取时间并更新时间戳,在Dubbo心跳处理中会使用当前值并判断是否超过空闲时间。

②:主要处理事件类型,目前主要处理readonly事件,用于Dubbo优雅停机。当注册中心反注册元数据时,因为网络原因,客户端不能及时感知注册中心事件,服务端会发送readonly报文告知下线。

④:处理收到的Response响应,告知业务调用方。

⑤:校验客户端不支持Telnet调用,因为只有服务提供方暴露服务才有意义。这里有个小改进,因为客户端支持异步参数回调,但为什么这里不能支持Telnet调用呢?异步参数回调客户端实际上也会暴露一个服务,因此针对这种场景Telnet应该是允许调用的。

⑥:触发Telnet调用,并将字符串返回给Telnet客户端,关于Telent调用在前面已经分析过了。

接下来我们继续分析如何处理请求和响应(HeaderExchangeHandler#
handleRequestJhandleResponse),如代码清单6-16所示。

在处理请求时,因为在编解码层报错会透传到Handler,所以在①中首先会判断是否是因为请求报文不正确,如果发生错误,则服务端会将具体异常包装成字符串返回,如果直接使用异常对象,则可能造成无法序列化的错误。在②中触发Dubbo协议方法调用,并且把方法调用返回值发送给客户端。如果调用发生未知错误,则会通过③做容错并返回。当发送请求时,会在DefaultFuture中保存请求对象并阻塞请求线程,在④中会唤醒阻塞线程并将Response中的结果通知调用方。

Dubbo 心跳 Handler

Dubbo默认客户端和服务端都会发送心跳报文,用来保持TCP长连接状态。在客户端和服务端,Dubbo内部开启一个线程循环扫描并检测连接是否超时,在服务端如果发现超时则会主动关闭客户端连接,在客户端发现超时则会主动重新创建连接。默认心跳检测时间是60秒,具体应用可以通过heartbeat进行配置。

Dubbo在服务端和客户端都复用心跳实现代码,抽象成HeartBeatTask任务进行处理,如代码6-17所示。

①:遍历所有的Channel,在服务端对应的是所有客户端连接,在客户端对应的是服务端连接。②:主要忽略已经关闭的Socket连接。

③:判断当前TCP连接是否空闲,如果空闲就发送心跳报文。目前判断是否是空闲的,根据Channel是否有读或写来决定,比如1分钟内没有读或写就发送心跳报文。

④:处理客户端超时重新建立TCP连接,目前的策略是检查是否在3分钟内(用户可以设置)都没有成功接收或发送报文。如果在服务端监测则会通过

⑤主动关闭远程客户端连接。

小结

本章首先讲解了 Dubbo调用原理和流程,同时对Dubb。的协议做了详细的讲解,这里的基础知识对RPC调用来说至关重要。在讲解完协议的基础上,我们又对Dubbo实现编解码、解决粘包和解包做了深入探讨。

本章重点在rpc调用,以及处理常规方法调用,我们也对本地Telnet调用的设计和实现原理做了说明。在实际开发过程中,不熟悉Dubbo开发的人员也能快速通过fastjson方式测试和验证服务,在Telnet健康检查方面我们也做了进一步的说明。

最后,我们对Dubbo中比较重要的Handler,比如Request/Response模型Handler和心跳Handler等做了详细的解析,同时对Dubbo的线程模型做了剖析。后面的关注点会聚焦于解决业务问题和服务治理上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值