本文目录导读:

我来帮你详细解释PHP外部服务追踪的实现方法,这是一个非常重要的开发实践,用于监控和调试与第三方API、数据库、微服务等的交互。
🎯 主要追踪维度
请求追踪
<?php
class RequestTracer {
private $startTime;
private $apiCalls = [];
private $requestId;
public function __construct() {
$this->requestId = uniqid('req_', true);
$this->startTime = microtime(true);
}
public function startApiCall($service, $url) {
return [
'service' => $service,
'url' => $url,
'start_time' => microtime(true),
'request_id' => $this->requestId
];
}
public function endApiCall($callId, $response) {
$endTime = microtime(true);
$duration = $endTime - $callId['start_time'];
$this->apiCalls[] = [
'service' => $callId['service'],
'url' => $callId['url'],
'duration' => $duration,
'status' => $response['status'] ?? 'unknown',
'timestamp' => date('Y-m-d H:i:s')
];
return $duration;
}
public function getSummary() {
return [
'request_id' => $this->requestId,
'total_duration' => microtime(true) - $this->startTime,
'api_calls' => $this->apiCalls,
'api_call_count' => count($this->apiCalls)
];
}
}
使用中间件追踪
<?php
class ServiceTrackingMiddleware {
private $tracer;
private $logger;
private $config;
public function __construct($config = []) {
$this->tracer = new RequestTracer();
$this->config = $config;
$this->logger = new Logger($config['log_path'] ?? '/var/log/tracking');
}
public function trackExternalCall($serviceName, $url, $method = 'GET', $data = null) {
$callId = $this->tracer->startApiCall($serviceName, $url);
try {
// 执行外部调用
$response = $this->executeExternalCall($url, $method, $data);
$duration = $this->tracer->endApiCall($callId, $response);
// 记录日志
$this->logApiCall($serviceName, $url, $duration, $response);
// 检查性能阈值
$this->checkPerformanceThreshold($serviceName, $duration);
return $response;
} catch (Exception $e) {
// 记录错误
$this->logError($callId, $e);
throw $e;
}
}
private function executeExternalCall($url, $method, $data) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->config['timeout'] ?? 30,
CURLOPT_CONNECTTIMEOUT => $this->config['connect_timeout'] ?? 5
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'status' => $httpCode,
'data' => $response,
'success' => $httpCode >= 200 && $httpCode < 300
];
}
private function logApiCall($service, $url, $duration, $response) {
$logData = [
'timestamp' => date('c'),
'service' => $service,
'url' => $url,
'duration' => $duration,
'status' => $response['status'],
'success' => $response['success']
];
$this->logger->info('external_api_call', $logData);
}
private function checkPerformanceThreshold($service, $duration) {
$threshold = $this->config['thresholds'][$service] ?? 1.0;
if ($duration > $threshold) {
$this->logger->warning('slow_external_call', [
'service' => $service,
'duration' => $duration,
'threshold' => $threshold
]);
}
}
private function logError($callId, $exception) {
$this->logger->error('external_call_error', [
'request_id' => $callId['request_id'],
'service' => $callId['service'],
'url' => $callId['url'],
'error' => $exception->getMessage()
]);
}
}
// 日志类
class Logger {
private $logPath;
public function __construct($logPath) {
$this->logPath = $logPath;
if (!is_dir($logPath)) {
mkdir($logPath, 0755, true);
}
}
public function info($type, $data) {
$this->writeLog('INFO', $type, $data);
}
public function warning($type, $data) {
$this->writeLog('WARNING', $type, $data);
}
public function error($type, $data) {
$this->writeLog('ERROR', $type, $data);
}
private function writeLog($level, $type, $data) {
$logEntry = [
'level' => $level,
'type' => $type,
'data' => $data,
'memory_usage' => memory_get_usage(true)
];
$logFile = $this->logPath . '/' . date('Y-m-d') . '.log';
file_put_contents(
$logFile,
json_encode($logEntry) . PHP_EOL,
FILE_APPEND | LOCK_EX
);
}
}
使用OpenTracing标准
<?php
// 需要安装 jaeger/opentracing
use OpenTracing\Formats;
use OpenTracing\GlobalTracer;
class OpenTracingServiceTracker {
private $tracer;
private $spans = [];
public function __construct() {
// 初始化Jaeger tracer
$config = Jaeger\Config::getInstance();
$config->gen128bit();
$this->tracer = $config->initTrace('php-service', '0.0.0.0:6831');
GlobalTracer::set($this->tracer);
}
public function startExternalCall($service, $url) {
$spanContext = GlobalTracer::get()->getActiveSpan()->getContext();
$span = GlobalTracer::get()->startSpan('external.call', [
'child_of' => $spanContext,
'tags' => [
'service.name' => $service,
'http.url' => $url,
'span.kind' => 'client'
]
]);
// 注入上下文到HTTP头
$headers = [];
GlobalTracer::get()->inject(
$span->getContext(),
Formats\HttpHeaders,
$headers
);
return [
'span' => $span,
'headers' => $headers
];
}
public function endExternalCall($callData, $status = 200) {
$span = $callData['span'];
$span->setTag('http.status_code', $status);
if ($status >= 400) {
$span->setTag('error', true);
}
$span->finish();
}
}
数据库追踪
<?php
class DatabaseQueryTracker {
private $queries = [];
private $pdo;
private $logger;
public function __construct($dsn, $username, $password, $config = []) {
$this->pdo = new PDO($dsn, $username, $password);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->logger = new Logger($config['log_path'] ?? '/var/log/db_tracking');
}
public function query($sql, $params = []) {
$startTime = microtime(true);
$startMemory = memory_get_usage();
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$duration = microtime(true) - $startTime;
$memoryUsed = memory_get_usage() - $startMemory;
// 记录查询
$this->queries[] = [
'sql' => $sql,
'params' => $params,
'duration' => $duration,
'memory' => $memoryUsed,
'timestamp' => date('c')
];
// 慢查询日志
if ($duration > 0.1) { // 超过100ms
$this->logger->warning('slow_query', [
'sql' => $sql,
'duration' => $duration,
'params' => $params
]);
}
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$this->logger->error('query_error', [
'sql' => $sql,
'error' => $e->getMessage()
]);
throw $e;
}
}
public function getQueryStats() {
$totalTime = array_sum(array_column($this->queries, 'duration'));
$totalMemory = array_sum(array_column($this->queries, 'memory'));
return [
'total_queries' => count($this->queries),
'total_time' => $totalTime,
'average_time' => count($this->queries) > 0 ? $totalTime / count($this->queries) : 0,
'total_memory' => $totalMemory,
'slow_queries' => count(array_filter($this->queries, fn($q) => $q['duration'] > 0.1))
];
}
}
使用追踪中间件(Laravel示例)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
class ExternalServiceTracking
{
public function handle($request, Closure $next)
{
// 请求开始
$startTime = microtime(true);
// 处理请求
$response = $next($request);
// 请求结束
$duration = microtime(true) - $startTime;
// 记录外部服务调用
if ($this->hasExternalCalls($request)) {
Log::channel('external')->info('external_service_call', [
'url' => $request->fullUrl(),
'duration' => $duration,
'method' => $request->method(),
'response_status' => $response->status(),
'external_calls' => $this->getExternalCallsData()
]);
}
// 添加追踪头信息
$response->headers->set('X-Tracking-Id', uniqid());
$response->headers->set('X-Duration', $duration);
return $response;
}
private function hasExternalCalls($request)
{
// 判断请求是否涉及外部服务调用
return true; // 简化示例
}
private function getExternalCallsData()
{
// 获取外部调用数据
return [];
}
}
数据可视化
<?php
class TrackingDashboard
{
public function getMetrics($service, $timeRange = '24h')
{
// 从日志中提取指标
$logs = $this->fetchLogs($service, $timeRange);
return [
'total_requests' => count($logs),
'average_response_time' => $this->calculateAvgTime($logs),
'error_rate' => $this->calculateErrorRate($logs),
'p95_response_time' => $this->calculatePercentile($logs, 95),
'slow_requests' => $this->getSlowRequests($logs)
];
}
private function fetchLogs($service, $timeRange)
{
// 从日志存储中获取数据
return [];
}
private function calculateAvgTime($logs)
{
if (empty($logs)) return 0;
return array_sum(array_column($logs, 'duration')) / count($logs);
}
private function calculateErrorRate($logs)
{
if (empty($logs)) return 0;
$errors = count(array_filter($logs, fn($log) => $log['status'] >= 400));
return ($errors / count($logs)) * 100;
}
private function calculatePercentile($logs, $percentile)
{
$durations = array_column($logs, 'duration');
sort($durations);
$index = ceil($percentile / 100 * count($durations));
return $durations[$index - 1] ?? 0;
}
private function getSlowRequests($logs, $threshold = 1.0)
{
return array_filter($logs, fn($log) => $log['duration'] > $threshold);
}
}
📊 推荐的监控工具
APM工具
- New Relic: 提供PHP代理自动检测
- Datadog: 支持分布式追踪
- Dynatrace: 自动发现服务依赖
开源方案
- Jaeger: Uber开源的分布式追踪系统
- Zipkin: Twitter开源的追踪系统
- Prometheus + Grafana: 监控和可视化
🎯 最佳实践
- 统一追踪ID: 使用唯一ID关联整个请求链路
- 异步日志: 使用消息队列避免阻塞主流程
- 级别分级: 设置不同追踪级别(DEBUG/INFO/WARNING)
- 采样策略: 高频服务使用采样追踪
- 性能考虑: 追踪本身不要成为性能瓶颈
// 采样追踪示例
class SampledTracer {
private $sampleRate;
public function shouldTrace() {
// 1%采样率
return rand(1, 100) <= $this->sampleRate;
}
}
这些方法可以根据你的具体需求选择和组合使用,建议从简单的日志追踪开始,逐步引入更完善的分布式追踪方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播