返回

在 Yii 中轻松添加请求 ID 头:跟踪和关联分布式请求

php

在分布式系统中,追踪请求在各个服务之间的流转轨迹至关重要。一个常见的做法是为每个请求分配一个唯一的标识符,即请求 ID,并将其包含在请求头中。当请求经过不同的服务时,每个服务都会记录下这个请求 ID,方便我们追踪整个请求的路径。

在 Yii 框架中,我们可以利用行为(Behavior)机制优雅地实现添加请求 ID 的功能。行为可以让我们在不修改现有类的情况下,扩展其功能。简单来说,它就像给类添加了一个插件,可以拦截请求和响应的生命周期事件,并在这些事件发生时执行特定的操作。

首先,我们需要在 Yii 应用的配置文件中注册一个新的行为。打开 components 配置,添加 request-id-behavior 行为:

'request-id-behavior' => [
    'class' => 'common\behaviors\RequestIdBehavior',
],

这段配置告诉 Yii,我们需要使用一个名为 RequestIdBehavior 的行为。这个行为的具体实现代码放在 common/behaviors/RequestIdBehavior.php 文件中。

接下来,我们创建 common/behaviors/RequestIdBehavior.php 文件,并编写 RequestIdBehavior 类的代码:

namespace common\behaviors;

use yii\base\Behavior;
use yii\web\Request;
use yii\web\Response;

class RequestIdBehavior extends Behavior
{
    public function events()
    {
        return [
            Request::EVENT_BEFORE_SEND => 'beforeSend',
            Response::EVENT_AFTER_PREPARE => 'afterPrepare',
        ];
    }

    public function beforeSend(Request $request)
    {
        $request->headers->add('x-request-id', $this->generateRequestId());
    }

    public function afterPrepare(Response $response)
    {
        $response->headers->add('x-request-id', $request->headers->get('x-request-id'));
    }

    protected function generateRequestId()
    {
        return uniqid();
    }
}

在这个类中,我们首先定义了 events() 方法,它告诉 Yii 这个行为需要监听哪些事件。这里我们监听了两个事件:Request::EVENT_BEFORE_SENDResponse::EVENT_AFTER_PREPARE。前者在请求发送之前触发,后者在响应准备完毕之后触发。

然后,我们分别实现了 beforeSend()afterPrepare() 方法。在 beforeSend() 方法中,我们使用 generateRequestId() 方法生成一个唯一的请求 ID,并将其添加到请求头中。在 afterPrepare() 方法中,我们将请求 ID 从请求头中取出,并添加到响应头中。这样,无论是在请求还是响应中,我们都可以看到这个唯一的请求 ID。

generateRequestId() 方法使用 PHP 内置的 uniqid() 函数生成一个唯一的字符串作为请求 ID。你也可以根据自己的需求修改这个方法,使用其他的方式生成请求 ID。

为了验证请求 ID 是否被正确添加,我们可以修改 Yii 的错误处理机制,将请求 ID 记录到日志文件中。打开 main.php 配置文件,在 components 数组中添加以下内容:

'errorHandler' => [
    ...
    'logTarget' => [
        ...
        [
            'class' => 'yii\log\FileTarget',
            'categories' => ['yii\web\HttpException:*'],
            'logFile' => '@runtime/logs/app-errors-with-request-id.log',
            'logVars' => ['_SERVER', 'REQUEST_URI', 'HTTP_REFERER'],
        ],
    ],
],

这段配置告诉 Yii,当发生 HTTP 异常时,将错误信息记录到 app-errors-with-request-id.log 文件中,并包含服务器环境变量、请求 URI 和 HTTP Referer 等信息。

现在,你可以发送一个请求,然后查看 app-errors-with-request-id.log 文件,你会发现每条错误日志中都包含了 x-request-id 头的信息,证明请求 ID 已经被成功添加并记录。

常见问题解答:

  1. 为什么请求 ID 通常放在 HTTP 头中?
    HTTP 头是客户端和服务器之间传递信息的标准方式,将请求 ID 放在 HTTP 头中可以方便地在不同的服务之间传递。

  2. 除了 uniqid() 函数,还有哪些方法可以生成请求 ID?
    可以使用 UUID、雪花算法等方法生成全局唯一的 ID。

  3. 如何将请求 ID 与业务日志关联起来?
    可以在记录业务日志时,将请求 ID 作为日志的一部分,方便后续查询和分析。

  4. 请求 ID 的长度有限制吗?
    HTTP 头的长度是有限制的,请求 ID 的长度不宜过长。

  5. 除了追踪请求,请求 ID 还有什么其他的用途?
    请求 ID 可以用于统计分析、安全审计等方面。