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

一、前言

宜信开源|调用链系列(1):解读UAVStack中的贪吃蛇

上篇文章分享了一下调用链的模型设计及模型时序图。相信大家通过上一篇文章对调用链有了一个整体上的了解,如:调用链是什么、能做什么及整体实现策略。

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

二、服务端信息收集

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

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

三、切点植入

在介绍切点之前我们应该对servlet容器(本文以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对外提供各种切点能力。

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

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

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

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

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

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

五、轻调用链实现

具体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通讯的过程中注入调用链上下文信息的效果。目标服务在解析请求信息时,将调用链上下文进行解析;在初始化调用链上下文逻辑时,使用传递过来的信息初始化目标服务的调用链上下文,实现跨系统调用时调用链连接。

六、总结

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

作者:李崇

来源:宜信技术学院

原文地址:https://www.cnblogs.com/yixinjishu/p/11351055.html

时间: 2024-10-09 08:49:22

调用链系列(2):轻调用链实现的相关文章

青云热链系列之听区块链里最真实的声音6月14日将举行

第一代互联网的起点是TCP/IP协议,就是执行一个网络上所有节点统一格式对等传输信息的开放代码.但是这样一个并不复杂的创新对于人类的影响是划时代的,她把全球统一市场所需要的基本价值观:"自由.平等.博爱",给程序化.协议化.可执行化了.进而派生出STMP邮件协议.HTTP域名协议等,去中心化的实现了全球信息传递的低成本高效率.正如阿里巴巴副总裁高红冰所说: "互联网就是消灭那个价值很低.成本很高的(信息)供应链--它开放.互联.对等.全球化.去中心化." 我们知道:

调用链系列一、Zipkin搭建、Spring-boot集承

一.背景介绍 1.Zipkin是什么 Zipkin分布式跟踪系统:它可以帮助收集时间数据,解决在microservice架构下的延迟问题:它管理这些数据的收集和查找:Zipkin的设计是基于谷歌的Google Dapper论文.每个应用程序向Zipkin报告定时数据,Zipkin UI呈现了一个依赖图表来展示多少跟踪请求经过了每个应用程序:如果想解决延迟问题,可以过滤或者排序所有的跟踪请求,并且可以查看每个跟踪请求占总跟踪时间的百分比. 2.为什么使用Zipkin 随着业务越来越复杂,系统也随之

Spring异步调用原理及SpringAop拦截器链原理

一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { /** * Indicate the 'async' annotation type to be detected at

区块链系列教程

想了解相关区块链开发,技术提问,请加QQ群:538327407 本人行业部分演讲 百家争鸣:区块链大规模商用,三年内能否实现?(CCF论坛:厦门哈希科技CTO林滨) 一.hyperledger fabric 1.在ubuntu下 搭建区块链 (Hyperledger Fabric) 可在腾讯云实验室测试 二.以太坊 Ethereum 三.Fisco Bcos 微众区块链底层 1.第一章 区块链系列 联盟链BCOS 底层搭建 2.第二章bcos sdk下载和配置是使用 2.第三章 通过java S

Spring远端调用的实现-Spring Http调用的实现

1:Spring Http设计思想 最近在研究公司自己的一套rpc远程调用框架,看到其内部实现的设计思想依赖于spring的远端调用的思想,所以闲来无事,就想学习下,并记录下. 作为spring远端调用的实现,最为简单的应该是通过http调用的实现,在这种依赖中不会依赖第三方等相关组件,调用者只需要配置相关http的协议 就可以实现,简单的配置,就可以使用spring的 IOC  容器的bean的定义等等思想去调用,简单,方便,无需写更多的http相关的代码, 比较适合内部通信系统之间的调用.

Qt刷新机制的一些总结(Qt内部画的时候是相当于画在后台一个对象里,然后在刷新的时候调用bitblt统一画,调用window的api并不会影响到后面的那个对象)

前段时间做过一个界面刷新的优化,遇到的坑比较多,在这里做一点点总结吧. 优化的方案是滚动滚动条的时候用截屏的方式代替界面全部刷新,优化完成后,界面在滚动时效率能提升大概一倍,背景介绍完毕. 用到最主要的是QT的截屏功能 window原生api会提供截屏滚动的功能.可以用这个ScrollWindowEx这个api.它会根据相应的参数在屏幕中进行滚动相应的区域.是不是很容易?但是结果却是不理想,因为用的是Qt,控件重写过PaintEvent的方法.调用api实时能看到效果,但是触发一次PaintEv

java调用静态函数时是否会调用构造函数

今天在看<think in java>时,有个问题没搞懂:在调用一个静态函数时,该类的构造函数是否会被调用.在网上并未找到相关内容,不如动手来试试结果. 1 public class test { 2 public test() { 3 System.out.println("constructor"); 4 } 5 public static void print() { 6 System.out.println("static"); 7 } 8 pu

C++之类的成员函数的调用 vs. 普通函数的调用

首先请看下面的语句: Point3d obj; Point3d *ptr = &obj; 当使用上述指针或者对象调用成员函数Func()时,会有: obj.Func(); ptr->Func(); 上述调用的背后到底完成了一些什么呢? 假设Func函数的定义如下: Point3d Point3d::Func() const { Float a = getA(); Point3d ret; ret._x = _x/a; ret._y = _y/a; ret._z = _z/a; return

MFC OnPaint()函数中最先调用CDialog::OnPaint()和最后调用CDialog::OnPaint()的巨大区别

OnPaint()函数中最先调用CDialog::OnPaint()和最后调用CDialog::OnPaint()的巨大区别,如果没有注意这个问题就会出现无厘头式的绘图问题-- 效果就是出不来!在经过两个多小时的折磨后法相问题所在,总结出教训: OnPaint()函数中首先就调用CDialog::OnPaint()函数: [cpp] view plain copy //给主窗体客户区添加位图背景 void CBackimageDlg::OnPaint() { CDialog::OnPaint()