基于云存儲實(shí)現(xiàn)用Windows Azure Storage增_第1頁
基于云存儲實(shí)現(xiàn)用Windows Azure Storage增_第2頁
基于云存儲實(shí)現(xiàn)用Windows Azure Storage增_第3頁
基于云存儲實(shí)現(xiàn)用Windows Azure Storage增_第4頁
基于云存儲實(shí)現(xiàn)用Windows Azure Storage增_第5頁
已閱讀5頁,還剩6頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

基于云存儲實(shí)現(xiàn)用WindowsAzureStorage增基于云存儲實(shí)現(xiàn)用WindowsAzureStorage增強(qiáng)應(yīng)用程序的引擎概述:您可以在云中運(yùn)行后臺進(jìn)程。KevinHoffman和NateDudek使用購物車示例演示如何構(gòu)建應(yīng)用程序引擎,以及如何使用AzureStorage實(shí)現(xiàn)異步消息傳送和處理。開發(fā)人員容易依賴其有形的、可感知的基礎(chǔ)結(jié)構(gòu),就像其是“安全毛毯”。他們知道如何使用、如何操作,如果出現(xiàn)問題,他們知道問題出在哪兒。而這通常會成為開發(fā)人員采用更新的技術(shù)(例如云計算)的障礙。心存懷疑的開發(fā)人員提出的最大疑問之一是他們?nèi)绾卧谠浦欣^續(xù)運(yùn)行后臺進(jìn)程,即他們的引擎如何繼續(xù)運(yùn)行。本文旨在通過向您演示如何構(gòu)建應(yīng)用程序引擎以及使用WindowsAzureStorage實(shí)現(xiàn)異步消息傳送和處理來為您揭開云中缺乏后臺處理的神秘面紗。為了證明開發(fā)人員可以拋開其有形的基礎(chǔ)結(jié)構(gòu)這條“安全毛毯”并將其應(yīng)用程序引擎置于云中,我們將介紹如何實(shí)現(xiàn)電子商務(wù)應(yīng)用程序的一個小型子集HollywoodHackers,您可以從中購買到Hollywood用于完全忽略物理法則和過時的常識的所有神奇技術(shù)。我們將介紹的兩個主要方案如下:將異步文本消息(“toasts”)發(fā)送給使用該應(yīng)用程序的用戶,以通知他們發(fā)生的重要事件(如已提交他們的購物車)或在員工之間發(fā)送消息。此方案使用WindowsAzureQueue、WindowsAzureTable和WindowsAzure工作者角色。此方案使用WindowsAzureQueue和WindowsAzure工作者角色將購物車提交給執(zhí)行引擎。使用隊(duì)列存儲進(jìn)行內(nèi)部應(yīng)用程序消息傳送在介紹具體的方案之前,我們需要先介紹一些有關(guān)WindowsAzureQueue的基礎(chǔ)知識。云中的隊(duì)列與傳統(tǒng)的.NET應(yīng)用程序中的隊(duì)列的運(yùn)行方式不太一樣。在處理AppDomain中的數(shù)據(jù)時,您知道該數(shù)據(jù)只有一份,它完整地位于單一托管進(jìn)程中。而在云中,您的一部分?jǐn)?shù)據(jù)可能在加利福尼亞,而另一部分可能在紐約,并且您可能會安排一個工作者角色在德克薩斯州對該數(shù)據(jù)進(jìn)行處理,而另一工作者角色在北達(dá)科他州進(jìn)行數(shù)據(jù)處理。很多開發(fā)人員在適應(yīng)這種分布式計算和分布式數(shù)據(jù)時面臨著一些不熟悉的問題,例如對可能出現(xiàn)的故障進(jìn)行編碼、針對數(shù)據(jù)提交形成多次重試的概念甚至冪等性的理念。只要您不將WindowsAzureQueue視為進(jìn)程內(nèi)的常規(guī)CLR隊(duì)列,其工作方式其實(shí)非常簡單。首先,應(yīng)用程序?qū)⑾蜿?duì)列獲取一些數(shù)量的消息(需要記住,一次不會超過20條)并提供一個超時。此超時控制對其他隊(duì)列處理客戶端隱藏這些消息的時間。當(dāng)應(yīng)用程序成功完成需要對隊(duì)列消息進(jìn)行的所有處理后,將刪除該消息。如果應(yīng)用程序引發(fā)異?;蛱幚黻?duì)列消息失敗,則在超時期限過后,其他客戶端可以再次看到該消息。因此,當(dāng)一個工作者角色處理失敗后,其他工作者角色可以繼續(xù)進(jìn)行處理。向隊(duì)列提交消息非常簡單:應(yīng)用程序直接或借助客戶端庫形成適當(dāng)?shù)腍TTPPOST消息,然后提交字符串或字節(jié)數(shù)組。隊(duì)列專門用于進(jìn)行內(nèi)部應(yīng)用程序消息傳送和非永久存儲,因此這些消息占用的空間需要相當(dāng)小。如上所述,您可能安排多個工作者角色都嘗試處理同一消息。雖然隱藏當(dāng)前正在處理的消息的不可見超時很有幫助,但不能確保萬無一失。要完全避免沖突,您應(yīng)該將您的引擎處理設(shè)計為冪等。換句話說,同一隊(duì)列消息應(yīng)該可以由一個或多個工作者角色處理多次,而不會使應(yīng)用程序處于不一致的狀態(tài)。理想情況下,您希望工作者角色可以檢測出是否已完成對給定消息的處理。在編寫工作者角色來處理隊(duì)列消息時,請牢記您的代碼可能會嘗試處理已處理過的消息,盡管這個可能性微乎其微。圖1中的代碼段顯示了如何使用隨WindowsAzureSDK一起提供的StorageClient程序集創(chuàng)建消息并將其提交給WindowsAzureQueue。StorageClient庫實(shí)際上只是WindowsAzureStorageHTTP接口周圍的包裝。圖1創(chuàng)建消息并將其提交給WindowsAzureQueuestringaccountName;

stringaccountSharedKey;

stringqueueBaseUri;

stringStorageCredentialsAccountAndKeycredentials;

if(RoleEnvironment.IsAvailable)

{

//Wearerunninginacloud-INCLUDINGLOCAL!

accountName=

RoleEnvironment.GetConfigurationSettingValue("AccountName");

accountSharedKey=

RoleEnvironment.GetConfigurationSettingValue("AccountSharedKey");

queueBaseUri=RoleEnvironment.GetConfigurationSettingValue

("QueueStorageEndpoint");

}

else

{

accountName=ConfigurationManager.AppSettings["AccountName"];

accountSharedKey=

ConfigurationManager.AppSettings["AccountSharedKey"];

queueBaseUri=

ConfigurationManager.AppSettings["QueueStorageEndpoint"];

}

credentials=

newStorageCredentialsAccountAndKey(accountName,accountSharedKey);

CloudQueueClientclient=

newCloudQueueClient(queueBaseUri,credentials);

CloudQueuequeue=client.GetQueueReference(queueName);

CloudQueueMessagem=newCloudQueueMessage(

/*stringorbyte[]representingmessagetoenqueue*/);

Queue.AddMessage(m);對于本文中的其他示例,我們使用了可以簡化此過程的一些包裝類(位于HollywoodHackers的CodePlex站點(diǎn)中,網(wǎng)址為:hollywoodhackers.codeplex/SourceControl/ListDownloadableCommits.aspx)。異步消息傳送(Toast)當(dāng)今,交互式網(wǎng)站不僅是一種時尚,更是一種需求。用戶已經(jīng)習(xí)慣了完全交互式網(wǎng)站,以致于當(dāng)他們遇到一個靜態(tài)的非交互式頁面時會認(rèn)為什么地方出問題了??紤]到這一點(diǎn),我們希望可以在我們的用戶使用這樣的站點(diǎn)時向他們發(fā)送通知。為此,我們將利用WindowsAzureQueue和WindowsAzureTable存儲機(jī)制構(gòu)建一個消息傳遞框架??蛻舳藢⑹褂门cjQueryGritter插件結(jié)合的jQuery在用戶的瀏覽器中將通知顯示為一個toast,類似于當(dāng)您收到新的Outlook電子郵件、即時消息或警報聲時在Windows系統(tǒng)托盤上方淡出的消息。當(dāng)需要向某個用戶發(fā)送通知時,該用戶將被插入到隊(duì)列中。因?yàn)楣ぷ髡呓巧?fù)責(zé)處理隊(duì)列中的每個項(xiàng)目,所以該角色將動態(tài)確定如何處理每個項(xiàng)目。在本例中,引擎只需要執(zhí)行一個操作,但在復(fù)雜的CRM網(wǎng)站或支持站點(diǎn)中,要執(zhí)行的操作可能不計其數(shù)。工作者角色在隊(duì)列中遇到用戶通知時,會將該通知存儲在表存儲中并將其從隊(duì)列中刪除。這樣,消息可以保留很長時間并等待用戶登錄進(jìn)行處理。隊(duì)列存儲中的消息的最大保存期限比較短,不會超過幾天。當(dāng)用戶訪問網(wǎng)站時,我們的jQuery腳本將異步獲取表中的所有消息,并通過在控制器上調(diào)用可返回JavaScriptObjectNotation(JSON)的方法在瀏覽器中以常見的形式顯示這些消息。盡管隊(duì)列只處理字符串或字節(jié)數(shù)組,但我們可以通過將任何類型的結(jié)構(gòu)化數(shù)據(jù)序列化為二進(jìn)制文件來將其存儲在隊(duì)列中,然后在我們需要使用時再將其轉(zhuǎn)換回來。這成為將強(qiáng)類型化的對象傳遞到隊(duì)列中的出色技術(shù)。我們會將此技術(shù)構(gòu)建到我們的隊(duì)列消息的基類中。然后,我們的系統(tǒng)消息類可以包含我們的數(shù)據(jù),而且可以將整個對象提交到隊(duì)列中并根據(jù)需要進(jìn)行利用(請參見圖2)。圖2在隊(duì)列中存儲結(jié)構(gòu)化數(shù)據(jù)namespaceHollywoodHackers.Storage.Queue

{

[Serializable]

publicclassQueueMessageBase

{

publicbyte[]ToBinary()

{

BinaryFormatterbf=newBinaryFormatter();

MemoryStreamms=newMemoryStream();

ms.Position=0;

bf.Serialize(ms,this);

byte[]output=ms.GetBuffer();

ms.Close();

returnoutput;

}

publicstaticTFromMessage(CloudQueueMessagem)

{

byte[]buffer=m.AsBytes();

MemoryStreamms=newMemoryStream(buffer);

ms.Position=0;

BinaryFormatterbf=newBinaryFormatter();

return(T)bf.Deserialize(ms);

}

}

[Serializable]

publicclassToastQueueMessage:QueueMessageBase

{

publicToastQueueMessage()

:base()

{

}

publicstringTargetUserName{get;set;}

publicstringMessageText{get;set;}

publicstringTitle{get;set;}

publicDateTimeCreatedOn{get;set;}

}請記住,要使用BinaryFormatter類,需要以完全信任模式(可以通過服務(wù)配置文件啟用此模式)運(yùn)行WindowsAzure工作者角色?,F(xiàn)在,我們需要一個簡單的包裝來與我們的隊(duì)列交互。從本質(zhì)上說,我們需要能夠?qū)⑾⒉迦腙?duì)列,獲取任何掛起的消息并清除該隊(duì)列(請參見圖3)。圖3用于與隊(duì)列交互的包裝namespaceHollywoodHackers.Storage.Queue

{

publicclassStdQueue:

StorageBasewhereT:QueueMessageBase,new()

{

protectedCloudQueuequeue;

protectedCloudQueueClientclient;

publicStdQueue(stringqueueName)

{

client=newCloudQueueClient

(StorageBase.QueueBaseUri,StorageBase.Credentials);

queue=client.GetQueueReference(queueName);

queue.CreateIfNotExist();

}

publicvoidAddMessage(Tmessage)

{

CloudQueueMessagemsg=

newCloudQueueMessage(message.ToBinary());

queue.AddMessage(msg);

}

publicvoidDeleteMessage(CloudQueueMessagemsg)

{

queue.DeleteMessage(msg);

}

publicCloudQueueMessageGetMessage()

{

returnqueue.GetMessage(TimeSpan.FromSeconds(60));

}

}

publicclassToastQueue:StdQueue

{

publicToastQueue()

:base("toasts")

{

}

}

}我們還需要為表存儲設(shè)置一個包裝,以便在用戶登錄到站點(diǎn)之前可以存儲用戶通知??梢允褂肞artitionKey(行集合的標(biāo)識符)和RowKey(可唯一標(biāo)識特定分區(qū)中的每個單獨(dú)行)組織表數(shù)據(jù)。選擇PartitionKey和RowKey使用的數(shù)據(jù)是在使用表存儲時所做的最重要的設(shè)計決策之一。這些特點(diǎn)允許跨存儲節(jié)點(diǎn)進(jìn)行負(fù)載平衡,并在應(yīng)用程序中提供內(nèi)置的可伸縮性選項(xiàng)。不考慮數(shù)據(jù)的數(shù)據(jù)中心關(guān)聯(lián)性,使用同一分區(qū)鍵的表存儲中的行將保留在相同的物理數(shù)據(jù)存儲中。因?yàn)獒槍γ總€用戶存儲對應(yīng)的消息,所以分區(qū)鍵將是UserName,而RowKey則成為標(biāo)識每行的GUID(請參見圖4)。圖4表存儲的包裝namespaceHollywoodHackers.Storage.Repositories

{

publicclassUserTextNotificationRepository:StorageBase

{

publicconststringEntitySetName=

"UserTextNotifications";

CloudTableClienttableClient;

UserTextNotificationContextnotificationContext;

publicUserTextNotificationRepository()

:base()

{

tableClient=newCloudTableClient

(StorageBase.TableBaseUri,StorageBase.Credentials);

notificationContext=newUserTextNotificationContext

(StorageBase.TableBaseUri,StorageBase.Credentials);

tableClient.CreateTableIfNotExist(EntitySetName);

}

publicUserTextNotification[]

GetNotificationsForUser(stringuserName)

{

varq=fromnotificationin

notificationContext.UserNotifications

wherenotification.TargetUserName==

userNameselectnotification;

returnq.ToArray();

}

publicvoidAddNotification

(UserTextNotificationnotification)

{

notification.RowKey=Guid.NewGuid().ToString();

notificationContext.AddObject

(EntitySetName,notification);

notificationContext.SaveChanges();

}

}

}因?yàn)槲覀兊拇鎯C(jī)制已經(jīng)確定,所以我們需要一個工作者角色作為引擎;以便在我們的電子商務(wù)站點(diǎn)的后臺處理消息。為此,我們定義了一個從Microsoft.ServiceHosting.ServiceRuntime.RoleEntryPoint類繼承的類,并將其與云服務(wù)項(xiàng)目中的工作者角色關(guān)聯(lián)(請參見圖5)。圖5作為引擎的工作者角色publicclassWorkerRole:RoleEntryPoint

{

ShoppingCartQueuecartQueue;

ToastQueuetoastQueue;

UserTextNotificationRepositorytoastRepository;

publicoverridevoidRun()

{

//Thisisasampleworkerimplementation.

//Replacewithyourlogic.

Trace.WriteLine("WorkerRole1entrypointcalled",

"Information");

toastRepository=newUserTextNotificationRepository();

InitQueue();

while(true)

{

Thread.Sleep(10000);

Trace.WriteLine("Working","Information");

ProcessNewTextNotifications();

ProcessShoppingCarts();

}

}

privatevoidInitQueue()

{

cartQueue=newShoppingCartQueue();

toastQueue=newToastQueue();

}

privatevoidProcessNewTextNotifications()

{

CloudQueueMessagecqm=toastQueue.GetMessage();

while(cqm!=null)

{

ToastQueueMessagemessage=

QueueMessageBase.FromMessage(cqm);

toastRepository.AddNotification(new

UserTextNotification()

{

MessageText=message.MessageText,

MessageDate=DateTime.Now,

TargetUserName=message.TargetUserName,

Title=message.Title

});

toastQueue.DeleteMessage(cqm);

cqm=toastQueue.GetMessage();

}

}

privatevoidProcessShoppingCarts()

{

//Wewilladdthislaterinthearticle!

}

publicoverrideboolOnStart()

{

//Setthemaximumnumberofconcurrentconnections

ServicePointManager.DefaultConnectionLimit=12;

DiagnosticMonitor.Start("DiagnosticsConnectionString");

//Frmationonhandlingconfigurationchanges

//seetheMSDNtopicat

//go.microsoft/fwlink/?LinkId=166357.

RoleEnvironment.Changing+=RoleEnvironmentChanging;

returnbase.OnStart();

}

privatevoidRoleEnvironmentChanging(objectsender,RoleEnvironmentChangingEventArgse)

{

//Ifaconfigurationsettingischanging

if(e.Changes.Any(change=>changeisRoleEnvironmentConfigurationSettingChange))

{

//Sete.Canceltotruetorestartthisroleinstance

e.Cancel=true;

}

}

}讓我們看一下工作者角色代碼。在初始化和設(shè)置所需的隊(duì)列和表存儲之后,此代碼將進(jìn)入一個循環(huán)。每10秒鐘,它就會處理一次隊(duì)列中的消息。每次我們通過處理循環(huán)時都將獲取隊(duì)列中的消息,直到最終返回null,這表示隊(duì)列為空。您從隊(duì)列中看到的消息永遠(yuǎn)不會超過20個,如果不信,您可以反復(fù)嘗試來驗(yàn)證一下。對隊(duì)列進(jìn)行的任何處理都有時間限制,必須在該時間范圍內(nèi)對每個隊(duì)列消息執(zhí)行有意義的操作,否則隊(duì)列消息將被視為超時,并在隊(duì)列中顯示備份,以便可以由其他工作者來處理此消息。每個消息都會作為用戶通知添加到表存儲中。關(guān)于工作者角色需要記住的重要一點(diǎn)是:一旦入口點(diǎn)方法完成,工作者角色也就結(jié)束了。這就是您需要在一個循環(huán)中保持邏輯運(yùn)行的原因。從客戶端的角度來說,我們需要能夠以JSON形式返回消息,以便jQuery可以異步輪詢并顯示新的用戶通知。為此,我們會將一些代碼添加到消息控制器中,以便可以訪問這些通知(請參見圖6)。圖6以JSON形式返回消息publicJsonResultGetMessages()

{

if(User.Identity.IsAuthenticated)

{

UserTextNotification[]userToasts=

toastRepository.GetNotifications(User.Identity.Name);

object[]data=

(fromUserTextNotificationtoastinuserToasts

selectnew{title=toast.Title??"Notification",

text=toast.MessageText}).ToArray();

returnJson(data,JsonRequestBehavior.AllowGet);

}

else

returnJson(null);

}在VisualStudio2010beta2下的ASP.NETMVC2(我們用于撰寫本文的環(huán)境)中,如果沒有JsonRequestBehavior.AllowGet選項(xiàng),您無法將JSON數(shù)據(jù)返回到j(luò)Query或其他客戶端。在ASP.NETMVC1中,不需要此選項(xiàng)?,F(xiàn)在,我們可以編寫JavaScript,它每15秒將調(diào)用一次GetMessages方法并將以toast形式消息顯示通知(請參見圖7)。圖7以toast形式消息顯示的通知$(document).ready(function(){

setInterval(function(){

$.ajax({

contentType:"application/json;charset=utf-8",

dataType:"json",

url:"/SystemMessage/GetMessages",

success:function(data){

for(msgindata){

$.gritter.add({

title:data[msg].title,

text:data[msg].text,

sticky:false

});

}

}

})

},15000)

});提交和處理購物車在我們的示例應(yīng)用程序中,我們希望使用隊(duì)列存儲執(zhí)行的另一個關(guān)鍵方案是提交購物車。HollywoodHackers有一個第三方履行系統(tǒng)(HollywoodHackers無法在其空間有限的倉庫中保留所有小工具),所以引擎需要對購物車進(jìn)行一些處理。一旦引擎完成其處理,它會向用戶通知隊(duì)列提交一個消息,告知用戶已經(jīng)對購物車進(jìn)行了處理(或者出現(xiàn)了問題)。如果處理購物車時用戶處于在線狀態(tài),該用戶將收到系統(tǒng)彈出的一個toast消息。如果用戶不在線,則會在其下次登錄到該站點(diǎn)時收到該彈出消息,如圖8所示。圖8示例用戶通知查看原圖(大圖)我們首先需要的是一些包裝類,使我們可以與購物車隊(duì)列交互。這些包裝非常簡單,如果要查看它們的源代碼,可以在CodePlex站點(diǎn)上查看。與標(biāo)準(zhǔn)CRUD(創(chuàng)建、讀取、更新、刪除)存儲庫不同的是,隊(duì)列中的讀取操作不是單純的讀取操作。請記住,只要獲取隊(duì)列中的消息,必須在有限的時間內(nèi)處理該消息,操作失敗或刪除消息都會顯示處理完成。這種模式不能順利地轉(zhuǎn)換到存儲庫模式,所以我們已經(jīng)不再借助包裝類執(zhí)行此操作?,F(xiàn)在,我們已經(jīng)擁有了要與購物車隊(duì)列交互的代碼,我們可以將一些代碼放在購物車控制器中,以便將購物車內(nèi)容提交到隊(duì)列中(請參見圖9)。圖9向隊(duì)列提交購物車publicActionResultSubmit()

{

ShoppingCartMessagecart=newShoppingCartMessage();

cart.UserName=User.Identity.Name;

cart.Discounts=12.50f;

cart.CartID=Guid.NewGuid().ToString();

Listitems=newList();

items.Add(newShoppingCartItem()

{Quantity=12,SKU="10000101010",

UnitPrice=15.75f});

items.Add(newShoppingCartItem()

{Quantity=27,SKU="12390123j213",

UnitPrice=99.92f});

cart.CartItem

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論