适用于Azure Service Bus的EventGridTrigger主题

时间:2019-03-04 10:36:55

标签: azure-functions azure-data-lake azure-servicebus-topics azure-eventgrid

我基于EventGrid触发器创建了一个Azure函数。每当有新消息到达服务总线主题时,就会触发该触发器。下面是生成的功能模板

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public static void Run(JObject eventGridEvent, TraceWriter log)
{
    log.Info(eventGridEvent.ToString(Formatting.Indented));
}

我对Azure功能的要求是处理数据并将其存储在ADLS中。现在,我该如何解析/反序列化来自JObject类型的数据。在将其持久化到Data Lake存储之前,我需要对该函数中的数据进行规范化。 我需要覆盖该功能吗?

请提供一些详细信息/参考以符合此要求

2 个答案:

答案 0 :(得分:1)

服务总线(高级)在两种情况下发送事件:

  1. ActiveMessagesWithNoListenersAvailable
  2. DeadletterMessagesAvailable

当存在与特定实体关联的消息且不存在活动的侦听器时,将发出第一个事件。该实体将连同其访问所需的其他必需信息(例如名称空间或要从中接收的订阅主题)一起在有效负载中指示。该模式在documentation中定义。

第二个事件模式明智,与第一个事件模式类似,是为死信信件队列发出的。

  

现在我该如何解析/反序列化来自JObject类型的数据。在将其持久化到Data Lake存储之前,我需要对该函数中的数据进行规范化。我需要覆盖该功能吗?

eventGridEvent JSON本身不会给您Azure服务总线消息。 您将需要知道原始消息是如何首先序列化的,即发送方使用了什么。该反序列化将需要进入Function,然后是代码以编写对象Data Lake。

答案 1 :(得分:0)

除了Sean的回答,Azure Service Bus与AEG的集成还可以为ASB实体建立一些监视功能。请注意,这种集成方式与存储Blob帐户不同,在每次创建/删除Blob时都会发布事件。 换句话说,ASB不会为到达ASB实体的每个消息发布事件,事件的发布就像实体看门狗一样。

这种实体看门狗使用以下逻辑:

  1. 实体中没有消息时,不会发布任何事件。
  2. 当第一条消息到达实体时,事件立即发布,并且实体上360秒钟没有活动的侦听器
  3. 当侦听器仍处于非活动状态并且实体中至少有一条消息时,事件每120秒发布一次
  4. 该事件在监听器闲置(非活动)时间达360秒后发布,但实体中仍然至少有一条消息。例如,如果我们在实体中有5条消息,而订阅者将仅使用REST Api上拉一条消息,则下一个事件将在360秒后发布。换句话说,看门狗实体允许将侦听器在空闲时间保持360秒。

基于上面的“看门狗实体”行为,此功能看起来更适合于缓慢的流量消息传递,例如ASB实体上的唤醒和监视侦听器。

请注意,通过在订阅级别使用短重试时间策略,可以避免侦听器的360秒空​​闲时间,因此可以在5分钟的重试时间内将订户再次呼叫3次。

出于测试目的,以下是EventGridTrigger函数的代码片段,用于ASB事件的订阅者。

#r "..\\bin\\Microsoft.Azure.ServiceBus.dll"
#r "Newtonsoft.Json"

using System;
using System.Threading.Tasks;
using System.Text;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Microsoft.Azure.ServiceBus.Primitives;



// sasToken cache
static SasTokenHelper helper = new SasTokenHelper(Environment.GetEnvironmentVariable("AzureServiceBusConnectionString"));

public static async Task Run(JObject eventGridEvent, ILogger log)
{
    log.LogInformation(eventGridEvent.ToString());

    // from the eventgrid payload
    var requestUri = $"{eventGridEvent["data"]?["requestUri"]?.Value<string>()}?api-version=2015-01";

    using (var client = new HttpClient())
    {

        client.DefaultRequestHeaders.Add("Authorization", helper.GetSasToken());

        do
        {
            // read & delete the message 
            var response = await client.DeleteAsync(requestUri);

            // check for message
            if (response.StatusCode != HttpStatusCode.OK)
            {
                log.LogWarning($">>> No message <<<");
                break;
            }

            // message body
            string jsontext = await response.Content.ReadAsStringAsync();

            // show the message
            log.LogInformation($"\nHeaders:\n\t{string.Join("\n\t", response.Headers.Select(i => $"{i.Key}={i.Value.First()}"))}\nBody:\n\t{jsontext}");
        } while (true);

    }

    await Task.CompletedTask;
}




// helpers
class SasTokenHelper
{
    DateTime expiringSaS;
    uint sasTTLInMinutes = 10;
    string sasToken = string.Empty;
    (string hostname, string keyname, string key) config;

    public SasTokenHelper(string connectionString)
    {
        config = GetPartsFromConnectionString(connectionString);
        GetSasToken();
    }

    public string GetSasToken()
    {
        lock (sasToken)
        {
            if (expiringSaS < DateTime.UtcNow.AddMinutes(-1))
            {
                this.sasToken = GetSASToken(config.hostname, config.key, config.keyname, sasTTLInMinutes);
                expiringSaS = DateTime.UtcNow.AddMinutes(sasTTLInMinutes);
            }
            return sasToken;
        }
    }

    internal (string hostname, string keyname, string key) GetPartsFromConnectionString(string connectionString)
    {
        var parts = connectionString.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Split(new[] { '=' }, 2)).ToDictionary(x => x[0].Trim(), x => x[1].Trim());
        return (parts["Endpoint"] ?? "", parts["SharedAccessKeyName"] ?? "", parts["SharedAccessKey"] ?? "");
    }

    internal string GetSASToken(string resourceUri, string key, string keyName = null, uint minutes = 10)
    {
        var tp = SharedAccessSignatureTokenProvider.CreateSharedAccessSignatureTokenProvider(keyName, key, TimeSpan.FromMinutes(minutes));
        return tp.GetTokenAsync(resourceUri, TimeSpan.FromSeconds(60)).Result.TokenValue;
    }
}