调用链系列(2):轻调用链实现

1、前言

调用链系列(1):解读UAVStack中的贪吃蛇java

上篇文章分享了一下调用链的模型设计及模型时序图。相信你们经过上一篇文章对调用链有了一个总体上的了解,如:调用链是什么、能作什么及总体实现策略。git

这篇文章咱们继续介绍调用链的服务端信息收集以及服务间上下文传递。github

2、服务端信息收集

服务端信息收集总体流程以下图所示,经过在应用容器(tomcat等)启动过程当中植入切点从而实如今应用逻辑执行以前和以后对请求进行劫持。segmentfault

  • 应用逻辑执行以前:解析request中调用链信息,并初始化调用链上下文;
  • 应用逻辑执行以后:解析response中调用链信息,并将本次请求处理的全部调用链信息输出到日志文件。

3、切点植入

在介绍切点以前咱们应该对servlet容器(本文以tomcat为例)处理一次请求的大体流程有一个总体的了解。tomcat

图片来源于网络网络

在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对外提供各类切点能力。app

有了中间件加强技术,在应用逻辑执行以前和以后的切点就有了,接下来就是在这些切点位置执行咱们本身的调用链逻辑了。框架

4、中间件加强技术在调用链中的使用

上文介绍的间件加强技术是一种经过使用javaagent方式动态地在tomcat代码中植入切点代码并以UAVServer的形式对外提供能力的框架(具体能力后续文章会详细介绍)。轻调用链实现正是使用了UAVServer对外提供的GlobalFilterHandler能力。spa

GlobalFilterHandler: 这里的GlobalFilterHandler是中间件加强技术中的一种能力,与传统的filter没有任何关系。它对外提供了四个能力:

  • doRequest:在全部应用处理请求以前进行劫持;
  • doResponse:在全部应用处理请求以后进行劫持;
  • BlockHandlerChain:阻塞自当前handler之后的全部handler,此处的handler为注册在当前;
  • BlockFilterChain阻塞自当前Filter之后的全部Filter。

调用链借助于GlobalFilterHandler提供的前两个能力,实现了在应用处理请求以前和以后执行调用链逻辑的功能。

5、轻调用链实现

具体UML图以下:

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

有了二次加强技术,咱们就能够开始下面的调用链绘制工做了。

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

  • 解析请求信息,提取其中调用链关心的信息,并将解析出来的信息放入上下文中;
  • 经过解析出来的请求头信息进行逻辑分流,根据不一样的协议类型就行不一样的逻辑处理;

✔mq逻辑
✔http逻辑
✔dubbo逻辑

  • 初始化调用链上下文,并初始化main span上下文;
  • 在应用处理完请求以后,将调用链信息进行统一输出。

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

5.1 解析请求信息

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

5.2 逻辑分流

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

5.3 初始化调用链上下文

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

  • 没有父节点则将当前节点看成初始化节点,并初始化记录当前服务内调用链信息的main span;
  • 有父节点则根据父节点信息初始化当前节点,并初始化记录当前服务内调用链信息的main span。

main span:在服务内可能会进行屡次客户端通信或服务间通信,须要一个main span来记录当前服务内调用链最后一个节点的信息。

5.4 调用链信息输出

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

5.5 服务间上下文传递

对于不一样协议调用链传递信息方式也略有不一样,具体实现方式借助了中间件加强技术提供的另外一个能力:AppFrkHook(简称hook,此功能在客户端调用链实现时会进行具体介绍)。它可以对用户使用的客户端技术进行劫持,如用户使用了httpclient进行通信,则对httpclient进行劫持并动态织入代码,从而达到在http通信的过程当中注入调用链上下文信息的效果。目标服务在解析请求信息时,将调用链上下文进行解析;在初始化调用链上下文逻辑时,使用传递过来的信息初始化目标服务的调用链上下文,实现跨系统调用时调用链链接。

6、总结

读完本文以后读者应该对中间件加强技术的实现有了一个大概的了解,而且对其提供的GlobalFilterHandler能力有了必定的认识。对于调用链应明白了服务端和服务间调用链的绘制全过程。

开源地址:https://github.com/uavorg/uav...

做者:李崇

来源:宜信技术学院