简介
链路追踪Tracing Analysis为分布式应用的开发者提供了完整的调用链路还原、调用请求量统计、链路拓扑、应用依赖分析等工具,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提高微服务时代下的开发诊断效率。
官方地址:https://github.com/openzipkin/zipkin
Zipkin运行架构
图片
产品架构(链路追踪)
图片
链路追踪的主要工作流程
- 客户侧的应用程序通过集成链路追踪的多语言客户端SDK上报服务调用数据。链路追踪支持多种开源社区的SDK,且支持OpenTracing标准。
- 数据上报至链路追踪控制台后,链路追踪组件进行实时聚合计算和持久化,形成链路明细、性能总览、实时拓扑等监控数据。您可以据此进行问题排查与诊断。
- 调用链数据可对接下游阿里云产品,例如LogSearch、CloudMonitor、MaxCompute等,用于离线分析、报警等场景。
业务场景
随着业务越来越复杂,系统也随之进行各种拆分,特别是随着微服务架构和容器技术的兴起,看似简单的一个应用,后台可能有几十个甚至几百个服务在支撑;一个前端的请求可能需要多次的服务调用最后才能完成;当请求变慢或者不可用时,我们无法得知是哪个后台服务引起的,这时就需要解决如何快速定位服务故障点,zipkin分布式跟踪系统就能很好的解决这样的问题。
图片
请求&响应
微服务架构下,一次请求后端会经历多个服务调用(所有请求链有相同的traceId和不同的spanId),都会沿着traceText带到每一个服务中。
数据是如何上报的?
直接上报数据
图片
“
不通过Agent而直接上报数据的原理(传统框架。PHP-FPM + Nginx模式)
- ThinkPHP6.0
- Laravel
- Yii2.0
通过Agent上报数据
图片
通过Agent上报数据的原理(现代化框架。命令行模式)。
- webman
- Swoole
安装
通过composer安装:
composer require openzipkin/zipkin
使用
创建Tracer
Tracer对象可以用来创建Span对象(记录分布式操作时间)。Tracer对象还配置了上报数据的网关地址、本机IP、采样频率等数据,您可以通过调整采样率来减少因上报数据产生的开销。
function create_tracing($endpointName, $ipv4)
{
$endpoint = Endpoint::create($endpointName, $ipv4, null, 2555);
/* Do not copy this logger into production.
* Read https://github.com/Seldaek/monolog/blob/master/doc/01-usage.md#log-levels
*/
$logger = new \Monolog\Logger('log');
$logger->pushHandler(new \Monolog\Handler\ErrorLogHandler());
$reporter = new Zipkin\Reporters\Http(\Zipkin\Reporters\Http\CurlFactory::create());
$sampler = BinarySampler::createAsAlwaysSample();
$tracing = TracingBuilder::create()
->havingLocalEndpoint($endpoint)
->havingSampler($sampler)
->havingReporter($reporter)
->build();
return $tracing;
}
记录请求数据
$rootSpan = $tracer->newTrace();
$rootSpan->setName('encode');
$rootSpan->start();
try {
doSomethingExpensive();
} finally {
$rootSpan->finish();
}
以上代码用于记录请求的根操作,如果需要记录请求的上一步和下一步操作,则需要传入上下文。示例:
$span = $tracer->newChild($parentSpan->getContext());
$span->setName('encode');
$span->start();
try {
doSomethingExpensive();
} finally {
$span->finish();
}
总体流程
Client Span Server Span
┌──────────────────┐ ┌──────────────────┐
│ │ │ │
│ TraceContext │ Http Request Headers │ TraceContext │
│ ┌──────────────┐ │ ┌───────────────────┐ │ ┌──────────────┐ │
│ │ TraceId │ │ │ X-B3-TraceId │ │ │ TraceId │ │
│ │ │ │ │ │ │ │ │ │
│ │ ParentSpanId │ │ Inject │ X-B3-ParentSpanId │Extract │ │ ParentSpanId │ │
│ │ ├─┼─────────>│ ├────────┼>│ │ │
│ │ SpanId │ │ │ X-B3-SpanId │ │ │ SpanId │ │
│ │ │ │ │ │ │ │ │ │
│ │ Sampled │ │ │ X-B3-Sampled │ │ │ Sampled │ │
│ └──────────────┘ │ └───────────────────┘ │ └──────────────┘ │
│ │ │ │
└──────────────────┘ └──────────────────┘
webman应用
1. 开通ARMS
开通ARMS地址 https://arms.console.aliyun.com/ (一般有15天试用)
2. 获得数据上报接入点url
进入 https://tracing.console.aliyun.com/#/globalSetting/cn-hangzhou/process 按照图示获得接入点url地址。
图片
如果你的服务器在阿里云上可以用阿里云vpc网络接入点,本示例用的是阿里云公网接入点。
安装
通过composer安装:
composer require openzipkin/zipkin
使用
1. 编写中间件
链路监控中间件 app\middleware\ArmsMiddleware.php
<?php
/**
* @desc 全链路监控中间件
* @author Tinywan(ShaoBo Wan)
* @date 2021/12/6 14:06
*/
declare(strict_types=1);
namespace app\middleware;
use Monolog\Handler\ErrorLogHandler;
use Monolog\Logger;
use support\Log;
use think\facade\Db;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use Zipkin\Reporters\Http;
use Zipkin\TracingBuilder;
use Zipkin\Samplers\BinarySampler;
use Zipkin\Endpoint;
use Workerman\Timer;
use const Zipkin\Tags\SQL_QUERY;
class ArmsMiddleware implements MiddlewareInterface
{
/**
* @desc: 方法描述
* @param Request $request
* @param callable $next
* @return Response
* @author Tinywan(ShaoBo Wan)
*/
public function process(Request $request, callable $next) : Response
{
static $tracing = null, $tracer = null;
if (!$tracing) {
$endpoint = Endpoint::create('开源技术小栈', $request->getRealIp(), null, 2555);
$logger = new Logger('log');
$logger->pushHandler(new ErrorLogHandler());
$reporter = new Http(['endpoint_url' => config('security')['endpoint_url']]);
$sampler = BinarySampler::createAsAlwaysSample();
$tracing = TracingBuilder::create()
->havingLocalEndpoint($endpoint)
->havingSampler($sampler)
->havingReporter($reporter)
->build();
$tracer = $tracing->getTracer();
// 55秒上报一次,尽量将上报对业务的影响减少到最低
Timer::add(55, function () use ($tracer) {
$tracer->flush();
});
register_shutdown_function(function () use ($tracer) {
$tracer->flush();
});
}
$rootSpan = $tracer->newTrace();
$rootSpan->setName($request->controller."::".$request->action);
$rootSpan->start();
$request->rootSpan = $rootSpan;
$request->tracer = $tracer;
$result = $next($request);
// 统计sql(日志在内存)
if (class_exists(Db::class)) {
$logs = Db::getDbLog(true);
if (!empty($logs['sql'])) {
foreach ($logs['sql'] as $sql) {
$sqlSpan = $tracer->newChild($rootSpan->getContext());
$sqlSpan->setName(SQL_QUERY);
$sqlSpan->start();
$sqlSpan->tag('db.statement', $sql);
$sqlSpan->finish();
}
}
}
$rootSpan->finish();
return $result;
}
}
2. 配置中间件
在 config/middleware.php 中添加全局中间件如下:
return [
'' => [
\app\middleware\ArmsMiddleware::class,
],
...
];
3. 查看监控
访问地址 https://tracing.console.aliyun.com/ ,效果类似如下:
图片
接口监控
图片
数据库监控
图片