jcchavezs/zipkin

此包已被废弃且不再维护。作者建议使用 openzipkin/zipkin 包。

PHP 的 Zipkin 仪表化

3.2.0 2023-09-28 20:54 UTC

README

CI Latest Stable Version Coverage Status Minimum PHP Version Total Downloads License

Zipkin PHP 是 Zipkin 的官方 PHP 跟踪实现,由 OpenZipkin 社区支持。

安装

composer require openzipkin/zipkin

设置

use Zipkin\Annotation;
use Zipkin\Endpoint;
use Zipkin\Samplers\BinarySampler;
use Zipkin\TracingBuilder;
use Zipkin\Reporters\Http;

// First we create the endpoint that describes our service
$endpoint = Endpoint::create('my_service');

$reporter = new Http(['endpoint_url' => 'http://myzipkin:9411/api/v2/spans']);
$sampler = BinarySampler::createAsAlwaysSample();
$tracing = TracingBuilder::create()
    ->havingLocalEndpoint($endpoint)
    ->havingSampler($sampler)
    ->havingReporter($reporter)
    ->build();

$tracer = $tracing->getTracer();

...

$tracer->flush();

注意:对于更完整的客户端/服务器端示例,请查看此存储库

跟踪

跟踪器创建并连接跟踪,这些跟踪模拟了可能分布式工作的延迟。它可以通过采样来减少进程的负载或减少发送到 Zipkin 的数据量。

跟踪器返回的跟踪在完成时向 Zipkin 报告数据,或者在未采样时什么也不做。在启动跟踪后,您可以注释感兴趣的事件或添加包含详细信息或查找键的标签。

跟踪具有一个上下文,它包括将跟踪放置在表示分布式操作的树中的正确位置的跟踪标识符。

本地跟踪

当跟踪本地代码时,只需在跟踪内运行它即可。

$span = $tracer->newTrace();
$span->setName('encode');
$span->start();

try {
  doSomethingExpensive();
} finally {
  $span->finish();
}

在上面的示例中,跟踪是跟踪的根。在许多情况下,您将是现有跟踪的一部分。在这种情况下,调用 newChild 而不是 newTrace

$span = $tracer->newChild($root->getContext());
$span->setName('encode');
$span->start();
try {
  doSomethingExpensive();
} finally {
  $span->finish();
}

自定义跟踪

一旦您有了跟踪,您可以向其添加标签,这些标签可以用作查找键或详细信息。例如,您可能添加一个包含您的运行时版本的标签。

$span->tag('http.status_code', '200');

RPC 跟踪

RPC 跟踪通常由拦截器自动完成。在幕后,它们添加与它们在 RPC 操作中角色相关的标签和事件。

以下是一个客户端跟踪示例

// before you send a request, add metadata that describes the operation
$span = $tracer->newTrace();
$span->setName('get');
$span->setKind(Kind\CLIENT);
$span->tag('http.status_code', '200');
$span->tag(Tags\HTTP_PATH, '/api');
$span->setRemoteEndpoint(Endpoint::create('backend', 127 << 24 | 1, null, 8080));

// when the request is scheduled, start the span
$span->start();

// if you have callbacks for when data is on the wire, note those events
$span->annotate(Annotation\WIRE_SEND);
$span->annotate(Annotation\WIRE_RECV);

// when the response is complete, finish the span
$span->finish();

采样

采样可以用来减少收集和报告的数据量。当一个跟踪没有被采样时,它不添加任何开销(no-op)。

采样是一个前置决策,这意味着在跟踪中的第一个操作时做出报告数据的决策,并且这个决策会向下传播。

默认情况下,有一个全局采样器,它对所有跟踪操作应用单个速率。 Sampler 是如何表示的,它默认为跟踪每个请求。

自定义采样

您可能希望根据操作的不同应用不同的策略。例如,您可能不希望跟踪对静态资源(如图像)的请求,或者您可能希望跟踪所有对新 API 的请求。

大多数用户将使用框架拦截器来自动化此类策略。以下是他们可能如何内部工作的示例。

private function newTrace(Request $request) {
  $flags = SamplingFlags::createAsEmpty();
  if (strpos($request->getUri(), '/experimental') === 0) {
    $flags = DefaultSamplingFlags::createAsSampled();
  } else if (strpos($request->getUri(), '/static') === 0) {
    $flags = DefaultSamplingFlags::createAsSampled();
  }
  return $tracer->newTrace($flags);
}

传播

传播需要确保来自同一根的活动被收集在一起,并在同一跟踪中。最常用的传播方法是复制来自发送 RPC 请求的客户端到接收它的服务器的跟踪上下文。

例如,当进行下游 Http 调用时,其跟踪上下文会随请求一起发送,并以请求头的形式编码。

   Client Span                                                Server Span
┌──────────────────┐                                       ┌──────────────────┐
│                  │                                       │                  │
│   TraceContext   │           Http Request Headers        │   TraceContext   │
│ ┌──────────────┐ │          ┌───────────────────┐        │ ┌──────────────┐ │
│ │ TraceId      │ │          │ X-B3-TraceId      │        │ │ TraceId      │ │
│ │              │ │          │                   │        │ │              │ │
│ │ ParentSpanId │ │ Extract  │ X-B3-ParentSpanId │ Inject │ │ ParentSpanId │ │
│ │              ├─┼─────────>│                   ├────────┼>│              │ │
│ │ SpanId       │ │          │ X-B3-SpanId       │        │ │ SpanId       │ │
│ │              │ │          │                   │        │ │              │ │
│ │ Sampled      │ │          │ X-B3-Sampled      │        │ │ Sampled      │ │
│ └──────────────┘ │          └───────────────────┘        │ └──────────────┘ │
│                  │                                       │                  │
└──────────────────┘                                       └──────────────────┘

上面的名称来自 B3 Propagation,它是 Brave 的内置功能,并且在许多语言和框架中都有实现。

大多数用户会使用框架拦截器来自动化传播。以下是它们可能如何内部工作。

以下是客户端传播可能的样子

// configure a function that injects a trace context into a request
$injector = $tracing->getPropagation()->getInjector(new RequestHeaders);

// before a request is sent, add the current span's context to it
$injector($span->getContext(), $request);

以下是服务器端传播可能的样子

// configure a function that extracts the trace context from a request
$extractor = $tracing->getPropagation()->getExtractor(new RequestHeaders);
$extracted = $extractor($request);

$span = $tracer->newChild($extracted);
$span->setKind(Kind\SERVER);

如果您没有使用框架或无法访问请求对象,您可以从 $_SERVER 变量中提取上下文

$extractor = $tracing->getPropagation()->getExtractor(new ServerHeaders);
$extracted = $extractor($_SERVER);

提取传播上下文

Extractor 从传入的请求或消息中读取跟踪标识符和采样状态。载体通常是请求对象或头。

SamplingFlags|TraceContext 通常仅与 $tracer->newChild(extracted) 一起使用,除非您在客户端和服务器之间共享span ID。

实现传播

Extractor 将输出一个带有以下之一的 SamplingFlags|TraceContext

  • 如果存在跟踪和span ID,则输出 TraceContext
  • 如果不存在标识符,则输出 SamplingFlags

当前Span

Zipkin 支持一个“当前Span”的概念,表示正在执行的操作。可以使用 Tracer::currentSpan() 向Span添加自定义标签,并使用 Tracer::nextSpan() 创建正在执行的任何内容的子Span。

当前Span的常见用例是对RPC客户端进行测试。例如

/**
  * This http clients composes an http client using PSR7
  */
class TraceClient implements ClientInterface
{
    public function request($method, $uri = '', array $options = [])
    {
        /* Gets the child Span of the current one */
        $span = $this->tracer->nextSpan();
        $span->setKind(Zipkin\Kind\CLIENT);
        $span->tag(Tags\HTTP_PATH, $uri);

        try {
            $response = $this->client->request($method, $uri, $options);
            $span->tag(Tags\HTTP_STATUS_CODE, (string) $response->getStatusCode());

            return $response;
        catch (Throwable $e) {
            $span->setError($e);
            throw $e;
        } finally {
            $span->finish();
        }
    }
}

手动设置作用域中的Span

在编写新的测试时,将创建的Span放置在作用域中作为当前Span非常重要。

在某些边缘情况下,您可能需要临时清除当前Span。例如,启动一个不应与当前请求关联的任务。为此,只需将null传递给 openScope

测试

测试

可以通过以下方式运行测试

composer test

静态检查可以通过以下方式运行

composer static-check

参考