在Azure上構建一個基于Facebook的營銷式應用程序(下)
原創本文承接《在Azure上構建一個基于Facebook的營銷式應用程序(上) 》 和《在Azure上構建一個基于Facebook的營銷式應用程序(中)》
選擇一個商店
如果我們在聯系信息窗體中通過了驗證,我們接下來就可以讓用戶選擇一個他自己喜歡的商店了。這個應用程序會給用戶展示一個包含三個商店的列表(這三個商店都在這個用戶的郵政編碼所在地方圓50英里之內,是按照從最近到最遠的順序來排序的)。要想讓這個窗體快速地顯示出來,盡量減少Web角色上的負載,提前計算距離,然后把據距離信息和商店數據整理到Azure Table Storage中是我們首先應該做的事情。要時刻謹記,我們的目標是可擴展性,而不是想當然的可升級性或未來的可預見性——營銷式應用程序存在的時間相對較短。
我使用的是公用的來自于1999年人口普查的美國郵政編碼列表(具體可以參考:http://www.census.gov/geo/www/tiger/zip1999.html)。這個列表包含每個郵政編碼的經度和緯度坐標。然后,因為我沒有自己的商店,所以我從這個列表隨機地選擇了一些郵政編碼,然后用這些郵政編碼生成了一個包含1000個樣例商店的列表。然后,把這個商店列表通過CSV文件(這種文件格式對Azure Azure Storage Explorer比較友好)的形式,上傳到我的***個Azure表中,“Store”實體如下所示:
Listing 7
public class Store: TableServiceEntity
{
// partition key is store number
// row key is empty
public Store()
{
this.RowKey = "";
}
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
接下來,我構建了第二張表,叫作“ZipStore”,對于這個國家中的每個郵政編碼來說,它包含0到3行。每行是一個“郵政編碼-商店”對,對于每個郵政編碼來說,只考慮在這個郵政編碼所在地方圓50英里內的三個最近的商店。要構建這樣一張表,我必須要遍歷郵政編碼列表,然后使用一個基于經度和緯度的公式(具體可以參考:http://zips.sourceforge.net/),計算出各個郵政編碼所在地到每一個商店的距離,把范圍內最近的三個商店保存下來。如果方圓50英里之內根本就沒有商店,那么我會放棄這個郵政編碼。30分鐘的數值分析之后,我得到了一個包含“郵政編碼-商店”信息的CSV文件,然后我把這個文件上傳到了Azure Table Storage中。“ZipStore”實體是十分簡單的。
Listing 8
class ZipStore: TableServiceEntity
{
// partition key is zip code
// row key is distance rank (0 is nearest)
public string StoreNumber { get; set; }
}
“partition key”存儲了郵政編碼。“row keys”是每個商店的靠近等級。每個“partition”代表一個郵政編碼,“partition”中的每一行代表一個商店和這個郵政編碼所在地的靠近程度。
現在,我們可以看一看“Store”容器了:
Listing 9
public class StoreRepository : IStoreRepository
{
readonly AzureTable<ZipStore> _zipStore;
readonly AzureTable<Store> _store;
public StoreRepository()
{
var factory = new AzureStorageFactory ();
_zipStore = (AzureTable<ZipStore>)factory.GetTable><ZipStore>();
_store = (AzureTable<Store>)factory.GetTable><Store>();
}
public IEnumerable<Store> GetNearbyStores(string ZipCode)
{
var stores = new List<Store>();
Store store;
var query = _zipStore.Query.Where(zs => zs.PartitionKey == ZipCode);
foreach (ZipStore zs in query)
{
store = _store.Get(s => s.PartitionKey == zs.StoreNumber && s.RowKey == "");
if (store != null)
stores.Add(store);
}
Return stores;
}
}
這里值得一提的是“GetNearbyStores()”方法,控制器使用這個方法來加載可用的商店列表。***個查詢只指定了“partition key”(郵政編碼),然后返回三個或少于三個商店。因為“row key”是距離等級,所以這個查詢會按照距離的順序來返回各個商店。而對于每個商店來說,我們使用商店號作為“partition key”,從“store”表中獲取商店名,城市,國家和郵政編碼。
雖然對于這個視圖來說,必須要進行四次查詢,但是它們都是通過“partition key”和“row key”來進行的,速度非常快。而且,你還可以購買存儲空間,通過把全部的“store”實體都直接存儲到“ZipStore”表中,盡量減少對單一查詢的檢查的方式,來獲得更好的性能。
#p#
存儲和隊列
這個叫作“SelectStore”的處理程序用用戶選擇的商店和注冊標記來更新“contact”,然后保存這個“contact”。
Listing 10
[HttpPost]
[CanvasAuthorize]
public ActionResult SelectStore(SelectStoreViewModel model)
{
FacebookApp app = new FacebookApp ();
Contact contact = contactRepository.GetFromFacebookId(app.UserId);
if (ModelState.IsValid)
{
contact.StoreNumber = model.StoreNumber;
contact.Registered = true;
contactRepository.Save(contact);
QueueContact(contact);
return this.CanvasRedirectToAction("SignupComplete");
}
Return View(model);
此外,為了方便Worker角色進行處理,這個控制器會把“contact”排成隊列。這是“QueueContact”的完整實現:
Listing 11
private void QueueContact(Contact contact)
{
// queue the contact for the worker role.
ContactQueueMessage message = new ContactQueueMessage();
message.FacebookId = contact.PartitionKey;
contactQueue.AddMessage(message);
}
Windows Azure Toolkit可以讓你更方面地使用Azure隊列。對于在多個角色(一個可擴展的應用程序是由這些角色組成的)之間進行通信來說,隊列是必需的。“ContactQueue”繼承于一個Windows Azure Toolkit類型,“ContactQueueMessage”也是如此,它們可以讓隊列方法的實現變得更加簡單。但是,當這個工具包從Worker角色的隊列中獲取消息的時候,這個工具包會才會真正地大放異彩。接下來我們將會看到這一點。
在Worker角色中Dequeuing
Worker角色做了很多的工作,但是這一切都是在響應隊列消息的過程中完成的。使用Windows Azure Toolkit,我們可以在這個角色的“OnStart()”方法中創建一個隊列處理程序。我們只需把這段代碼添加到默認的實現中就可以了:
Listing 12
AzureStorageFactory factory = new AzureStorageFactory();
IAzureQueue<ContactQueueMessage> queue =
factory.GetQueue<ContactQueueMessage>(AzureConstants.ContactQueueName);
QueueHandler.For<ContactQueueMessage>(queue)
.Every(TimeSpan.FromSeconds(5))
.Do(new EmailAndStoreContact());
在“OnStart”中,我們創建了一個“AzureStorageFactory”對象,然后使用它來查找“contact”隊列。然后,我們創建了一個處理程序,在這個例子中,它每5秒檢查一次隊列,如果有一個消息可用的話,它就用這個消息調用我們的消息處理器(“EmailAndStoreContact”)。因為處理隊列消息是Worker角色的工作,所以,我們需要做的所有事情只是把它寫入到“EmailAndStoreContact”中而已。對于這個類來說,Main方法被叫作Run,它的實現如下所示:
Listing 13
public void Run(ContactQueueMessage message)
{
Contact contact = contactRepository.Get(message.FacebookId);
if (contact != null)
{
// TODO: contact another service to send off the email with
// the premium coupon here (outside the scope of this sample)!
// store the contact to sql Azure.
SQLContact sqlContact = new SQLContact();
MapContactToSqlContact(contact, sqlContact);
sqlContactRepository.Add(sqlContact);
sqlContactRepository.Save();
}
}
這個消息處理器從隊列消息中獲取“contact”的Facebook ID,然后使用它從這個容器中獲取“contact”。因為在這個應用程序中我們并沒有真正地email什么內容,所以剩下的所有事情只是把“contact”發送到SQL Azure中而已。這里,我們使用了一個“SQLContact”對象,它是“LINQ to SQL”中的一個數據類。
究竟為什么從Azure Table Storage遷移到SQL Azure呢?因為雖然Azure表很快,但是它們并不適合進行某些特定的查詢。把聯系數據遷移到SQL Azure中可以生成表,其他應用程序就可以查詢這個“contact”表來判斷我們的活動進行的怎么樣了。
#p#
起點
你可以把這個應用程序的源碼當成你自己在Facebook上的營銷活動的起點。就像上面討論的那樣,(除了50000個獎品)你還需要添加一些其他的東西。首先,在Worker角色中,要有email的處理程序。其次,要有一些輔助性的Facebook要素,例如:支持這個應用程序在公司頁面上作為一個標簽來運行,為“like”你的頁面的客戶提供額外的獎勵,通過邀請或Wall posts,進一步促進“病毒”的傳播。在大多數情況下,支持這些特性意味著直接和Facebook JavaScript API打交道。
即使你從來沒有發起過你自己的活動,對于那些使用C#的Azure開發者來說,也會有很多收獲的。首先,使用Windows Azure toolkit可以簡化訪問Azure存儲表和隊列的過程。其次,如果你正在編寫一個Facebook應用程序,那么你可以使用Facebook C# SDK。再次,使用Worker角色可以把后臺任務分離出來。***(但并非到此為止),可以讓你知道如何組織你的存儲結構才能給你的應用程序提供***的可擴展性。
你需要的一些工具:
這些工具的***版本可以通過下面這些地址來下載:
Windows Azure SDK的下載地址:http://msdn.microsoft.com/en-us/windowsazure/cc974146.aspx
Windows Azure Toolkit的下載地址:http://azuretoolkit.codeplex.com/
Facebook C# SDK的下載地址:http://facebooksdk.codeplex.com/
樣例代碼:
這些樣例代碼是本文寫作的時候使用的一些代碼。
你可以通過如下地址來下載本文的樣例代碼:
http://facebooksdk.codeplex.com/releases/view/60694
本文承接《在Azure上構建一個基于Facebook的營銷式應用程序(上) 》
和《在Azure上構建一個基于Facebook的營銷式應用程序(中)》
原文名:Building a Facebook Marketing App on Azure 作者:Steve Apiki
【本文乃51CTO精選譯文,轉載請標明出處!】
【編輯推薦】