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

从Azure SQL通过sp_invoke_external_rest_endpoint入队的Azure Queue消息成死信问题求助

问题:Azure SQL发送队列消息触发Function后进入死信队列,编码相关问题

我通过Azure SQL的sp_invoke_external_rest_endpoint成功将消息<QueueMessage><MessageText>HelloWorld</MessageText></QueueMessage>发送至Azure Queue,但队列触发的Azure Function取出该消息后,它最终进入了死信队列(poison queue)。若通过Azure门户添加相同消息内容(需勾选“Encode the message body in Base64”选项),则消息可被正常处理。推测这是编码问题,恳请提供解决方案。


可复现代码

SQL代码(Azure SQL Database)

DECLARE @QueueMessage_XML nvarchar(max) = N'&lt;QueueMessage&gt;&lt;MessageText&gt;HelloWorld&lt;/MessageText&gt;&lt;/QueueMessage&gt;';
DECLARE @QueueMessage nvarchar(max) = @QueueMessage_XML;

DECLARE @Time datetime = SYSUTCDATETIME();
DECLARE @FormattedMoment nvarchar(50) = FORMAT(@Time, 'ddd, dd MMM yyyy HH:mm:ss' ) + ' GMT';

DECLARE @ContentType nvarchar(256) = N'&quot;content-type&quot;:&quot;application/xml&quot;';
DECLARE @Accept nvarchar(256) = N'&quot;accept&quot;:&quot;application/xml&quot;';
DECLARE @StorageApiMsVersion nvarchar(256) = N'&quot;x-ms-version&quot;:&quot;2023-11-03&quot;';
DECLARE @MsDate nvarchar(256) = N'&quot;x-ms-date&quot;:&quot;' + @FormattedMoment + N'&quot;';

DECLARE @HeadersArray table([HeaderItem] nvarchar(256) NOT NULL);
INSERT INTO @HeadersArray ([HeaderItem]) VALUES 
    (@ContentType), 
    (@Accept), 
    (@StorageApiMsVersion), 
    (@MsDate);
DECLARE @Headers nvarchar(4000) = N'{' + (SELECT STRING_AGG([HeaderItem], ',') FROM @HeadersArray) + N'}';

DECLARE @Method nvarchar(6) = N'POST';
DECLARE @TimeoutSeconds int = 30;
DECLARE @Response nvarchar(max) = N'';
DECLARE @VisibilityTimeoutSeconds int = 20;

DECLARE @AccountName nvarchar(256)  = N'stacctname';
DECLARE @QueueName nvarchar(256)  = N'request';
DECLARE @QueueEndpoint nvarchar(256)  = N'https://' + @AccountName + N'.queue.core.windows.net';

DECLARE @Url nvarchar(4000) = @QueueEndpoint + N'/' + @QueueName + N'/messages';

DECLARE @ReturnValue int = 0;
EXEC @ReturnValue = sp_invoke_external_rest_endpoint
  @url = @Url,
  @payload = @QueueMessage,
  @headers = @Headers,
  @method = @Method,
  @credential = [https://stacctname.queue.core.windows.net],
  @timeout = @TimeoutSeconds,
  @response = @Response OUTPUT;

SELECT @ReturnValue AS [ReturnValue], @Response AS [Response];

Azure Function代码

环境信息:

  • 隔离模式(Isolated)
  • C# .NET 8
  • Visual Studio 2022 Version 17.9.0 Preview 1.0
using Azure.Storage.Queues.Models;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace FunctionApp;

public class DequeueRequest(ILogger<DequeueRequest> logger)
{
    private readonly ILogger<DequeueRequest> _logger = logger;

    [Function(nameof(DequeueRequest))]
    public void Run([QueueTrigger("request", Connection = "AzureWebJobsStorage")] QueueMessage message)
    {
        //intentionally muted
        _ = message.Body;
    }
}

排查更新

我已大致定位到需在SQL中通过BASE64_ENCODE(CAST(@Payload_String AS varbinary(max)))对消息进行Base64编码,但从队列取出消息后进行JSON反序列化时仍出现无法追踪的错误。直接使用原始字符串可成功反序列化,但调用message.Body.ToString()则失败并导致消息进入死信队列。

成功的反序列化代码

var request = JsonSerializer.Deserialize<Request>(
    @"{""RequestId"":80,""StartDate"":""1753-01-01T00:00:00"",""EndDate"":""9999-12-29T00:00:00""}")

失败的反序列化代码(导致消息成死信)

var request = JsonSerializer.Deserialize<Request>(message.Body.ToString());

解决方案

1. SQL端完成消息Base64编码并添加编码头部

Azure Queue要求Base64编码的消息需通过content-encoding头部告知服务编码类型,否则Function的QueueTrigger无法正确解码。修改SQL代码如下:

DECLARE @QueueMessage_XML nvarchar(max) = N'<QueueMessage><MessageText>HelloWorld</MessageText></QueueMessage>';
-- 将nvarchar(UTF-16)转换为varbinary后进行Base64编码
DECLARE @QueueMessage nvarchar(max) = BASE64_ENCODE(CAST(@QueueMessage_XML AS varbinary(max)));

DECLARE @Time datetime = SYSUTCDATETIME();
DECLARE @FormattedMoment nvarchar(50) = FORMAT(@Time, 'ddd, dd MMM yyyy HH:mm:ss' ) + ' GMT';

DECLARE @ContentType nvarchar(256) = N'"content-type":"application/xml"';
DECLARE @Accept nvarchar(256) = N'"accept":"application/xml"';
DECLARE @StorageApiMsVersion nvarchar(256) = N'"x-ms-version":"2023-11-03"';
DECLARE @MsDate nvarchar(256) = N'"x-ms-date":"' + @FormattedMoment + N'"';
-- 添加content-encoding头部,告知服务消息已Base64编码
DECLARE @ContentEncoding nvarchar(256) = N'"content-encoding":"base64"';

DECLARE @HeadersArray table([HeaderItem] nvarchar(256) NOT NULL);
INSERT INTO @HeadersArray ([HeaderItem]) VALUES 
    (@ContentType), 
    (@Accept), 
    (@StorageApiMsVersion), 
    (@MsDate),
    (@ContentEncoding); -- 新增编码头部
DECLARE @Headers nvarchar(4000) = N'{' + (SELECT STRING_AGG([HeaderItem], ',') FROM @HeadersArray) + N'}';

-- 后续URL、调用sp_invoke_external_rest_endpoint的代码保持不变

2. Function端正确解析消息内容

在Isolated模式下,QueueMessage.Body是BinaryData类型,默认ToString()使用UTF-8编码解析,但SQL中nvarchar转换为varbinary时用的是UTF-16编码,因此需指定编码解析:

using Azure.Storage.Queues.Models;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using System.Text.Json;

namespace FunctionApp;

public class DequeueRequest(ILogger<DequeueRequest> logger)
{
    private readonly ILogger<DequeueRequest> _logger = logger;

    [Function(nameof(DequeueRequest))]
    public void Run([QueueTrigger("request", Connection = "AzureWebJobsStorage")] QueueMessage message)
    {
        // 使用UTF-16(Unicode)编码解析BinaryData,匹配SQL端的编码
        var messageContent = message.Body.ToString(System.Text.Encoding.Unicode);
        // 反序列化JSON内容
        var request = JsonSerializer.Deserialize<Request>(messageContent);
        
        // 后续处理逻辑
        _logger.LogInformation("解析消息成功:RequestId={RequestId}", request?.RequestId);
    }
}

// 假设的Request实体类
public class Request
{
    public int RequestId { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

关键原因说明

  • SQL中nvarchar采用UTF-16编码,转换为varbinary(max)时保留该编码,Base64编码后必须通过content-encoding: base64头部告知Azure Queue服务,否则服务会将其视为原始文本存储,导致Function读取时解码错误。
  • Function端BinaryData.ToString()默认使用UTF-8编码,与SQL端的UTF-16编码不匹配,会导致字符串乱码,最终引发JSON反序列化失败,消息进入死信队列。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 04:18:10