You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在RabbitMQ通信的微服务间共享单个Sentry事务?

如何在采用RabbitMQ通信的微服务间使用单个Sentry事务?

我尝试了以下代码,但消费者会创建新事务而非沿用原有事务,请问如何通过RabbitMQ在两个微服务间传递事务?

生产者中间件代码

public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
    $transaction = SentrySdk::getCurrentHub()->getTransaction();

    if (!is_null($transaction)) {
        $spanContext = new SpanContext();
        $spanContext->setOp('rabbitmq.send');
        $spanContext->setDescription('description');

        $span = $transaction->startChild($spanContext);
        $sentryTrace = $span->toTraceparent();

        $envelope = $envelope->with(new SentryStamp($sentryTrace));

        $envelope = $stack->next()->handle($envelope, $stack);

        $span->finish();

        return $envelope;
    }

    return $stack->next()->handle($envelope, $stack);
}

消费者中间件代码

public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
    /** @var $sentryStamp SentryStamp */
    $sentryStamp = $envelope->last(SentryStamp::class);
    if (!is_null($sentryStamp)) {
        $sentryTrace = $sentryStamp->getSentryTrace();
        $transactionContext = TransactionContext::fromSentryTrace($sentryTrace);
        $transactionContext->setOp('rabbitmq.consumer');
        $transactionContext->setDescription('description');

        $transaction = SentrySdk::getCurrentHub()->startTransaction($transactionContext);
        SentrySdk::getCurrentHub()->setSpan($transaction);

        $spanContext = new SpanContext();
        $spanContext->setOp('processing');
        $spanContext->setDescription("description");
        $span = $transaction->startChild($spanContext);
        SentrySdk::getCurrentHub()->setSpan($span);

        $envelope = $stack->next()->handle($envelope, $stack);

        $span->finish();
        $transaction->finish();

        return $envelope;
    }

    return $stack->next()->handle($envelope, $stack);
}

问题原因与解决方法

你当前的消费者代码会创建新事务,核心原因是TransactionContext::fromSentryTrace($sentryTrace)生成的上下文是用于启动独立子事务的,但你直接调用startTransaction会在Sentry中生成一个全新的事务节点,而非延续生产者的根事务链路。

正确的做法是:消费者端不要启动新事务,而是基于传递的trace信息启动子span,将消息消费过程作为生产者根事务的一部分进行追踪。

修改后的消费者中间件代码

public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
    /** @var $sentryStamp SentryStamp */
    $sentryStamp = $envelope->last(SentryStamp::class);
    if (!is_null($sentryStamp)) {
        $sentryTrace = $sentryStamp->getSentryTrace();
        
        // 基于传递的trace信息创建span上下文,而非事务上下文
        $spanContext = SpanContext::fromSentryTrace($sentryTrace);
        $spanContext->setOp('rabbitmq.consumer');
        $spanContext->setDescription('description');

        // 启动子span,而非新事务
        $consumerSpan = SentrySdk::getCurrentHub()->startSpan($spanContext);
        SentrySdk::getCurrentHub()->setSpan($consumerSpan);

        // 处理消息的子span,嵌套在消费span下
        $processingSpan = $consumerSpan->startChild(new SpanContext([
            'op' => 'processing',
            'description' => 'description'
        ]));
        SentrySdk::getCurrentHub()->setSpan($processingSpan);

        $envelope = $stack->next()->handle($envelope, $stack);

        // 按逆序结束span,保证链路层级正确
        $processingSpan->finish();
        $consumerSpan->finish();

        return $envelope;
    }

    return $stack->next()->handle($envelope, $stack);
}

关键要点说明

  • 生产者端代码无需修改,它已经正确创建了rabbitmq.send子span,并将trace信息通过SentryStamp嵌入到消息中。
  • 消费者端必须用SpanContext::fromSentryTrace()替代TransactionContext::fromSentryTrace(),确保基于原有事务的trace信息创建子span。
  • 调用startSpan()而非startTransaction(),让消费操作归属到生产者发起的根事务链路中。
  • 严格遵循span嵌套顺序:先启动消费span,再启动处理子span,最后按逆序结束,保证链路层级清晰。

内容的提问来源于stack exchange,提问作者sveta600

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:06:21