PHP `Distributed Tracing` (`OpenTelemetry`/`Zipkin`):追踪跨服务调用

各位观众老爷们,大家好!今儿咱就来聊聊PHP里的“分布式追踪”这事儿,保证让你听完之后,也能像福尔摩斯一样,把藏在服务调用深处的Bug给揪出来。

开场白:你家的微服务“迷路”了吗?

想象一下,你家搞了个微服务架构,服务A调用服务B,服务B又调用服务C… 哇,链条一下子就拉长了。一旦某个请求慢了,你咋知道是哪个环节出了问题?靠猜?靠日志?那效率可就太低了。

这时候,就需要我们的主角——“分布式追踪”闪亮登场了。它就像一个GPS,能帮你追踪请求在各个服务之间的“旅行轨迹”,让你对整个调用链一目了然。

第一章:什么是分布式追踪?(别跟我说你不知道)

简单来说,分布式追踪就是一种监控和诊断分布式系统的技术。它通过在请求链路中添加唯一的ID,将一次用户请求串联起来,记录请求经过的每个服务的耗时、状态等信息,最终形成一个完整的调用链。

说白了,就是给每个请求打上一个“身份证”,然后记录它都去了哪些地方,干了啥事儿。

第二章:OpenTelemetry vs. Zipkin:两大门派之争

目前比较流行的分布式追踪方案,主要有OpenTelemetry和Zipkin。这两位就像武林中的两大门派,各有千秋。

  • Zipkin:老牌劲旅,简单易用

    Zipkin算是分布式追踪领域的“老前辈”了,它的优点是部署简单,容易上手。但缺点是扩展性相对较弱,对一些高级特性的支持不够完善。

  • OpenTelemetry:后起之秀,潜力无限

    OpenTelemetry则是一个更加开放和标准化的方案,它提供了统一的API和SDK,可以支持多种语言和后端存储。它的优点是扩展性强,功能丰富,但缺点是配置相对复杂。

特性 Zipkin OpenTelemetry
诞生时间 较早 较晚
社区活跃度 活跃 非常活跃
标准化 相对较弱 强,旨在成为统一的标准
扩展性 较弱
学习曲线 简单 相对复杂
支持的语言 主要Java,也支持其他语言,但不如OTel广泛 支持多种语言,包括PHP、Java、Python、Go等
后端存储 可以对接多种存储,如Elasticsearch、Cassandra 可以对接多种存储,包括Zipkin、Jaeger、Prometheus等
适用场景 简单微服务,对追踪要求不高 大型微服务,对追踪要求高,需要灵活的配置和扩展

第三章:PHP + OpenTelemetry:手把手教你搭建追踪系统

好了,理论知识咱们先放一边,现在来点干货。咱们以OpenTelemetry为例,手把手教你搭建一个PHP的分布式追踪系统。

1. 安装必要的扩展

首先,你需要安装OpenTelemetry的PHP扩展。可以通过PECL来安装:

pecl install opentelemetry

如果你的PHP版本较低,可能需要先安装一些依赖库,具体可以参考OpenTelemetry的官方文档。

2. 安装OpenTelemetry Collector(简称OTel Collector)

OTel Collector是OpenTelemetry的核心组件,负责接收、处理和导出追踪数据。你可以把它理解为一个“数据中转站”。

你可以通过多种方式安装OTel Collector,比如使用Docker:

docker run --name otel-collector -p 4317:4317 -p 55680:55680 otel/opentelemetry-collector:latest

这个命令会启动一个OTel Collector容器,并将4317端口(gRPC协议)和55680端口(HTTP协议)映射到宿主机。

3. 配置OTel Collector

接下来,你需要配置OTel Collector,告诉它如何处理追踪数据。创建一个config.yaml文件,内容如下:

receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:

exporters:
  zipkin:
    endpoint: "http://localhost:9411/api/v2/spans"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [zipkin]

这个配置文件的意思是:

  • receivers: 接收来自OTLP协议(gRPC和HTTP)的追踪数据。
  • processors: 使用batch处理器,将多个span批量发送。
  • exporters: 将追踪数据导出到Zipkin,Zipkin的地址是http://localhost:9411/api/v2/spans
  • service: 定义了一个名为traces的pipeline,将receivers、processors和exporters连接起来。

然后,重启OTel Collector,并指定配置文件:

docker stop otel-collector
docker run --name otel-collector -p 4317:4317 -p 55680:55680 -v $(pwd)/config.yaml:/etc/otel-collector-config.yaml otel/opentelemetry-collector:latest --config=/etc/otel-collector-config.yaml

4. 修改PHP代码,添加追踪功能

现在,咱们来修改PHP代码,添加追踪功能。首先,引入OpenTelemetry的SDK:

<?php

require_once __DIR__ . '/vendor/autoload.php';

use OpenTelemetrySDKTraceTracerProvider;
use OpenTelemetrySDKResourceResourceInfo;
use OpenTelemetrySDKResourceResourceAttributes;
use OpenTelemetrySemConvResourceAttributes as SemConvAttributes;
use OpenTelemetryAPITraceTracerInterface;
use OpenTelemetryContextContext;
use OpenTelemetryAPITraceSpanKind;
use OpenTelemetryAPITraceStatusCode;
use OpenTelemetrySDKTraceSpanProcessorSimpleSpanProcessor;
use OpenTelemetryContribOtlpGrpcExporter as OtlpExporter;
use OpenTelemetryContribZipkinExporter as ZipkinExporter;
use OpenTelemetrySDKTraceSamplerAlwaysOnSampler;

// 配置资源信息
$resource = ResourceInfo::create(ResourceAttributes::create([
    SemConvAttributes::SERVICE_NAME => 'php-service',
    SemConvAttributes::SERVICE_VERSION => '1.0.0',
]));

// 配置导出器 (这里使用ZipkinExporter)
$exporter = new ZipkinExporter(
    'php-zipkin-exporter',
    'http://localhost:9411/api/v2/spans' // Zipkin的地址
);

// 配置采样器 (这里使用AlwaysOnSampler,采样所有请求)
$sampler = new AlwaysOnSampler();

// 配置Span处理器
$spanProcessor = new SimpleSpanProcessor($exporter);

// 配置TracerProvider
$tracerProvider = new TracerProvider(
    $spanProcessor,
    $resource,
    $sampler
);

// 获取Tracer
$tracer = $tracerProvider->getTracer('php-tracer', '1.0');

// 开始一个Span
$span = $tracer->startSpan('my-operation');

// 设置Span属性
$span->setAttribute('key', 'value');

// 执行一些操作
echo "Hello, world!n";

// 模拟耗时操作
usleep(rand(100, 500) * 1000);

// 记录一个事件
$span->addEvent('my-event', ['key' => 'value']);

// 设置Span状态
$span->setStatus(StatusCode::STATUS_OK, 'Everything is fine');

// 结束Span
$span->end();

// 关闭TracerProvider (可选,但推荐)
$tracerProvider->shutdown();

这段代码做了以下几件事:

  • 初始化TracerProvider: 创建了一个TracerProvider实例,用于管理Tracer。
  • 配置Exporter: 配置了一个ZipkinExporter,将追踪数据发送到Zipkin。
  • 配置Sampler: 配置了一个AlwaysOnSampler,采样所有请求。在生产环境中,你可以使用其他的Sampler,比如TraceIdRatioBasedSampler,根据一定的比例采样请求。
  • 获取Tracer: 通过TracerProvider获取一个Tracer实例,用于创建Span。
  • 创建Span: 使用Tracer创建一个Span,表示一个操作的开始和结束。
  • 设置Span属性: 可以给Span添加一些属性,用于记录一些额外的信息。
  • 记录事件: 可以在Span中记录一些事件,用于标记一些重要的时刻。
  • 设置Span状态: 可以设置Span的状态,表示操作是否成功。
  • 结束Span: 在操作结束后,需要结束Span。

5. 运行PHP代码,查看追踪数据

运行你的PHP代码,然后打开Zipkin的Web UI(通常是http://localhost:9411),你应该可以看到追踪数据了。

6. 跨服务调用追踪

上面只是一个简单的例子,展示了如何在单个服务中添加追踪功能。如果你的应用有多个服务,你需要将Trace ID在服务之间传递,才能实现跨服务调用追踪。

你可以通过HTTP Header来传递Trace ID。OpenTelemetry提供了一些标准的Header,比如traceparenttracestate

<?php

use OpenTelemetryContextPropagationTextMapPropagatorInterface;
use OpenTelemetryContextContext;
use OpenTelemetryAPITraceTracerInterface;

class MyHttpClient
{
    private TracerInterface $tracer;
    private TextMapPropagatorInterface $propagator;

    public function __construct(TracerInterface $tracer, TextMapPropagatorInterface $propagator)
    {
        $this->tracer = $tracer;
        $this->propagator = $propagator;
    }

    public function get(string $url) : string
    {
        $span = $this->tracer->startSpan('http_get');

        // Extract the current context. If there is no current context, this will return Context::getCurrent() which is the root context.
        $context = Context::getCurrent();

        // Inject the current context into the HTTP headers
        $carrier = [];
        $this->propagator->inject($carrier, $context);

        $options = [
            'http' => [
                'header' => array_map(function ($key, $value) {
                    return "$key: $value";
                }, array_keys($carrier), array_values($carrier)),
            ],
        ];
        $context = stream_context_create($options);

        $result = file_get_contents($url, false, $context);

        $span->end();
        return $result;
    }
}

// 使用示例
$httpClient = new MyHttpClient($tracer, OpenTelemetry::getContextPropagator());
$result = $httpClient->get('http://another-service/api/data');

在接收请求的服务中,你需要从HTTP Header中提取Trace ID,并将其设置为当前Context。

第四章:一些高级技巧

  • 自定义Span: 你可以根据需要创建自定义的Span,用于追踪一些特定的操作。
  • 采样策略: 在生产环境中,你需要根据实际情况选择合适的采样策略,以减少追踪数据的量。
  • 日志集成: 可以将追踪数据和日志集成起来,方便排查问题。
  • 监控告警: 可以基于追踪数据创建监控告警,及时发现潜在的问题。

第五章:总结与展望

分布式追踪是微服务架构中不可或缺的一部分。它可以帮助你快速定位问题,提高系统的可观测性。虽然OpenTelemetry和Zipkin各有优缺点,但它们都是非常优秀的分布式追踪方案。

未来,随着微服务架构的普及,分布式追踪技术将会越来越重要。希望今天的分享能帮助你更好地理解和应用分布式追踪技术。

结尾:布置作业

今天的课程就到这里了。布置个作业:

  1. 尝试搭建一个基于OpenTelemetry的PHP分布式追踪系统。
  2. 将追踪数据导出到Zipkin或Jaeger等后端存储。
  3. 探索OpenTelemetry的更多高级特性。

咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注