當前位置: 妍妍網 > 碼農

PHP 服務實作效能剖析、跟蹤和可觀察性最佳實踐

2024-06-10碼農

簡介

鏈路追蹤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、采樣頻率等數據,您可以透過調整取樣率來減少因上報數據產生的開銷。

    functioncreate_tracing($endpointName, $ipv4)
    {
    $endpoint = Endpoint::create($endpointName, $ipv4, null2555);
    /* 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);
    namespaceapp\middleware;
    useMonolog\Handler\ErrorLogHandler;
    useMonolog\Logger;
    usesupport\Log;
    usethink\facade\Db;
    useWebman\MiddlewareInterface;
    useWebman\Http\Response;
    useWebman\Http\Request;
    useZipkin\Reporters\Http;
    useZipkin\TracingBuilder;
    useZipkin\Samplers\BinarySampler;
    useZipkin\Endpoint;
    useWorkerman\Timer;
    useconstZipkin\Tags\SQL_QUERY;
    classArmsMiddlewareimplementsMiddlewareInterface
    {
    /**
    @desc: 方法描述
    @param Request $request
    @param callable $next
    @return Response
    @author Tinywan(ShaoBo Wan)
    */

    publicfunctionprocess(Request $request, callable $next) : Response
    {
    static $tracing = null, $tracer = null;
    if (!$tracing) {
    $endpoint = Endpoint::create('開源技術小棧', $request->getRealIp(), null2555);
    $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(55function()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/ ,效果類似如下

    介面監控

    資料庫監控