前言在微服务体系结构中,请求通常涉及多个模块,多个中间件以及相互协作以完成的多个机器。
在这一系列的呼叫请求中,有些是串行的,有些是并行的。
如何确定在此请求之后调用哪些服务,哪些模块,哪些节点以及调用顺序?如何定位每个模块的性能问题?本文将为您揭示答案。
微服务体系结构这是一个稍微复杂的示例。
如果用户报告某个页面运行缓慢,则我们知道该页面的请求调用链为A -----> C -----> B -----> D,如何定位此时可能引起问题的模块?此外,如果每个服务Service A,B,C,D都部署在多台机器上。
您如何知道某个请求在哪台计算机上调用该服务?可以清楚地看到,由于无法准确定位每个请求的确切路径,在微服务架构下存在以下痛点:1.很难对问题进行故障诊断并且周期很长; 2.很难做到。
重现特定方案3.系统性能瓶颈分析很困难。
有没有一种方法可以准确地生成完整的呼叫链并以可视方式呈现?这需要分布式呼叫链跟踪系统。
分布式呼叫链跟踪系统:考虑设计。
如果我们想自己实现这样的分布式跟踪系统,我们应该如何设计呢?首先,我们必须区分每个呼叫链(一个时髦的名称称为Trace),为其分配一个全局唯一的ID(称为TraceID),并将此ID带到呼叫链上的每个呼叫中,这样,每个子呼叫已连接。
其次,我们必须记录所有调用的顺序和父子关系。
假设存在上述呼叫链,如果我们仅记录这四个呼叫:A ----> BB ----> CA ----> DD ----> ED ----&gt ; F尽管我们知道它属于一个呼叫(相同的TraceID),但仍然无法绘制出完整的呼叫拓扑。
因此,必须记录父子关系:A ----> B是B ----> C的父调用A ----> D是D ----的父调用> E A ----> D或D ----> F的父调用是如何记录的?您需要为每个呼叫分配一个ID(称为SpanID),并将该ID传递给子呼叫。
子调用基于父跨度ID生成自己的SpanID:在这样的表中显示:这样就很容易根据ID之间的关系进行计算。
这绘制了调用链(即可视化视图)。
魔术师代理很容易说,但是在分布式环境中,如何正确生成TraceID,ParentSpanID,SpanID?微服务在这里是为了实现业务,并且当然不执行监视和跟踪工作,因为这对微服务来说太麻烦了。
因此,必须有一个独立的组件来监视微服务之间的调用,并在不干扰微服务的情况下生成这些ID。
这个独立的组件是代理。
如果代理想要执行魔术,则需要将其安装在每个服务所在的计算机上:魔术师遵循的规则也非常简单。
以上图中的服务A上的代理为例:1.当代理监视有人正在呼叫服务A,但没有ParentSpanID时,它知道这是一个全新的呼叫,应创建一个新的TraceID。
2.当代理监视A呼叫B时,它可以生成SpanID = 1,并将此ID作为ParentSpanID传递给B。
这样,当B呼叫C时,B的代理可以将该呼叫的SpanID生成为1.13。
当代理监视A呼叫D时,它可以生成SpanID = 2,并将该ID作为ParentSpanID传递给DD。
调用E和F时,可以分别生成SpanID 2.1和2.2。
您可能已经注意到一个问题:跨进程调用微服务,如何在服务之间传递TraceID和ParentSpanID?这要求代理执行“魔术”。
代理需要了解微服务之间的传输协议,然后静默地将其“隐藏”在微服务中。
将TraceID和ParentSpanID移到某个位置,然后将其传递给下一个服务。
例如,HTTP协议定义了Header和Body。
标头通常会放置请求的长度,并请求非业务信息,例如IP。
业务数据通常放置在正文中。
然后,代理可以悄悄地“隐藏”或隐藏。
标头中的TraceID和ParentSpanID,这样它就不会影响主体中的业务数据,并且可以将跟踪所需的数据传递给下一个服务。
您可能已经想到了代理的实现原理。
可以如下实现该代理:指定“代理”。
在微服务中(例如Dubbo中的MonitorFilter.invoke方法),然后使用动态修改字节码来增强它: