PHP 服务实现性能剖析、跟踪和可观察性实践

开发 前端
数据上报至链路追踪控制台后,链路追踪组件进行实时聚合计算和持久化,形成链路明细、性能总览、实时拓扑等监控数据。您可以据此进行问题排查与诊断。

简介

链路追踪Tracing Analysis为分布式应用的开发者提供了完整的调用链路还原、调用请求量统计、链路拓扑、应用依赖分析等工具,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提高微服务时代下的开发诊断效率。

官方地址:https://github.com/openzipkin/zipkin

Zipkin运行架构

图片图片

产品架构(链路追踪)

图片图片

链路追踪的主要工作流程

  1. 客户侧的应用程序通过集成链路追踪的多语言客户端SDK上报服务调用数据。链路追踪支持多种开源社区的SDK,且支持OpenTracing标准。
  2. 数据上报至链路追踪控制台后,链路追踪组件进行实时聚合计算和持久化,形成链路明细、性能总览、实时拓扑等监控数据。您可以据此进行问题排查与诊断。
  3. 调用链数据可对接下游阿里云产品,例如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/ ,效果类似如下:

图片图片

接口监控

图片图片

数据库监控

图片图片

责任编辑:武晓燕 来源: 开源技术小栈
相关推荐

2024-03-27 14:43:07

.NET Core后端监控可观测性

2021-07-12 11:24:00

流利说可观察性平台阿里云

2021-06-06 22:39:48

网络安全监控网络攻击

2022-08-12 06:26:54

微服务架构

2021-01-26 09:11:16

数字体验DEM网络可观察性

2023-06-12 16:45:20

数据管理

2024-06-18 10:16:49

2021-10-26 10:26:25

云计算云计算环境云应用

2021-11-14 22:14:08

人工智能机器学习工具

2021-06-27 17:18:23

网络可观察性网络网络运营

2023-08-24 08:00:00

开发Java可观察性

2023-02-23 19:28:09

ODD测试

2022-07-18 13:37:56

云计算云原生可观察性

2023-02-21 08:00:00

2023-11-17 08:00:54

Tetragon执行工具

2022-12-29 10:16:12

观察性系统监视

2021-09-26 09:50:21

开发技能程序

2023-01-28 13:42:16

2023-03-10 14:03:57

2023-03-23 13:48:00

工具应用场景选型
点赞
收藏

51CTO技术栈公众号