转载

调用链系列二:解读UAVStack中的调用链技术

在上周的推送中,我们介绍了调用链的模型设计与模型时序图,本周将为大家继续介绍调用链是如何在中间件增强技术的赋能之下实现服务端信息收集以及服务间上下文传递的。

服务端信息收集

服务端信息收集流程如下图所示,通过在应用容器(Tomcat等)启动过程中植入切点,实现在应用逻辑执行之前和之后对请求进行劫持。

  • 应用逻辑执行之前:解析request中的调用链信息,并初始化调用链上下文;

  • 应用逻辑执行之后:解析response中的调用链信息,并将本次请求处理的所有调用链信息输出到日志文件。

调用链系列二:解读UAVStack中的调用链技术

切点植入

在介绍切点之前,我们应该整体了解Servlet容器(本文以Tomcat为例)处理一次请求的基本流程。

调用链系列二:解读UAVStack中的调用链技术

Connector接收到一次连接并转化成请求(Request)后,会将请求传递到Engine管道(Pipeline)的阀(ValveA)中。请求通过Engine的管道传递到Engine Valve,然后经由Engine Valve传递到一个Host的管道中,并在该管道中传递到Host Valve这个阀里;接着从Host Valve传递到一个Context的管道中,并在该管道中传递到Context Valve中;最后传递到Wrapper C内管道所包含的阀Wrapper Valve中,在这里经过过滤器链(Filter Chain)最终送到一个Servlet中。根据Tomcat的这种架构设计特点,我们可以在Tomcat处理一次请求的生命周期过程中植入自己的逻辑,从而增强Tomcat对外提供的能力,即UAV的 中间件增强技术

中间件增强技术除了巧妙运用了tomcat容器的架构设计之外还借助了java Instrumentation(它给我们提供了一种能够在对象第一次加载时动态修改字节码的能力,由于篇幅原因在此不进行详细讲解)。在UAV中通过UAVServer对外提供各种切点能力。

有了中间件增强技术,就有了在应用逻辑执行之前和之后的切点,接下来就是在这些切点位置执行我们自己的调用链逻辑了。

中间件增强技术在调用链中的使用

上文介绍的间件增强技术是一种通过使用javaagent方式动态地在Tomcat代码中植入切点代码并以UAVServer的形式对外提供能力的框架(具体能力后续文章会详细介绍)。轻调用链正是依赖于UAVServer对外提供的GlobalFilterHandler能力。 GlobalFilterHandler:这里的GlobalFilterHandler是中间件增强技术中的一种能力,与传统的filter没有任何关系。它对外提供了四种能力:

  1. doRequest:在所有应用处理请求之前进行劫持;
  2. doResponse:在所有应用处理请求之后进行劫持;
  3. BlockHandlerChain:阻塞自当前handler以后的所有handler,此处的handler为注册在当前;
  4. BlockFilterChain阻塞自当前Filter以后的所有Filter。 调用链借助GlobalFilterHandler提供的前两个能力,实现了在应用处理请求之前和之后执行调用链逻辑的功能。

轻调用链实现

UML图如下:

调用链系列二:解读UAVStack中的调用链技术

从UML图中可以清晰地看到,InvokeChainSupporter(调用链实现逻辑入口和调用链所需资源初始化实现类)将中间件增强技术进行了二次增强。它允许使用者在其中注册不同的handler,并且在handler的preCap和doCap(中间件增强技术中的逻辑执行之前和之后的切点术语)方法之前和之后动态植入adapter,从而执行更多的定制化适配和个性化逻辑。所有supporter和adapter均采用反射调用方式,最大程度上减少了中间件增强技术的依赖。

有了二次增强技术,我们就可以开始下面的调用链绘制工作了。

轻调用链绘制实现主要依赖于注册在InvokeChainSupporter上的ServiceSpanInvokeChainHandler。主要绘制过程如下:

  1. 解析请求信息,从中提取调用链关心的信息,并将解析出来的信息放入上下文中;
  2. 对解析出来的请求头信息进行逻辑分流,根据不同的协议类型进行不同的逻辑处理(MQ逻辑、Http逻辑、Dubbo逻辑);
  3. 初始化调用链上下文,并初始化main span上下文;
  4. 在应用处理完请求之后,统一输出调用链信息。

下面来看一下具体每一步都做了什么。

解析请求信息

对于像Tomcat这样的中间件容器,进入Tomcat的所有请求都会被封装成HttpServletRequest和HttpServletResponse(后文简称request和response)最终进入用户的Servlet中。借助中间件增强技术,调用链在用户逻辑处理之前将request和response进行一次拦截,并解析其中是否含有调用链信息。如果有,则将调用链信息进行封装放入上下文中。

逻辑分流

由于不同协议对应的调用链绘制逻辑也不同,此处调用链会根据协议类型进行一次分发。

初始化调用链上下文

解析调用链上下文中的信息:

  1. 若没有父节点则以当前节点为初始化节点,并初始化记录当前服务内调用链信息的main span;
  2. 若有父节点则根据父节点信息初始化当前节点,并初始化记录当前服务内调用链信息的main span。 main span:在服务内可能会进行多次客户端通讯或服务间通讯,需要一个mainspan来记录当前服务内调用链最后一个节点的信息。

调用链信息输出

在用户逻辑处理结束之后,调用链记录器会从上下文中取出当前服务的调用链信息并将其输出到指定日志路径。

服务间上下文传递

针对不同协议,调用链传递信息的方式也略有不同,具体实现方式借助了中间件增强技术提供的另一个能力:AppFrkHook(简称hook,此功能在客户端调用链实现时具体介绍)。Hook能够对用户使用的客户端技术进行劫持,如用户使用了Httpclient进行通讯,则对Httpclient进行劫持并动态植入代码,从而实现在Http通讯的过程中注入调用链上下文信息。目标服务在解析请求信息时,对调用链上下文进行解析;在初始化调用链上下文逻辑时,使用传递过来的信息初始化目标服务的调用链上下文,实现跨系统调用时的调用链连接。

读完本文,大家应该了解了服务端和服务间调用链的绘制全过程;对中间件增强技术的实现及其提供的GlobalFilterHandler能力有了初步认识。下篇文章我们将向大家介绍如何从Http(HyperTextTransferProtocol) 即超文本传输协议中获取request和response的body和header。

官方网站

开源地址

UAVStack已在Github上开放源码,并提供了安装部署、架构说明和用户指南等双语文档,欢迎访问-给星-拉取~~~

扫一扫下方二维码 关注一个不会让你失望的公众号

调用链系列二:解读UAVStack中的调用链技术
原文  https://juejin.im/post/5bdab053f265da3947318e54
正文到此结束
Loading...