本文章是由機器翻譯。

預測:雲端多雲

將雲端與 MEF 分開

Joseph Fultz
Chris Mabry

下載代碼示例

Joseph Fultz
一位同事,並一直對一個專案在過去的幾個月,他們利用 Microsoft 可擴充性框架 (城域)。在本文中,我們將看看如何使用城域,使雲部署有點更易於管理和靈活。城域 — — 和統等類似的框架 — — 是軟體結構,使開發人員能夠從管理依賴項解析、 創建物件和具現化。現在又一次您可能會發現自己寫一個工廠方法或創建依賴物件內部建構函式或方法所需的初始化,但大部分這類工作是由於框架如城域不再需要。

通過使用城域我們聯同 StorageClient API 的部署中,我們可以部署和可用的新類無需回收或重新部署我們的 Web 角色。此外,我們可以將類型的更新的版本部署到不完整的重新部署雲,只是轉而回收應用程式。請注意雖然我們使用城域在這裡,以下類似的結構使用團結、 溫莎城堡、 StructureMap 或任何其他類似容器應網相同的結果,被語法和類型註冊語義的主要差異。

設計和部署

俗話說得好:若要獲取出多一點,你必須放在多一點。在這種情況下,需要某些建築標準和周圍部署一些額外的工作。首先,如果你習慣使用依賴注入 (DI) 或構成容器,機會是你非常熱衷於保留執行和在您的代碼中分離的介面。我們不要偏離這一目標在這裡 — — 我們的所有具體類實現有追溯到一個介面類別型的繼承。這並不意味著每個類將直接繼承的介面,但類一般會遵循一種模式介面 “ 虛擬 “ 混凝土一樣的抽象的圖層。

圖 1 表明,不只是我很感興趣的主要類有這樣一個鏈,但事實上其所需的屬性之一也抽象。所有的抽象容易更換部件或添加額外的功能中的一個新的庫,出口 (在本例中,介面) 所需的合同形式。超越組成,被嚴格有關提取您的類設計好副作用是它更好地使測試通過 mocked 介面。

Class Diagram
圖 1 類別圖表

要求的困難部分是為應用程式的部署模型的變化。因為我們希望建立我們的目錄的進口和出口執行時間,並刷新它而無需重新部署,我們要部署的二進位檔案保存在 Web 角色部署之外我們具體的類。這也迫使一點額外的工作,為在啟動時應用程式。圖 2 描繪 Global.asax 啟動工作,它要求到我們已經創建了名為 MEFCoNtext 説明器類。

Building the Catalog at Startup
圖 2 構建在啟動目錄

運行庫組成

因為我們要從存儲中的檔被載入編錄,我們不得不進入我們雲存儲容器的那些檔。因此,獲取檔到 Windows Azure 存儲位置需要成為部署過程的一部分。這是大概最容易做的是使用 Windows Azure PowerShell Cmdlet (wappowershell.codeplex.com) 以及一些後期生成步驟。對於我們而言,我們會手動移動的二進位檔案,使用 Windows Azure 存儲資源管理器 (azurestorageexplorer.codeplex.com)。

我們創建了一個包含一個常見的診斷類、 客戶實體和幾個規則庫專案。所有的規則庫必須從繼承和匯出介面類別型 IBusinessRule <t>,其中 t 表示對其強制執行規則的實體。這裡有一條規則的類聲明的導入部分:

 

[Export(typeof(IBusinessRule<ICustomer>))]
public class CustomerNameRule : IBusinessRule<ICustomer>
{
  [Import(typeof(IDiagnostics))]
  IDiagnostics _diagnostics;
    ...
}

出口,以及診斷程式依賴項的城域將注入來說,當我們問為規則的物件時,您可以看到。 它是重要的是要知道你解決你想要的實例,該合約的反過來被匯出為那會是什麼。 Microsoft.net 框架 4.5 將會允許一些目前圍繞該容器中的泛型約束的放鬆的城域給帶來一些增強功能。 例如,目前可以註冊和檢索東西如 IBusinessRule <ICustomer>,但不是像 IBusiness 規則 <t>。 有時,您希望僅限於其實際範本類型的類型的所有實例。 目前,最簡單的方法來做到這一點是要註冊是商定的公約 》,在您的專案或解決方案中的字串合同名稱。 對於我們的示例中,將工作像前面的聲明。

我們有兩個規則,一個電話號碼和一個名稱,用於診斷程式庫中,其中每個將通過城域容器提供。 我們要做的第一件事是抓取 Windows Azure 存儲庫,並把他們帶到一個本地資源 (本地目錄) 所以我們可以使用 DirectoryCatalog 載入它們。 要做到這一點,我們在 Global.asax 的 Application_Start 中包括函式呼叫幾個:

// Store the local directory for later use (directory catalog)
MEFContext.CacheFolderPath = 
  RoleEnvironment.GetLocalResource("ResourceCache").RootPath.ToLower();
MEFContext.InitializeContainer();

我們只抓住所需的資源路徑,它被配置為 Web 角色的一部分,,然後調用此方法來設置容器。 該初始化方法反過來調用 UpdateFromStorage 以獲取的檔和 BuildContainer 來創建目錄,然後在城域容器。

UpdateFromStorage 方法在一個預定的容器中查找和迴圈的容器,他們每個人都下載到本地資源資料夾中的檔。 此方法的第一部分所示圖 3

圖 3 上半葉 UpdateFromStorage

// Could also pull from config, etc.
string containerName = CONTAINER_NAME;
// Using development storage account
CloudStorageAccount storageAccount = 
  CloudStorageAccount.DevelopmentStorageAccount;
// Create the blob client and use it to create the container object
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Note that here is where the container name is passed
// in order to get to the files we want
CloudBlobContainer blobContainer = new CloudBlobContainer(
  storageAccount.BlobEndpoint.ToString() + 
  "/" + containerName,
  blobClient);
// Create the options needed to get the blob list
BlobRequestOptions options = new BlobRequestOptions();
options.AccessCondition = AccessCondition.None;
options.BlobListingDetails = BlobListingDetails.All;
options.UseFlatBlobListing = true;
options.Timeout = new TimeSpan(0, 1, 0);

今年上半年我們成立了存儲用戶端獲取我們的需要。 對於這種情況下,我們要求,無論有沒有。 在哪裡你正在降低檔從存儲到本地資源的情況下,它可能值得做一輪完整,無所不包。 為更多的焦油增高提取的檔時,您可以指定一些 IfMatch 條件的選項。AccessCondition 屬性。 這就需要對 blob 上載時設置的 etags。 此外,您可以優化改造城域貨櫃儲存的上次更新時間並應用 IfModifiedSince AccessCondition 的更新側。

圖 4 UpdateFromStorage 的一半顯示第二個。

圖 4 下半葉 UpdateFromStorage

// Iterate over the collect
// Grab the files and save them locally
foreach (IListBlobItem item in blobs)
{
  string fileAbsPath = item.Uri.AbsolutePath.ToLower();
  // Just want the file name ...
  fileAbsPath = 
    fileAbsPath.Substring(fileAbsPath.LastIndexOf('/') + 1);
  try
  {
    Microsoft.WindowsAzure.StorageClient.CloudPageBlob pageblob =
      new CloudPageBlob(item.Uri.ToString());
    pageblob.DownloadToFile(MEFContext.CacheFolderPath + fileAbsPath, 
      options);
  }
  catch (Exception)
  {
    // Ignore exceptions, if we can't write it's because
    // we've already got the file, move on
    }
}

一旦準備就緒,存儲用戶端時,我們只是反覆運算的 blob 專案,並將它們下載到該資源。 根據條件和目標的總體下載,無法複製在此操作中的本地資料夾結構,或生成基於公約 》 的資料夾結構。 有時一個資料夾結構是一項要求,以避免名稱衝突。 我們只會用鳥槍法和抓取的所有檔,因為我們知道它是此示例只是兩個或三個 Dll 在一個地方貼。

這,我們在地方有檔,只是需要建立在容器。 在城域組成的容器是從一個或多個目錄生成的。 在這種情況下,我們將使用 DirectoryCatalog,因為這使它容易簡單地指向目錄的目錄並載入可用的二進位檔案。 因此,要註冊類型,並準備在容器的代碼是短和簡單:

// Store the container for later use (resolve type instances)
var catalog = new DirectoryCatalog(CacheFolderPath);
MEFContainer = new CompositionContainer(catalog);
MEFContainer.ComposeParts();

現在我們就會運行該網站,如中所示,我們應看到在容器中,可用的類型的轉儲圖 5

Initial Exports
圖 5 初始出口

我們不傾倒在這裡,整個容器但而專門為 IDiagnostics 介面,然後所有類型 IBusinessRule <ICustomer> 的出口要求。 正如您所看到的我們擁有一支的前將一個新的商務規則庫上載到的存儲容器,這些每個。

我們已經將 NewRules.dll 放入存儲的位置,現在需要得到它載入到應用程式。 理想情況下,您想要通過做一點點的檔的存儲容器上觀看觸發容器重建。 再次,這是輕鬆完成使用 IfModifiedSince AccessCondition 快速輪詢。 不過,我們已經選擇了更多手工操作過程的更新目錄點擊我們的測試應用程式。 結果如 [圖 8] 所示。

Updated Rules Exports
圖 8 更新規則出口

我們只是重複步驟以創建目錄和初始化該容器,和現在我們有一個新的規則庫,以強制執行。 請注意我們還沒有重新開機應用程式或重新調配,但我們有新的環境中運行的代碼。 唯一的鬆散結束在這裡是需要一些同步方法,因為我們不能試圖使用組成的容器,而我們要更換該引用的代碼:

var catalog = new DirectoryCatalog(CacheFolderPath);
CompositionContainer newContainer = 
  new CompositionContainer(catalog);
newContainer.ComposeParts();
lock(MEFContainer)
{
  MEFContainer = newContainer;
}

建設一個輔助容器,然後只替換引用的主要原因是要減少鎖量子並返回容器馬上使用。

要進一步發展基本代碼下, 一步將會貫徹您自己的自訂目錄類型 — — 例如,AzureStorageCatalog,如中所示圖 9。 不幸的是,當前的物件模型沒有正確的介面或定義,可方便地重用基地,所以使用繼承以及一些封裝的一點可能是最好的選擇。 實現一個類似于 AzureStorageCatalog 的類上市將使一個簡單的模型的具現化的自訂目錄和直接在組合容器中使用它。

圖 9 AzureStorageCatalog

public class AzureStorageCatalog:ComposablePartCatalog
{
  private string _localCatalogDirectory = default(string);
  private DirectoryCatalog _directoryCatalog = 
    default(DirectoryCatalog);
  AzureStorageCatalog(string StorageSetting, string ContainerName)
    :base()
  {
    // Pull the files to the local directory
    _localCatalogDirectory = 
      GetStorageCatalog(StorageSetting, ContainerName);
    // Load the exports using an encapsulated DirectoryCatalog
    _directoryCatalog = new DirectoryCatalog(_localCatalogDirectory);
  }
  // Return encapsulated parts
  public override IQueryable<ComposablePartDefinition> Parts
  {
    get { return _directoryCatalog.Parts; }
  }
  private string GetStorageCatalog(string StorageSetting, 
    string ContainerName)
  {  }
}

 

更新現有的功能

添加新的功能,對我們的部署是相當容易,但我們還沒有對更新現有的功能或圖書館,同樣的好消息。雖然過程是比完整的重新部署,它仍相當涉及因為我們要移動的檔存儲和相關的 Web 角色到必須更新其本地資源資料夾。不過,我們會也回收角色,因為我們需要卸載並重新載入的應用程式域來刷新存儲在該容器中的類型定義。即使您將根據容器和類型載入到輔助應用程式域,並嘗試從那裡載入,在其中您正在請求類型的應用程式域將載入它從以前載入中繼資料。圍繞這我們可以看到的唯一方法是將實體發送到輔助應用程式域,並添加一些自訂封送處理,而不是在主應用程式域上使用的匯出的類型。這種模式似乎有問題給我們 ; 雙 AppDomain 本身似乎有問題。因此,一個更簡單的解決方案是回收角色後,新的二進位檔案都可用。

有一些關於 Windows Azure 更新域的好消息。看一看我 2 月 2012年的列中,"Windows Azure 部署域"(msdn.microsoft.com/magazine/hh781019),其中描述行走的更新域和每年重新開機實例。積極的一面,該網站就熬夜無需完全重新調配。但是,可能可以刷新過程中遇到兩個不同的行為。不過,這是可接受的風險,因為同樣會真正滾動更新過程中如果你做了全面部署。

您可以配置此發生內部署,但問題是協調之一。若要執行此操作會需要實例的重新開機進行協調,因此實例會要麼需要選出一個領導者或有一些投票制度。而不是將 Web 角色寫入一些人工智慧,我們覺得由監視過程和前面引用的 Windows Azure Cmdlet 更輕鬆地處理任務。

有很多原因,使用一個架構,例如城域,超出了我們在這兒已經突出顯示的功能的窄位。我們想要強調的是框架的,通過使用 Windows Azure 組合中的固有功能具有組成/迪/演的控制項類型,您可以創建一個動態雲應用程式,可以輕鬆地回應似乎總是彈出的最後一分鐘更改。

Joseph Fuitz 是在惠普公司,作為 HP.com 全球 IT 組的一部分工作的軟體設計師。他以前是微軟,使用其頂級企業和 ISV 客戶定義的體系結構和設計解決方案的軟體設計師。

Chris Mabry 是惠普公司在開發人員主管當前重點是領導一個團隊,以提供一個豐富的 UI 體驗基於服務啟用用戶端框架。

由於下面的技術專家,檢討這篇文章:克裡斯 · 布魯克斯