首页 > 解决方案 > 尝试使用 API-M 部署 API 端点的金丝雀,但后端服务 url 在后端部分被覆盖

问题描述

我已经针对当前的生产端点设置了我的 API-M 端点,这工作正常。我现在想将一小部分访问者指向一个新的(并希望改进的)端点,但如果它失败了,我希望 API-M 对当前端点重试请求。

我已经制定了这样的策略,配置为在我让它工作时有 100% 的机会击中金丝雀。入站部分取自https://github.com/Azure/api-management-samples/blob/914f1032583dd9e1a1ca8ba01eaba247290fa134/policies/Route%20percentage%20of%20traffic%20to%20canary.policy.xml的示例:

<policies>
    <inbound>
        <base />
        <set-variable name="canaryPercentage" value="@(100)" />
        <set-variable name="canaryBackendServiceUrl" value="https://new-base-uri" />
        <choose>
            <when condition="@(new Random().Next(100) < context.Variables.GetValueOrDefault<int>("canaryPercentage"))">
                <set-backend-service base-url="@(context.Variables.GetValueOrDefault<string>("canaryBackendServiceUrl"))" />
            </when>
        </choose>
    </inbound>
    <backend>
        <retry condition="@(context.Response != null && context.Response.StatusCode >= 500)" count="10" interval="10" max-interval="100" delta="10" first-fast-retry="true">
            <set-backend-service base-url="https://old-base-uri" />
            <forward-request timeout="10" />
        </retry>
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

根据跟踪,入站部分有效。这是“入站”部分的最后一条日志消息:

set-backend-service (0.038 ms)
{
    "message": "Backend service URL was changed.",
    "oldBackendServiceUrl": "https://old-base-uri/",
    "newBackendServiceUrl": "https://new-base-uri",
    "request": {
        "url": "https://new-base-uri/endpoint"
    }
}

但是第二天,它就把它扔掉了(很抱歉今天早上把它搞砸了——这是“后端”部分的第一个声明):

set-backend-service (0.016 ms)
{
    "message": "Backend service URL was changed.",
    "oldBackendServiceUrl": "https://new-base-uri",
    "newBackendServiceUrl": "https://old-base-uri/",
    "request": {
        "url": "https://old-base-uri/endpoint"
    }
}

我很难理解为什么会发生这种情况,因为政策的后端元素中没有提及。

我想要发生的是,请求应该首先被转发到金丝雀(新)端点,如果由于服务器端错误(> = 500)而失败,则针对生产端点重试请求。

标签: azure-api-management

解决方案


设法自己解决了这个问题。我必须承认 API-M 语法有点令人困惑,但诀窍是在重试元素和选择元素中仔细检查条件。还要注意 set-body 元素 - 它是必需的,同时将传入的主体设置为变量。没有它,重试请求将不会发布任何正文,您最终会遇到以下错误:

forward-request (1.326 ms)
{
"messages": [
    "Content length mismatch",
    "Content length mismatch"
    ]
}

该变量在入站元素中设置:

<set-variable name="body" value="@(context.Request.Body.As<string>(preserveContent: true))" />

完成这项工作的完整后端元素:

 <backend>
        <retry condition="@(context.Response.StatusCode >= 400)" count="1" interval="0" first-fast-retry="true">
            <choose>
                <when condition="@(context.Response.StatusCode >= 400)">
                    <set-backend-service base-url="https://production-uri" />
                </when>
            </choose>
            <set-body>@((string)context.Variables["body"])</set-body>
            <forward-request />
        </retry>
    </backend>

推荐阅读