65.9K
CodeProject 正在变化。 阅读更多。
Home

Azure:EventGrid/Azure Function 演示

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (22投票s)

2017年12月9日

CPOL

12分钟阅读

viewsIcon

57521

演示如何一起使用 Azure Functions 和 Azure EventGrid

引言

在本文中,我们将通过使用 Azure Functions 来探讨无服务器计算的概念,我将把它们连接到一个 Azure Event Grid 自定义事件来完成一些工作。演示本身没什么特别之处,但我希望到本文结束时,您能够创建自己的事件,并理解可以使用 Azure Functions 完成什么。

 

什么是无服务器计算?

无服务器计算的承诺


如果您可以将所有时间都花在构建和部署出色的应用程序上,而不用花时间管理服务器,那会怎么样?无服务器计算可以让您做到这一点,因为运行和扩展应用程序所需的基础架构由您管理。将精力集中在您的业务上。将资源从基础架构管理转移到创新和更快地将应用程序推向市场。


无服务器计算是对服务器、基础架构和操作系统的一种抽象。在构建无服务器应用程序时,您无需预配和管理任何服务器,因此可以摆脱基础架构的担忧。无服务器计算由云中近乎实时发生的事件和触发器的响应驱动。作为一个完全托管的服务,服务器管理和容量规划对开发人员来说是不可见的,计费仅基于消耗的资源或代码实际运行的时间。

 

摘自 https://azure.microsoft.com/en-gb/overview/serverless-computing/,截至 2017 年 12 月 6 日

 

代码在哪里?

本文的代码可以在这里找到:https://github.com/sachabarber/msft-azureeventgrid

先决条件

Visual Studio 2017 版本 15.4 或更高版本中的 Azure 开发工作负载包含 Azure Functions 工具。请确保在 Visual Studio 2017 安装中包含 Azure 开发工作负载。

若要创建和部署函数,您还需要:

  • 一个有效的 Azure 订阅。如果您没有 Azure 订阅,可以获得免费帐户。
  • 一个 Azure 存储帐户。要创建存储帐户,请参阅创建存储帐户。

有关更多详细信息,请参阅此页面:https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-vs

 

为什么选择 Azure Functions?

Azure Functions 是一种在云中轻松运行少量代码或“函数”的解决方案。您可以只编写解决手头问题所需的代码,而不必担心整个应用程序或运行它的基础架构。

https://docs.microsoft.com/en-us/azure/azure-functions/functions-overview ,截至 2017 年 12 月 6 日

Azure Functions 提供了许多与 Azure 中其他服务的连接器,例如,支持以下服务,这些服务可以触发您的 Azure Function 代码:

  • Azure Cosmos DB
  • Azure 事件中心
  • Azure 事件网格
  • Azure 移动应用(表)
  • Azure 通知中心
  • Azure 服务总线(队列和主题)
  • Azure 存储(Blob、队列和表)
  • GitHub(Webhook)
  • 本地(使用服务总线)
  • Twilio(短信)

 

响应式编程

 

我非常喜欢 Rx 和响应式编程,对我而言,Azure Functions 也支持这种响应式思维。您发送一个事件,Azure Function 会订阅并根据事件的发生执行操作。这非常易于理解,您可以拥有许多隔离/独立的函数,每个函数都完成其工作的一部分,所有这些都由 Azure 平台提供支持以实现可伸缩性。

 

为什么选择 EventGrid?

EventGrid 是新推出的服务(至少在我撰写本文时是这样),Microsoft 在 EventGrid 主页上是这样描述它的:“使用完全托管的事件路由服务构建响应式的、事件驱动的应用程序。通过将无服务器逻辑连接到多个源的事件来创建更丰富的应用程序场景。”

那么,我们为什么要使用它而不是使用像 ServiceBus/EventHub 这样已有的消息系统呢?

我偶然发现了一篇很棒的文章,其中详细介绍了这一点,我将在此处摘录并引用。

 

Azure 服务总线

当您有购物车、在购买商品时,以及在转移资金或材料时,您可能会使用 Azure 服务总线,因为您需要事务、瞬时一致性、时间控制(例如,如果在 x 分钟内未处理,则将其发送到死信队列或发送到其他地方进行升级)。当您需要执行状态转换的此类协调(工作流)操作时,您将使用 Azure 服务总线。它实际上就像 IBM MQ 或 BizTalk Server,但规模更大。总之,当您需要健壮性且不能丢失任何消息时,您会选择 Azure 服务总线。

 

Azure 事件网格

当您实际发货和运输货物时,您会响应诸如“商品已从货架上取下”、“商品已送到邮寄区域”、“商品已发货”或“此货件被拒绝”等事件。在这种情况下,您不会执行中央控制工作流,而是会使用基于事件的模型为每个产品项运行活动工作流。在这里,您正在响应实时发生的实际变化。Azure Event Grid 非常适合此类响应式场景。

 

Azure 事件中心

Azure 事件中心主要用于遥测场景。假设企业中使用的每个组件(例如,Log4Net 或 Log4J)都为该电子商务应用程序发出遥测数据,并且您希望捕获它,那么您将使用 Azure 事件中心。一个很好的例子是 Azure Application Insight,它在底层使用 Azure 事件中心来捕获遥测信息。

希望这有助于阐明 Microsoft Azure 中需要三种不同的消息传递平台以及何时适当地使用它们。

 

所有这些都来自这篇很棒的文章:https://www.servicebus360.com/blogs/azure-service-bus-event-hub-event-grid-one-choose,截至 2017 年 12 月 6 日

 

演示

好了,理论说得够多了,我们要演示什么呢?嗯,实际上很简单,我们将预配一个新的 Azure EventGrid,然后创建一个 Azure Function 订阅者,它将响应一个由本地控制台应用程序发送到 Azure 托管的 EventGrid 的自定义事件。

就是这样。哦,但是我们希望在将 Azure Function 发布到 Azure 之前,能够使用 Visual Studio 在本地调试本地控制台应用程序和 Azure Function。

我敢肯定,您会同意这些要求很简单。

 

预配 EventGrid

在我们可以进行任何操作之前,我们需要在 Azure 中实际创建一个 EventGrid,因此您需要一个**有效的** Azure 帐户。如果您有其中一个,只需使用门户创建一个新的 EventGrid topic

 

点击查看大图

这将需要一些时间来预配。但当它最终预配完成后,您应该会在门户中看到类似这样的内容。

如果您点击这个,您可以添加 **Event Subscription**。这允许您将 EventGrid 连接到其他组件,这些组件将响应通过 EventGrid 传递的消息。

 

稍后在查看 Azure Function 时,我们将回到这个 **Event Subscription** 区域。

 

EventGrid 消息

使用 Azure EventGrid 时,您不能随意推送任何旧的有效负载,消息有一个固定的架构,您可以在此处阅读:

https://docs.microsoft.com/en-us/azure/event-grid/event-schema

 

下图说明了该架构。

 

那么,这是否意味着我们不能发送自己的数据?

 

不,完全可以。您只需要确保整体消息架构符合上述规范即可。您的自定义数据可以放在 data 字段中。

这是此演示应用程序中 Azure EventGrid 和 Azure Function 都使用的消息。

//generic wrapper for Azure EventGrid custom event
public class CustomEvent<T>
{

    public string Id { get; private set; }

    public string EventType { get; set; }

    public string Subject { get; set; }

    public string EventTime { get; private set; }

    public T Data { get; set; }

    public CustomEvent()
    {
        Id = Guid.NewGuid().ToString();

        DateTime localTime = DateTime.Now;
        DateTime utcTime = DateTime.UtcNow;
        DateTimeOffset localTimeAndOffset = 
        	new DateTimeOffset(localTime, TimeZoneInfo.Local.GetUtcOffset(localTime));
        EventTime = localTimeAndOffset.ToString("o");
    }
}

//This is the data that will end up in the "data" field of the CustomEvent in this small demo
public class Account
{
    public string Name { get; set; }

    public string Gender { get; set; }
}
	

这也是一个相当重要的信息。

事件以数组的形式发送到 Azure Event Grid,其中可以包含多个事件对象。如果只有一个事件,则数组的长度为 1。数组的总大小最多为 1 MB。数组中的每个事件最多为 64 KB。

 

控制台发布者

好了,现在我们知道如何预配 EventGrid 以及消息的样子,我们如何向预配的 EventGrid 发送消息?

这部分其实很简单,我们可以使用一个简单的控制台应用程序(或您喜欢的任何其他应用程序),并确保您有一个 Nuget/硬引用到 Newtonsoft.Json(或您喜欢的任何 JSON 库)。

然后我们可以简单地使用如下代码:

using Msft.Event.Core;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Msft.Event
{

    class Program
    {

        static void Main(string[] args)
        {
            {
                var eventNew = MakeRequestEvent();
                eventNew.Wait();
                Console.WriteLine(eventNew.Result.Content.ReadAsStringAsync().Result);
            }
            Console.ReadKey();
        }

        private static async Task<HttpResponseMessage> MakeRequestEvent()
        {
            string endpoint = "https://YOUR_ENDPOINT_HERE.westus2-1.eventgrid.azure.net/api/events";
            var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Add("aeg-sas-key", "YOUR_KEY_HERE");

            List<CustomEvent<Account>> events = new List<CustomEvent<Account>>();

            var customEvent = new CustomEvent<Account>();
            customEvent.EventType = "TestType";

            customEvent.Subject = "Test";
            customEvent.Data = new Account() { Name = "Maik", Gender = "Male" };

            events.Add(customEvent);
            string jsonContent = JsonConvert.SerializeObject(events);

            Console.WriteLine(jsonContent);

            var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");

            return await httpClient.PostAsync(endpoint, content);
        }
    }
}

在这里,您需要使用门户从您的 Azure EventGrid 中找到以下值:

  • endpoint

这些可以在门户的 EventGrid 窗格中找到。

点击查看大图

 

函数

现在,我们转向 Azure Function。这也很简单,我们在 Visual Studio 中创建一个新的 Azure Functions 项目(如果您看不到此选项,则可能需要运行 Visual Studio Installer 并确保已选择 Azure 工作流元素)。

因此,一旦您有了新的 Azure Functions 项目,就可以通过右键单击项目并选择“添加新项”(如下所示)来添加一个新的 Azure Function。

当被问及时,请选择以下选项:

  • HTTP 触发
  • 匿名访问

之后,您应该会看到此入门代码。

using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

namespace Msft.Azure.Function
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<HttpResponseMessage> Run(
			[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
        {
            log.Info("C# HTTP trigger function processed a request.");

            // parse query parameter
            string name = req.GetQueryNameValuePairs()
                .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
                .Value;

            // Get request body
            dynamic data = await req.Content.ReadAsAsync<object>();

            // Set name to query string or body data
            name = name ?? data?.name;

            return name == null
                ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
                : req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
        }
    }
}

这是一个不错的起点,但我们希望让函数与 EventGrid 进行通信。所以,让我们看看演示代码的最终结果,如下所示:

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Msft.Event.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Msft.Azure.Function
{
    public static class EventGridFunction
    {
        //https://blogs.msdn.microsoft.com/brandonh/2017/11/30/locally-debugging-an-azure-function-triggered-by-azure-event-grid/
        //https://docs.microsoft.com/en-us/azure/event-grid/security-authentication#webhook-event-delivery
        [FunctionName("EventGridFunction")]
        public static async Task<HttpResponseMessage> Run(
			[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
        {


            var payloadFromEventGrid = JToken.ReadFrom(new JsonTextReader(new StreamReader(await req.Content.ReadAsStreamAsync())));
            dynamic eventGridSoleItem = (payloadFromEventGrid as JArray)?.SingleOrDefault();
            if (eventGridSoleItem == null)
            {
                return req.CreateErrorResponse(HttpStatusCode.BadRequest, $@"Expecting only one item in the Event Grid message");
            }

            if (eventGridSoleItem.eventType == @"Microsoft.EventGrid.SubscriptionValidationEvent")
            {
                log.Verbose(@"Event Grid Validation event received.");
                return new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent(JsonConvert.SerializeObject(new
                    {
                        validationResponse = ((dynamic)payloadFromEventGrid)[0].data.validationCode
                    }))
                };
            }


            log.Info("C# HTTP trigger function processed a request.");
            // Get request body
            string data = await req.Content.ReadAsStringAsync();
            var theEvents = JsonConvert.DeserializeObject<List<CustomEvent<Account>>>(data);

            //TODO : handle the events in some way here


            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(JsonConvert.SerializeObject(new
                {
                    status = "good"
                }))
            };
        }
    }
}

现在,那里有很多东西在进行,那都是什么?

这是因为 EventGrid 和 Azure Function 之间的订阅需要协同工作。当 Event Grid 创建订阅终结点时,您的函数需要正确处理 Event Grid 发出的 SubscriptionValidationEvent。您可以在此处阅读更多相关信息:此处

事实上,这不是我提出的。当我研究如何做到这一点时,我偶然发现了 Microsoft 的一位同事写的一篇很棒的文章,它帮助我完成了大部分工作:https://blogs.msdn.microsoft.com/brandonh/2017/11/30/locally-debugging-an-azure-function-triggered-by-azure-event-grid/

 

在本地测试函数

现在我们已经具备了所有拼图碎片,我们想对其进行测试,对吧?所以,让我们考虑一下:我们有一个本地控制台应用程序向 Azure 托管的 EventGrid 发布事件,并且我们想在 Visual Studio 中使用本地 Azure Function 来订阅来自 Azure 托管的 EventGrid 的事件。那么,这将如何运作呢?

幸运的是,在我上面链接的文章中,Brandon 提到了一个名为 **ngrok** 的便捷工具,它允许打开一个 TCP 隧道并提供一个漂亮的终结点 URI,然后您可以将其用于在 Azure 门户中为 EventGrid 订阅设置,指向本地 Azure Function 以供测试。

启动 ngrok 时,您可以选择指定它在本地转发的端口。对于 .Net Azure Functions,默认情况下是 **7071**,因此您需要在运行 ngrok 时将其提供给 **ngrok**。

这是在安装 **ngrok** 后(在命令/PowerShell 窗口中)运行所需的所有内容。

ngrok http -host-header=localhost 7071

但要小心。

您运行的 **ngrok** 命令是基于会话的,当您用来运行 **ngrok** 命令的命令窗口关闭时,该终结点也会失效,您需要再次运行该命令,并在关闭命令窗口时将 Azure 门户 EventGrid 订阅更新为新的 **HTTPS** URI。

 

不要忘记在创建订阅时让您的函数在本地运行,因为这对于正确返回具有正确有效负载的 200 OK 至关重要,否则 EventGrid 将出错,并且订阅创建失败,您将不得不重新创建它。

在一切就绪后,您现在只需将正确的终结点(来自 **ngrok** 命令行窗口的 **HTTPS** URI)信息输入到 Azure EventGrid 订阅区域即可。

点击查看大图

 

好的,现在一切就绪,您应该能够从演示项目中一起运行以下内容:

 

  • Msft.Azure.Function
  • Msft.Event.Console

 

这应该就可以正常工作了。

最后一点是:

 

迁移到 Azure

要从 Visual Studio **发布** Azure Function,您需要一个**有效的** Azure 帐户。设置好之后,您应该可以在 Azure Cloud Explorer(视图菜单下)中看到它。

这是我的工作示例。

一旦您将它设置好,发布到 Azure 就相当简单了,只需选择 Azure Function 项目,右键单击它并选择 **Publish** 菜单。

然后您应该选择一个新配置文件,在那里选择 **Azure Function App**,然后单击对话框中的 **publish** 按钮。

点击查看大图

 

一旦成功发布到 Azure,您需要获取函数的终结点,并更新与 EventGrid 关联的订阅,以便当通过 EventGrid 传递任何新事件时,会调用**真实**的云端 **Azure Function**。您将获取 **Azure Function App** 的终结点,然后像之前为本地测试一样更新 EventGrid 订阅,只不过现在您将使用从 Azure 门户获取的 **Azure Function App** 的真实地址。

 

 

结论

我非常喜欢使用 Azure Functions 来处理小型的离散工作,我不会到处都使用它们。事实上,我们不需要它们处理任何需要长时间运行的工作者,我们倾向于使用 Azure Service Fabric 来处理这类任务。然而,您无法忽视 Azure Functions 使用起来有多么简单,以及有多少挂钩来支持它们。

如果您正在使用 Azure,那么在您考虑的任何未来架构中,它们都值得考虑。

 

© . All rights reserved.