PHP 服務實現性能剖析、跟蹤和可觀察性實踐
簡介
鏈路追蹤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/ ,效果類似如下:
圖片
接口監控
圖片
數據庫監控
圖片