2018 年 2 月

第 33 卷,第 2 期

本文章是由機器翻譯。

Azure - 雲端中使用 Azure 事件格線的事件驅動架構

David Barkol

它是令人興奮的時間設為雲端架構設計人員。創新的步調帶來 forefront 一組新的挑戰,且依設計會才可遏制方式解決方案的技術。獲益這股成長是開發人員與架構設計人員可以選擇從多樣的服務和選項。

當開發人員執行拆解其架構,以善用新的服務,例如 Azure 函式、 邏輯應用程式和其他熟悉的障礙介面的練習。在許多情況下,我們發現自己再次拼湊 「 黏附 」,可讓這些服務可正常運作。新的 Azure 事件方格啟動以外的工具來解決這項挑戰,提供完整受管理,可調整且極具彈性的雲端中的第一級的事件路由服務。

在本文中,我要瀏覽 Azure 事件方格的彈性,並示範如何使用它來解決企業應用程式中熟悉的挑戰。也建議您在查看事件方格正式通知,在aka.ms/egblog

顛倒的相依性

不是新的使用中的解決方案或應用程式事件的概念。事實上,事件導向程式設計已成功地過高的概念很久。Pub/Sub 佇列和 Gui 是幾個運用此概念的組織、 應用程式或系統中發生的事件對回應的範例。

其中一個核心的租用戶的事件驅動的架構。 若要反轉現有的服務可能會彼此的相依性圖 1顯示一組依賴相互通訊並支援人力資源 (HR) 部門的處理序的範例。

包含邏輯相關的其他服務的服務
圖 1 包含邏輯相關的其他服務的服務

這項設計工作,每個服務必須包含其他通訊人之相關的一些基本邏輯。這些相依性,也建立挑戰,不但小數位數,因為用這個邏輯分散的架構。經過一段時間,依序展開 [這些類型的方案,因為它們變成難以維護並且愈來愈容易損毀引入更多的變更和相依性。

或者,事件導向的設計背後的概念中升級的事件為項目架構中的第一級棒的概念,將移除這些相依性。這很重要的考量可讓許多其他系統可以利用集中式的服務,相依性和邏輯分散在應用程式的負擔。圖 2強調的相依性的 HR 部門方案反轉藉由引進這個主體的概念。

集中式的服務,它會反轉其他服務之間的相依性
圖 2 的集中式服務,它會反轉其他服務之間的相依性

此金鑰的服務是文章的其餘部分會所有相關。我將探索 Azure 事件方格,以及如何使用它來支援新一代的解決方案。

介紹 Azure 事件方格

Azure 事件方格是新的完全受管理服務,可支援事件路由利用發行者訂閱者模型。基本上,事件的方格是事件路由服務,可管理路由和傳遞的事件從許多來源和 「 訂閱者 」。圖 3所建立的事件方格概觀文件 (bit.ly/2qhaj9q),說明數個發行者和適用於事件方格今天的處理常式。

Azure 事件方格概觀
圖 3 Azure 事件方格概觀

「 發行者 」,例如 Blob 儲存體帳戶、 事件中心或甚至 Azure 訂用帳戶建立事件。當事件發生時,在要發行到端點,稱為到摘要式所有傳入訊息的事件方格服務管理的主題。將成長在 Azure 上與事件方格整合的服務清單中有更多的創新。

事件發行者不限於在 Azure 上的服務。事實上,很常見的使用案例包括從自訂應用程式或從任何地方可以執行的系統產生的事件。這包括裝載在內部,在資料中心,或甚至在其他雲端上的應用程式。這些類型的 「 發行者 」 被指自訂主題。如果它們可以張貼事件方格服務的 HTTP 要求,然後是候選項目當做傳送事件。

事件處理常式包括數個服務在 Azure 上、 以及。這些示範了一些新的無伺服器技術在 Azure 上,例如函式和邏輯應用程式。除了 Azure 自動化中,另一種事件處理常式可能是任何 HTTP 回呼,亦稱為 WebHook。事件方格時,已藉由建立事件訂閱登錄處理常式。如果事件處理常式端點公開可存取且由傳輸層安全性加密,然後訊息可以推送至它從事件方格。

不同於其他許多 Azure 服務,沒有需要佈建或管理事件方格命名空間。原生的 Azure 資源的主題是建立在與中完全透明的使用者自訂主題臨機操作佈建和資源群組中存在時。事件訂閱只與主題相關聯。此模型可簡化管理的主題訂用帳戶,並且讓事件方格密集多租用戶,允許大規模向外延展。

Azure 事件方格的任何語言或平台無關。雖然它原生整合與 Azure 服務,它可以輕鬆地利用支援 HTTP 通訊協定,這樣很聰明及創新服務的任何項目。

事件或命令

我深入了解一些程式碼及建置解決方案,會反白顯示其中部分功能之前,可讓事件與命令之間進行區別。其差異有時可能難以察覺的但請務必了解設計系統依賴訊息時。

當訊息傳送的特定動作或回應時,它是最有可能的命令。例如,如果員工會提升組織內郵件會指示他填寫表單的新管理員,然後它會具有特定用途或意圖。由於訊息的寄件者具有預期的情況下,而且在某些情況下,即使預期的回應,我們可以將此分類為命令。

如果沒有任何知識或期望的如何處理發行訊息,則它已經被視為事件。例如,假設要在組織中,要求相同的員工已變更他們的郵寄地址。因為此動作可能需要在組織中的許多系統,但不需要特別留意其中任何 「 發行者 」,它就會是未定義任何目的的訊息。在此情況下,「 發行者 」 只通知所有相關的當事人已發生事件。它是事件,而且清楚地發生的事件方格這類服務是可行的選項。

我可以花更多時間討論這些區別,此外如何選取適當訊息處理服務在 Azure 上,不過,已超出本文的範圍。建議您將此具洞察力的 post 讀取在主題上的 Clemens Vasters: bit.ly/2CH3sbQ

人力資源案例

若要取得更深入的了解事件方格的最佳方式是撰寫程式碼,利用其功能。在本文中,我將查看來自虛構的 HR 應用程式的一些事件。我將事件發行至自訂主題,然後使用數個不同的處理常式的事件訂閱。

為了讓事情變簡單,我將會實作兩種類型的事件處理常式的系統 — 當員工新增組織與員工會移除。這些事件會關閉,它會提供選項,透過不同方式展示如何篩選及處理事件的本質。方案的視覺表示法如下所示圖 4

範例解決方案
圖 4 範例方案

高階方案所組成,我將在本文中建置數個重要元件。讓我們來瀏覽此處。

HR 應用程式可以將訊息傳送的事件方格主題時,就會員工事件。這包括事件新增和移除組織中的員工。每個訊息會包含員工,她部門和事件類型的相關資訊。

新員工  褖畫惎會訂閱訊息的組織中的新員工邏輯應用程式。最後會傳送歡迎電子郵件到新的員工。

新的員工設備順序是 Azure 函式會訂閱事件的新員工,工程部門。它接著會建立一則訊息以便進行其他處理佇列中。

員工記錄是在員工離開組織時,接收訊息,會公開 Web API 的 ASP.NET Core 建置自訂網站。

建立自訂主題

若要開始,我會需要在 Azure 中建立幾個基本的資源。您可以啟動 Azure 雲端命令介面,從入口網站,或在本機上使用命令列介面 (CLI)。如何使用雲端殼層的詳細資訊,請參閱bit.ly/2CsFtQB。如果您未使用雲端殼層之前,強烈建議它。

首先要做為建立資源群組來管理及封裝的 Azure 資源:

az group create --name <resource-group-name> --location <location>

建立群組之後,會佈建事件方格主題。這會提供將自訂事件 HR 應用程式從發行端點。主題的名稱必須是唯一區域,因為它會在 Azure 上的可公開存取的服務。位置也必須有可用的事件方格服務所在的地區。我通常搭配 westus2 位置或,請參閱每個 Azure 區域中提供的服務清單 (請參閱bit.ly/2DU15ln)。

az eventgrid topic create --name <topic-name> \
  --location <location> \
  --resource-group <resource group name>

在執行之後要建立本主題的命令,將會傳回資源的相關詳細資料。輸出會看起來類似,但不完全類似的程式碼:

{
  "endpoint": "https://<topic name>.westus2-1.eventgrid.azure.net/api/events",
  "id": "/subscriptions/xxxx-xxx-xx-xxx-xx/resourceGroups/eventgridsolution-rg/providers/Microsoft.EventGrid/topics/<topic name>",
  "location": "westus2",
  "name": "<topic name>",
  "provisioningState": "Succeeded",
  "resourceGroup": "eventgridsolution-rg",
  "tags": null,
  "type": "Microsoft.EventGrid/topics"
}

請記下的 [端點值,它將在日後發佈事件時。您還需要其中一個所產生的兩個存取金鑰進行授權。若要擷取索引鍵,您可以列出與主題相關聯。您可以和應該循環,並重新產生這些金鑰基於安全性考量,就像 Azure 上的其他服務。

az eventgrid topic key list --name <topic-name> --resource-group <resource-group-name>

如果您的喜好設定是在 Azure 入口網站中工作,您可以建立和檢視所有這些選項和設定,以及。

發行事件

在傳送之前的第一個事件,您需要了解本主題所預期的事件結構描述。下列程式碼中所述的結構,每個事件,不論 「 發行者 」 的 Azure 資源或自訂的應用程式,是否會遵守 (很有幫助參考事件結構描述,以及一些範例,請參閱bit.ly/2CG8oxI):

[
  {
    "topic": string,
    "subject": string,   
    "id": string,
    "eventType": string,
    "eventTime": string,
    "data":{
      object-unique-to-each-publisher
    }
  }
]

指出第一件事是事件會傳送一個陣列中。這是刻意為了提供傳送要求內的多個事件的能力。事件可以傳送批次中可同時支援案例減少網路多話其中連線不一定可用。

我想要發行的第一個事件時,為新員工聯結組織。此事件的承載可能類似的內容圖 5

圖 5 位員工新增的事件

[{
  "id": "30934",
  "eventType": "employeeAdded",
  "subject": "department/engineering",
  "eventTime": "2017-12-14T10:10:20+00:00",
  "data":{
    "employeeId": "14",
    "employeeName": "Nigel Tufnel",
    "employeeEmail": "nigel@contoso.com",
    "manager": "itmanager@contoso.com",
    "managerId": "4"
  }
}]

在本文稍後我將使用相同的結構,但在做為當員工離職的值,有些差異。這個事件中的索引鍵屬性如下所示:

eventType 是用來唯一識別已發行的事件類型的值。這個屬性可供想要訂閱特定事件類型,而不是所有類型的處理常式。

主旨是值,例如 eventType 所提供的 「 訂閱者 」 提供額外的篩選選項提供有關事件的其他內容。我將可以利用 eventType 和主旨推出時我建立訂用帳戶。主旨和 eventType 提供事件內容。

發行者定義值區只可包含一或多個屬性是物件的資料。「 發行者 」 指派事件本身內這個屬性的相關資訊。例如,Azure Blob 儲存體事件包含有關建立或刪除 blob,例如 URL 和內容類型的詳細資料。

若要發行的事件,我使用郵差 (或類似工具) 來模擬來自 HR 應用程式,以先前所述的端點位址的訊息。我可以進行授權,將項目來呼叫 aeg sas 金鑰的標頭中,它的值是其中一個主題建立時產生存取金鑰。要求的主體會包含所述的裝載圖 5

沒有任何訂閱者,因為沒有可以尚未發現的任何項目。下一個步驟是查看此作業如何藉由建立發送到事件的事件方格的幾個事件處理常式。

處理事件搭配 Azure 函式

接下來屬於訂閱事件。我們第一個處理常式會是 Azure 函式。若要深入了解建立函式的基本概念,請參閱bit.ly/2A6pFgu。這種情況下,我想要特別為最近加入的員工訂閱事件。此外,而且只為重要的這個處理常式必須只叫用隸屬於 theengineering 部門的員工。

大部分的範例逐步解說使用 Azure 入口網站的函式建立,這是超級方便而快速。我想要示範如何從 Visual Studio 在本機,完成此動作。這將 pave 更多可實際執行的程式碼的方式。我也會使用稱為 ngrok 的公用程式 (請參閱ngrok.com) 以支援本機偵錯工具事件方格。

如果您想要跟著做,您將需要 ngrok,以及最新版的 Visual Studio (我用於版本 15.5.2 本文)。讓我們開始建立新的專案,然後選取 Azure 函式,從雲端的範本。在 [新增專案] 對話方塊選取 HTTP 觸發程序選項,然後保留預設值。

更新以反映內容都會在函式的程式碼圖 6。放心地重新命名檔案,以反映函式的名稱。

圖 6 實作新的員工事件處理常式 >

using System;
using System.Collections.Generic;
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 Newtonsoft.Json;
namespace NewEmployeeApp
{
  public static class NewEmployeeHandler
  {
    public class GridEvent<T> where T : class
    {
      public string Id { get; set; }
      public string EventType { get; set; }
      public string Subject { get; set; }
      public DateTime EventTime { get; set; }
      public T Data { get; set; }
      public string Topic { get; set; }
    }
      [FunctionName("newemployeehandler")]
      public static async Task<HttpResponseMessage> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
        HttpRequestMessage req,
        TraceWriter log)
         {
          log.Info("New Employee Handler Triggered");
          // Retrieve the contents of the request and
          // deserialize it into a grid event object.
          var jsonContent = await req.Content.ReadAsStringAsync();
          var gridEvent =
            JsonConvert.DeserializeObject<List<GridEvent<Dictionary<string,
              string>>>>(jsonContent)
              ?.SingleOrDefault();
            // Check to see if the event is available and
            // return an error response if its missing.
            if (gridEvent == null)
            {
              return req.CreateErrorResponse(HttpStatusCode.BadRequest,
                $@"Missing event details");
            }
          // Check the header to identify the type of request
          // from Event Grid. A subscription validation request
          // must echo back the validation code.
          var gridEventType = req.Headers.GetValues("Aeg-Event-Type"). 
            FirstOrDefault();
          if (gridEventType == "SubscriptionValidation")
          {
            var code = gridEvent.Data["validationCode"];
            return req.CreateResponse(HttpStatusCode.OK,
              new { validationResponse = code });
          }
          else if (gridEventType == "Notification")
          {
            // Pseudo code: place message into a queue
            // for further processing.
            return req.CreateResponse(HttpStatusCode.OK);
          }
          else
          {
            return req.CreateErrorResponse(HttpStatusCode.BadRequest,
              $@"Unknown request type");
          }
        }
  }
}

還有一些重要的程式碼,來檢閱以下。一開始的類別稱為 GridEvent 要反映的裝載和從事件方格事件結構描述。在理想情況下,我會將此類別放在通用程式庫,以便之後可以重複使用。此範例中,它用於強類型物件還原序列化要求的內容。

事件方格會將傳送至其兩個 「 訂閱者 」 類型的要求 — SubscriptionValidation 和通知,您可以藉由檢查標頭中的值進行識別。驗證要求,請務必確保所有訂閱者會明確地加入。我必須在此處只會回應以確認我可以接收訊息的驗證程式碼:

var code = gridEvent.Data["validationCode"];
return req.CreateResponse(HttpStatusCode.OK,
  new { validationResponse = code });

也可以依其事件類型識別驗證要求:Microsoft.EventGrid.SubscriptionValidationEvent.事件類型是通知,如果我繼續進行的商務邏輯的實作。公開至其他服務端點時,強烈建議您使用此防禦的程式設計方法。

裝載在 Azure 中,以及使用 azurewebsites.net 網域時,所參考的函式不需要訂用帳戶的驗證邏輯。相反地,它們是由事件方格的白名單,以及數個其他服務設定,例如邏輯應用程式和來自 Azure 自動化執行書籍的回呼。因為我打算在本機測試,我需要回應的驗證程式碼事件方格,以確認函式做為有效的端點。

最後,事件方格執行階段 SDK 會處理大部分的此設定,從還原序列化事件,並正在建立強類型事件方格的物件,若要自動驗證端點。撰寫本文時,更新的執行階段 Sdk 未尚未提供。

測試區域函式

使其在本機連接埠 7071 上執行,讓我們開始從 Visual Studio 函式。一旦執行,開啟命令提示字元,並使用 ngrok 建立安全通道:

ngrok http -host-header=localhost 7071

我會收到回 HTTPS 位址 ngrok 来做為訂閱者端點。地址應看起來像 https://d69f6bed.ngrok.io,但不同的子網域與每次 ngrok 執行命令。附加我們的函式的路由 URL,讓它類似 https://<generated-value>.ngrok.io/api/newemployeehandler 類似。這會是事件訂閱的端點位址。

函式執行和就地安全的通道,我現在可以建立事件訂閱從 CLI 或 Azure 雲端殼層:

az eventgrid event-subscription create --name <event-subscription-name> \
  --resource-group <resource group name> \
  --topic-name <topic name> \
  --subject-ends-with engineering \
  --included-event-type employeeAdded \
  --endpoint <function endpoint>

我可以選擇新增事件訂閱從入口網站的對話方塊中,填寫中所示圖 7

從入口網站建立事件訂閱
圖 7 從入口網站建立事件訂閱

我想要呼叫的事件訂閱建立數個重要的引數。

主旨開始與 (前置詞篩選條件) 是一個選擇性引數,它會篩選根據事件中的 [主旨] 欄位的前置詞。它是常值字串的相符項目。不支援萬用字元和規則運算式。

主體結尾有 (後置詞篩選條件) 是根據篩選的事件後置詞的選擇性引數。不支援萬用字元和規則運算式。

包含事件的類型 (事件類型) 是選擇性的訂閱事件類型清單。每個型別是以空格分隔。

現在可以傳回發行事件範例稍早在文件,以確保,事件會透過從流向郵差,事件方格中,最後至本機的函式。可自由變更驗證篩選條件的要求中的值按預期方式。

處理事件:邏輯應用程式和 WebHook

下一個事件訂閱是邏輯應用程式。就像 Azure 函式範例,它只想要加入的 employee 事件類型。因為我想要從所有部門將訊息傳送給員工,它將不會利用前置或後置篩選。邏輯應用程式的完整的版示圖 8

歡迎新員工邏輯應用程式
歡迎新員工的圖 8 A 邏輯應用程式

邏輯應用程式的事件方格觸發程序的開頭。選取 Microsoft.EventGrid.topics 與資源類型,可讓我從自訂主題訂用帳戶中挑選。

剖析 JSON 動作將會有幫助存取中的資料物件的屬性。我將使用這個範例裝載產生結構描述:

{
  "id": "40000",
  "eventType": "employeeAdded",
  "subject": "department/finance",
  "eventTime": "2017-12-20T10:10:20+00:00",
  "data":{
    "employeeId": "24",
    "employeeName": "David St. Hubbins",
    "employeeEmail": "david@contoso.com",
    "manager": "finance@contoso.com",
    "managerId": "10"
  }
}

接下來,必須完成的事件類型的篩選條件使用的條件動作。這是稍微離開事件訂閱的建立方式與函式因為沒有選取此選項的事件方格觸發程序中的方法。

最後一個步驟會傳送電子郵件給員工。它會使用從要填入的收件者地址和電子郵件的主旨欄位的第二個步驟所擷取的屬性。若要測試邏輯應用程式,請按一下 [執行],從設計工具並將訊息傳送至像之前的端點。

基本 HTTP 回呼或 WebHook 最後一個事件訂閱。我會更新現有的 ASP.NET Core 應用程式和 Web API 的內送事件。WebHook 的程式碼會非常類似於先前所撰寫的 Azure 函式。有些微的差異包括標頭值會擷取用來檢查要求的類型,如中所示的方式圖 9

圖 9 Web API 控制器接收事件

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace EmployeeRecords.Controllers
{
  public class GridEvent<T> where T : class
  {
    public string Id { get; set; }
    public string Subject { get; set; }
    public string EventType { get; set; }
    public T Data { get; set; }
    public DateTime EventTime { get; set; }
  }
  [Produces("application/json")]
  [Route("api/EmployeeUpdates")]
  public class EmployeeUpdatesController : Controller
  {
    private bool EventTypeSubcriptionValidation
      => HttpContext.Request.Headers["aeg-event-type"].FirstOrDefault() ==
        "SubscriptionValidation";
    private bool EventTypeNotification
      => HttpContext.Request.Headers["aeg-event-type"].FirstOrDefault() ==
        "Notification";
    [HttpPost]
    public async Task<HttpResponseMessage> Post()
    {
      using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
      {
        var jsonContent = await reader.ReadToEndAsync();
        var gridEvent =
          JsonConvert.DeserializeObject<List<GridEvent<Dictionary<string,
          string>>>>(jsonContent)
            .SingleOrDefault();
        if (gridEvent == null)
        {
          return new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest};
        }
        // Check the event type from Event Grid.
        if (EventTypeSubcriptionValidation)
        {
          // Retrieve the validation code and echo back.
          var validationCode = gridEvent.Data["validationCode"];
          var validationResponse =
            JsonConvert.SerializeObject(new { validationResponse =
            validationCode });
          return new HttpResponseMessage
          {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(validationResponse)
          };
        }
        else if (EventTypeNotification)
        {
          // Pseudo code: Update records
          return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
        }
        else
        {
          return new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest };
        }
      }
    }
  }
}

在建立事件訂閱時,登錄的事件類型應該是 employeeRemoved。這項變更可滿足需求,此處理常式只想要移除從組織的員工接收訊息。也請注意,因為訂閱者想要收到每個項目,不論部門,會使用前置詞和後置詞都不篩選:

az eventgrid event-subscription create --name <event-subscription-name> \
  --resource-group <resource group name> \
  --topic-name <topic name> \
  --included-event-type employeeRemoved \
  --endpoint <function endpoint>

最後,請記住,事件訂閱的端點必須是安全。如果您參考應用程式服務在 Azure 上,您必須指定 HTTPS 位址,或加入訂用帳戶將會失敗。

總結

Azure 事件方格是真正的遊戲變更服務。在本文中,我解決常見的應用程式整合案例。事件方格當做啟用技術,例如 Azure 函式、 邏輯應用程式和甚至自訂的 WebHook 可以位於任何位置的其他服務應用程式連接。輔以無伺服器的應用程式,當事件方格固然,因為這兩個同時運用的極大的小數位數和 Azure 支援的整合功能。這篇文章中的程式碼,請參閱github.com/dbarkol/AzureEventGrid


David Barkol是全域的黑色輸送帶小組的 microsoft Azure 專家。在 Twitter 上連絡他: @dbarkol或透過電子郵件在dabarkol@microsoft.com

非常感謝下列 Microsoft 技術專家檢閱這篇文章:Bahram Banisadr 和 Dan Rosanovsanova
Dan Rosanova 是負責 Azure 訊息一套產品包括服務匯流排、 事件中心、 Azure 轉送和事件方格原則程式管理員負責人。
 
Bahram Banisadr 是負責處理來建立 Azure 服務連線這個時候的 Azure 事件方格 PM。


MSDN Magazine 論壇中的這篇文章的討論