通过Azure 事件中心接收更改通知

Webhook 不适合在高吞吐量方案中接收更改通知,或者当接收方无法公开可用的通知 URL 时。 或者,可以使用 Azure 事件中心

可以使用Azure 事件中心高吞吐量方案的示例包括订阅大量资源集的应用程序、订阅频繁更改的资源的应用程序,以及跨大量组织订阅资源的多租户应用程序。

本文将指导你完成管理 Microsoft Graph 订阅的过程,以及如何通过 Azure 事件中心 接收更改通知。

使用Azure 事件中心接收更改通知

Azure 事件中心是专为大规模部署而构建的常用实时事件引入和分发服务。 使用 Azure 事件中心接收更改通知与 Webhook 在某些方面有所不同,包括:

  • 不依赖公开显示的通知 URL。 事件中心 SDK 将通知中继到应用程序。
  • 无需恢复通知 URL 验证。 可忽略收到的验证消息。
  • 需要预配事件中心。
  • 需要预配 Azure 密钥保管库。

设置 Azure 密钥保管库和 Azure 事件中心

Azure CLI 允许在 Azure 中编写脚本并自动执行管理任务。 可以在本地计算机上安装 CLI 或直接从 Azure Cloud Shell 运行。

# --------------
# TODO: update the following values
#sets the name of the resource group
resourcegroup=rg-graphevents-dev
#sets the location of the resources
location='uk south'
#sets the name of the Azure Event Hubs namespace
evhamespacename=evh-graphevents-dev
#sets the name of the hub under the namespace
evhhubname=graphevents
#sets the name of the access policy to the hub
evhpolicyname=grapheventspolicy
#sets the name of the Azure KeyVault
keyvaultname=kv-graphevents
#sets the name of the secret in Azure KeyVault that will contain the connection string to the hub
keyvaultsecretname=grapheventsconnectionstring
# --------------
az group create --location $location --name $resourcegroup
az eventhubs namespace create --name $evhamespacename --resource-group $resourcegroup --sku Basic --location $location
az eventhubs eventhub create --name $evhhubname --namespace-name $evhamespacename --resource-group $resourcegroup --partition-count 2 --message-retention 1
az eventhubs eventhub authorization-rule create --name $evhpolicyname --eventhub-name $evhhubname --namespace-name $evhamespacename --resource-group $resourcegroup --rights Send
evhprimaryconnectionstring=`az eventhubs eventhub authorization-rule keys list --name $evhpolicyname --eventhub-name $evhhubname --namespace-name $evhamespacename --resource-group $resourcegroup --query "primaryConnectionString" --output tsv`
az keyvault create --name $keyvaultname --resource-group $resourcegroup --location $location --enable-soft-delete true --sku standard --retention-days 90
az keyvault secret set --name $keyvaultsecretname --value $evhprimaryconnectionstring --vault-name $keyvaultname --output none
graphspn=`az ad sp list --display-name 'Microsoft Graph Change Tracking' --query "[].appId" --output tsv`
az keyvault set-policy --name $keyvaultname --resource-group $resourcegroup --secret-permissions get --spn $graphspn --output none
keyvaulturi=`az keyvault show --name $keyvaultname --resource-group $resourcegroup --query "properties.vaultUri" --output tsv`
domainname=`az ad signed-in-user show --query 'userPrincipalName' | cut -d '@' -f 2 | sed 's/\"//'`
notificationUrl="EventHub:${keyvaulturi}secrets/${keyvaultsecretname}?tenantId=${domainname}"
echo "Notification Url:\n${notificationUrl}"

注意:此处提供的脚本与基于 Linux 的 shell、Windows WSL 和 Azure Cloud Shell兼容。 它需要一些更新才能在 Windows shell 中运行。

创建订阅并接收通知

创建所需的 Azure KeyVault 和Azure 事件中心服务后,现在可以创建更改通知订阅,并开始通过Azure 事件中心接收更改通知。

创建订阅

使用事件中心订阅更改通知与使用 Webhook 订阅更改通知几乎相同。 创建订阅期间main差异是 notificationUrl。 必须将其设置为 EventHub:https://<azurekeyvaultname>.vault.azure.net/secrets/<secretname>?tenantId=<domainname>,并使用以下值:

  • azurekeyvaultname - 创建密钥保管库时为其提供的名称。 可在 DNS 名称中找到它。
  • secretname - 创建密钥时为其提供的名称。 可在 Azure 密钥保管库的“密钥”页面上找到它。
  • domainname - 租户的名称;例如,contoso.com 或 contoso.com。 由于此域用于访问 Azure 密钥保管库,因此它必须匹配持有 Azure 密钥保管库的 Azure 订阅使用的域。 若要获取此信息,可以转到创建的 Azure 密钥保管库的概述页,然后选择订阅。 域名将显示在“目录”字段下。

注意

不允许重复订阅。 当订阅请求包含与现有订阅包含的 changeType资源 相同的值时,请求失败并显示 HTTP 错误代码 409 Conflict和错误消息 Subscription Id <> already exists for the requested combination

接收通知

更改通知现在由事件中心传递到应用程序。 有关详细信息,请参阅事件中心文档中的接收事件

在应用程序中接收通知之前,需要创建另一个具有“侦听”权限的共享访问策略并获取连接字符串,类似于配置事件中心中列出的步骤。

提示

为侦听事件中心消息的应用程序创建单独的策略,而不是重用在 Azure KeyVault 中设置的相同连接字符串。 这种分离遵循最小特权原则,确保解决方案的每个组件仅具有所需的权限。

处理验证通知

每当应用程序创建新订阅时,它都会收到验证通知。 应忽略这些通知。 下面的示例表示验证消息的正文。

 {
    "value":[
        {
            "subscriptionId":"NA",
            "subscriptionExpirationDateTime":"NA",
            "clientState":"NA",
            "changeType":"Validation: Testing client application reachability for subscription Request-Id: 522a8e7e-096a-494c-aaf1-ac0dcfca45b7",
            "resource":"NA",
            "resourceData":{
                "@odata.type":"NA",
                "@odata.id":"NA",
                "id":"NA"
            }
        }
    ]
}

具有大型有效负载的丰富通知的订阅

事件中心的最大消息大小为 1 MB。 使用 丰富通知时,可能会收到超过此限制的通知。 若要通过事件中心接收大于 1 MB 的通知,还必须向订阅请求添加 Blob 存储帐户。

设置存储并创建订阅

  1. 创建存储帐户
  2. 在存储帐户中创建容器 并为其分配名称。
  3. 检索存储帐户访问密钥或连接字符串
  4. 将连接字符串添加到密钥保管库,并为其命名。 此值是机密名称。
  5. 创建或重新创建订阅,现在使用以下语法包括 blobStoreUrl 属性: blobStoreUrl: "https://<azurekeyvaultname>.vault.azure.net/secrets/<secretname>?tenantId=<domainname>"

接收丰富通知

当事件中心收到大于 1 MB 的通知有效负载时,通知不包含丰富通知中包含的 resourceresourceDataencryptedContent 属性。 通知将改为包含一个 附加的PayloadStorageId 属性,该属性的 ID 指向存储这些属性的存储帐户中的 Blob。

如果缺少 Microsoft Graph 更改跟踪应用程序,该怎么办?

租户中可能缺少 Microsoft Graph 更改跟踪服务主体,具体取决于租户的创建时间和管理操作。 服务主体的全局唯一 appId0bf30f3b-4a52-48df-9a82-234910c4a086 ,你可以运行以下查询来确认它是否存在于租户中。

GET https://graph.microsoft.com/v1.0/servicePrincipals(appId='0bf30f3b-4a52-48df-9a82-234910c4a086')

如果服务主体不存在,请按如下所示创建它。 必须向调用应用授予 Application.ReadWrite.All 权限才能运行此操作。

方法 1

POST https://graph.microsoft.com/v1.0/servicePrincipals
Content-type: application/json

{
    "appId": "0bf30f3b-4a52-48df-9a82-234910c4a086"
}

方法 2

POST https://graph.microsoft.com/v1.0/servicePrincipals(appId='0bf30f3b-4a52-48df-9a82-234910c4a086')
Content-type: application/json
Prefer: create-if-missing

{
    "displayName": "Microsoft Graph Change Tracking"
}