November 2008 - Posts
אחרי שבפוסטים הקודמים עשינו היכרות קצרה עם Windows Azure והכלים הבסיסיים שלו, הגיע הזמן לכתוב קוד.
מה נעשה?
האפליקציה שאדגים היא באמת אפליקציה פשוטה, אבל כזאת שמדגימה את העקרונות החשובים ב cloud service. ב cloud service יהיה לנו שני חלקים, שביחד יהוו cloud service שממיר קבצים מפורמט BMP ל-JPG:
- Web Role - יהווה ממשק WEB-י לאפליקציה שלנו. יאפשר למשתמשים להעלות את הקובץ, ולצפות בקבצים המומרים.
- Worker Role - יבצע בפועל את ההמרה.
מה צריך?
את הפיתוח של האפליקציה אני אדגים מהמכונה הלוקאלית שלי, כך שלמעשה כל מה שצריך זה לעקוב אחרי ההוראות הבסיסיות שכתבתי פה.
יצירת הפרוייקט החדש
ב Visual Studio, צרו פרוייקט חדש מהסוג Web and Worker Cloud Service. הסוג הזה, הוא למעשה template של cloud service שמגיעה עם שני roles מוגדרים וכל ההוראות הדרושות כדי שנוכל לבצע build ולדבג לוקאלית, מהמכונה שלנו, ואף לפבלש בהמשך כ Hosted Service ל Windows Azure.
כדי שנוכל לעבוד בקלות עם queues ו blobs, עליהם דיברתי בפוסט הקודם, נוסיף רפרנס לDLL של הפרוייקט StorageClient, שנמצא בתיקייה של ה SDK. את הרפרנס נוסיף גם ל Worker Role וגם ל Web Role.
הכנת קבצי הקונפיגורציה
פרוייקטים של cloud service מכילים שני סוגי קבצי קונפיגורציה דיפולטיים. הראשון, נקרא ServiceDefinition.csdef ואמור להכיל את הרשימה של איזה הגדרות יכיל קובץ הקונפיגורציה. ההגדרות עצמם, בדיוק כל ההגדרות שהוגדרו ללא הוספה או החסרה, אמורות להופיע בקובץ ServiceConfiguration.cscfg. את התוכן המדוייק של הקבצים, תוכלו לראות בפרוייקט ההדגמה שזמין להורדה בתחתית הפוסט. בגדול, הגדרנו AccountName ו AccountSharedKey שמיועדים להדגמה. BlobStorageEndpoint ו QueueStorageEndpoint אלה הגדרות שמתייחסות לאן יישלכו בקשות ה REST לעבודה עם כלים אלה. הערכים שהכנסתי, 127.0.0.1 בפורטים 1000 ו-1001 בהתאמה, אלה הערכים שבהם פועלת סביבת הפיתוח הלוקאלית.
כתיבת ה Web Role
הממשק ה WEB-י שלנו עומד להיות ממש פשוט, ולהכיל שני דברים בדיוק: אפשרות להעלות קובץ חדש להמרה, וקישורים להורדה של הקבצים שכבר הומרו. להעלאת הקבצים נשתמש ב FileUpload control.
למען הנוחוות, הנה הקוד המלא של העמוד עם ה WebRole (לחצו להגדלה):

בקוד הזה יש כמה חלקים:
בחלק הראשון, במידה ויש קובץ להעלאה, אני בודק האם כבר בוצע אתחול ראשוני. באתחול ראשוני, הכוונה ליצירת blob containers ויצירת queue. אני יוצר container, כאשר אני מגדיר שלא לשייך לו metadata כלשהו ולהגדיר שהוא public. את ההגדרות, כלומר, את ה endpoints אני לוקח מהקובץ קונפיגורציה.
בחלק השני, אני יוצר את ה blob, כלומר את הקובץ עצמו ומוסיף אותה ל container (אני מקבל שוב פעם את המופע הקיים, ויוצר את ה blob). הפרמטר true אומר לדרוס במידה שהשם כבר קיים. בחלק השלישי אני שם message ב queue וכותב ללוג.
למעשה, זה כל ה web role, והוא באמת עובד (כפי שתוכלו לראות כשתריצו את הפרוייקט המוגמר אצלכם) ומאפשר להעלות קבצים.
עצה: אם אתם רוצים להעלות אם קבצים הועלו בסביבת הפיתוח הלוקאלית שלכם, תריצו את פרוייקט הדוגמא Cloud Drive שיפתח לכם חלון powershell עם provider שמאפשר לנווט ב blob containers.
כתיבת ה Worker Role
כפי שהסברתי בפוסטים הקודמים, ה Worker Role הוא זה שעושה את העבודה. במקרה שלנו, לעבוד באופן קבוע, ואם יש לו איזשהו פריט בתור שצריך להמיר, לבצע את ההמרה ולשמור כ blob ב container אחר.

כל הקוד הזה נמצא בתוך ה class שנוצר אוטומטית כחלק מהטמפלייט שיורש מ RoleEntryPoint במתודה Start.
מה שאנחנו עושים פה, זה דבר ראשון לכתוב ללוג. שימו לב, שאת ההודעות של הלוג אפשר לראות כשמפעילים במכונה הלוקאלית את ה Development Fabric.
לאחר מכן, בחלק הראשון והשני, אנחנו יוצרים שני containers. הראשון, למעשה מקבל את ה container שאליו נשמרים הקבצים המקוריים, bmptoconvertcontainer והשני הוא converted, שאליו נשמור את הקבצים המומרים.
בחלק השלישי, אנחנו מתחברים לתור עם המידע.
ואז, בלולאה אין סופית, אנחנו שולפים מהתור, במידה וקיים פריט ומעבדים אותו - בחלק החמישי. אנחנו למעשה מכניסים למשתנה path את מה שמכיל ההודעה בתור, שזה למעשה הGUID של blob ואז אנחנו מוציאים את ה blob מה container ואת התוכן שלו מכניסים לאובייקט BlobContents שיצרנו.
בהמשך, אנחנו מוסיפים blob (קובץ) חדש ל blob container של המומרים (converted), ואנחנו למעשה כותבים אליו את המערך bytes שחוזר מהמתודה שכתבתי ConvertImage שממירה מBMP ל-JPEG. ואז, אני כותב ללוג שהומר ומוחק את הפריט מהתור.
כבר בשלב הזה הכל עובד. אם נריץ את ה web role, לאחר שנעלה, נראה בלוג את ההודעה עם הGUID של הפריט והמידע שהוא מוכן לעיבוד. במקביל, נוכל להסתכל על הלוג (דרך ה Development Fabric) של ה Woker ושם נראה שהוא באמת מעבד את הפריט. אבל, רק כדי שיהיה נוח, אני הוספתי ל Page Load של דף הבית של ה web role קוד קטן שיציג לנו את מה שנמצא ב blob container של converted. למעשה, שיציג את כל ה blobs שם.

שימו לב, ש list blobs מקבלת פרמטר אחד שאליו העברנו string ריק. מדובר, למעשה, ב prefix. ב blob container אחד נוכל לשמור כמה סוגים של פריטים (נניח, את הקבצים לפני ההמרה ואחרי ההמרה) כשהדרך שנפריד בהם זה ה prefix. הסיבה העיקרית לכך, היא לחסוך מקום במידה והתימחור יהיה על בסיס blob containers. לטעמי, הרבה יותר נוח להשתמש בזוג blob containers נפרדים עבור כל סוג.
וזהו, עכשיו יש לנו הכל עובד.
אז, מה הלאה?
השלב הבא,הוא להוריד את הקוד של הפרוייקט ולראות איך הכל מסתדר ביחד. מה שעשיתי בפוסט הזה זה להציג ולהבהיר נקודות עיקריות, לא נכנסתי למשמעות של כל שורה. אם יש שאלות, גם גוגל וגם אני נשמח לעזור.
אני אפרסם עוד פוסט, שבו אני אדגים כיצד להעלות את כל מה שפיתחנו עד עכשיו על המכונה הלוקאלית ל datacenters של מיקרוסופט, כלומר לפרודקשן ב Windows Azure.
בהצלחה!
Windows Azure, עליו כתבתי בכלליות בפוסט הקודם, מציע לנו תשתית מעניינת (שכמובן, ניתנת להרצה לוקאלית גם לצרכי דיבאג ופיתוח) לשמירת מידע של ה cloud service שלנו ולהעברת מידע בין חלקין שונים של ה service (נניח, בין Web Role ל Worker Role) או בין services שונים.
לצורך כך, יש שלוש סוגים של שמירת מידע ואכסון מידע שעליהם אדבר בפוסט זה. בפוסט זה, אני אציג את המושגים ואכיר לכם את הכלים. הסברים בפועל איך עובדים מולם, יהיו בפוסט הבא.
Queues
תורות, זאת הדרך שלנו לשמור מידע ולהעביר מידע לטיפול. נניח, לצורך העניין, שיש לנו service עם web role ו worker role. ה worker role, כפי שכתבתי בפוסט הקודם, מתפקד כלולאה אין סופית, כאשר הוא ממתין לאיזשהו מידע שיגיע שאותו הוא יעבד ועליו הוא יבצע את תפקידו (זה ה scenario הנפוץ ביותר). המידע הזה, מגיע מתור. כאשר, מנגנון התורות המשמש הוא המנגנון של Windows Azure. דרך ה web role אפשר להעביר את ההוראה על הפעולה, כאשר ההוראה תיכנס לתור ו worker role יבצע אותה (כאשר, יכולים להיות כמה מופעים של אותו worker role שיוציאו במקביל מהתור, ו Windows Azure ערוך להתמודד עם זה).
בד"כ, מה שיעבור ב Queue אלה למעשה הפניות לפריטים שאותם צריך לעבד. למשל, איזשהו ID של קובץ שעליו צריך לעשות עיבוד, איזשהו חשבון שצריך ליצור וכו'.
Containers ו- Blobs
צורך נוסף שקיים, הוא לאכסן קבצים ומידע בינארי על סוגיו השונים. לצורך זה
, קיימים ה Blobs וה Containers. ב Windows Azure, ניתן ליצור חשבון מסוג Storage Account שהוא זה שמכיל למעשה את ה containers (יכול להכיל מספר רב שלהם) וכל container מכיל blob (קובץ, לצורך העניין).
כאשר עובדים מול סביבת ה production הסופית, נגדיר את ה Endpoints לאותם ה endpoints שיופיעו במסך שלאחר יצירת ה storage project. כאשר נפתח לוקאלית נצטרך להריץ את ה Development Storage (התמונה התחתונה) ששם יופיעו לנו מה ה endpoints שנשתמש
בהם בפיתוח הלוקאלי. שימו לב, שה development storage מדמה storage project אחד ב Windows Azure.
איך בפועל עובדים מול הכלים הללו?
לכלים הללו, כמו לכל הכלים ב Windows Azure יש ממשק REST API.
אל תדאגו, אתם לא באמת צריכים לעבוד עם ה REST API הזה. אחד מה samples projects שמגיעים עם ה SDK נקרא Storage Client. הפרוייקט החמוד הזה, מכיל למעשה אובייקטים שמאפשרים לעבוד בצורה נורמלית ונוחה, מונחית אובייקטים מול הכלים שהזכרתי בפוסט זה.
על איך עובדים בפועל, אני אכתוב בפוסט הבא. בינתיים, אם אתם רוצים לראות איך הכל משתלב בצורה יפה, יש פרוייקט בשם Thumbnails ב SDK, שמדגים פרוייקט עם שני roles - הראשון web role שמאפשר להעלות תמונות שמאוכסנות כ blob ב container ומכניס את הID של התמונה ל queue. ה worker role מוציא את ה ID של התמונה מה queue, יוצר ממנה צלמית ושומר גם אותה כ blob ב container אחר.
בפוסט הבא, אני אדגים איך עובדים עם כל הכלים הללו, באמצעות המחלקות שנמצאות בפרוייקט הדוגמא StorageClient.
מה זה Windows Azure?
Windows Azure, שהוכרז בPDC, הוא שם של משהו שבין מערכת הפעלה לשירות שמאפשר לנו, כמפתחים, לפתח אפליקציות שירוצו "בענן".
בניסוח קצת פחות עיתונאי, וקצת יותר ברור, Windows Azure זה שם של מערכת הפעלה, אבל לא כזאת שרצה אצלכם. המערכת הזאת, רצה במרכזי השרתים של מיקרוסופט, ואת האפליקציות מעלים אליה. המערכת מבוססת על Windows Server בתוספת עוד כמה תכונות. בגדול, כמפתחים, ניתן להתייחס ל Azure כמערכת שמריצה אפליקציות שנכתבות בצורה מסויימת ב .net עם כמה יתרונות, וגם עם כמה מגבלות.
מה אפשר לפתח עבור Windows Azure?
בעבודה עם Windows Azure, יש לנו שני סוגי פרוייקטים עיקריים - יש לנו Web Cloud Service, כלומר, Web Application שרץ בענן ו Worker Cloud Service (כלומר, איזשהו service שרץ ללא UI - משהו בדומה ל windows service) ושילוב של שניהם. כל אחד מהם, או שניהם ביחד, הם חלק ממה שנקרא Cloud Service - השירות עצמו שרץ בענן. לכל שירות יכולים להיות בין 0-2 roles, כאשר role ךצורך העניין זה או ה Worker Role חסר הממשק או ה Web Role. ה Cloud Service, ירוץ ב production בתור Hosted Service.
Worker Role מכיל class שיורש מ RoleEntryPoint ועושה override למתודה Start ו GetHealthStatus. המתודה Start רצה תמיד בתחילת כל מופע של ה Worker Role (לכל Cloud Service יכול להיות הרבה יותר ממופע אחד בו זמנית) והיא אחראית לקרוא ללוגיקה שלו. בד"כ, המטרה ב WorkerRole תהיה שירוץ תמיד (לולאת while true) ובד"כ יבצע פעולות עיבוד על נתונים שיעברו אליו ב queue (יש ב Windows Azure מערכת של storage services שמספקת שירותי queues, אכסון קבצים, שמירת מידע בצורה טבלאית עם ADO.NET Data Services)
ה WebRole יכול להיות החל מאיזשהו ממשק משתמש ל Worker Role ועד אתר שלם, ללא Worker Role כלל. במקרה כזה, יהיה צורך להתגבר על כמה מגבלות של אי נוחות (כמו חוסר האפשרות לעבוד עם DB בצורה המקובלת, מגבלות בשמירת state וכו') - לחלקן, יש פיתרון במסגרת ה samples של ה SDK (כמו גרסא ל providers של ASP.NET) ולחלקן פיתרונות שפותחו בשילוב אנשים מהקהילה וממיקרוסופט - כמו שיטה להריץ ASP.NET MVC על Windows Azure.
בכל אופן, ניתן לפתח (כמעט) כל דבר שירוץ בענן, כאשר בפועל בשרתים יש .NET 3.5 עם Service Pack כך, שכל אפליקציה המשתמשת בהם - תרוץ. חשוב לזכור, שאין עבודה עם SQL Server ישירות, אלא רק באמצעות ADO.NET Data Services.
איך מתחילים?
כעיקרון, Windows Azure הוא Hosted Service בשרתים של מיקרוסופט. אבל כדי לאפשר פיתוח נוח ודיבגינג, מוצעת האפשרות להריץ לוקאלית, בדומה ל Web Development Server המשולב ב Visual Studio. כדי לעשות זאת:
- תורידו ותתקינו את ה SDK
- תורידו ותתקינו Windows Azure Tools for Microsoft Visual Studio 2008 שיוסיף לכם את ה templates הנחוצים כדי להתחיל לפתח ולדבג בקלות מ VS.
בשלב זה, יתחילו לרוץ ה Development Storage שעושה סימולציה לשירות ה storage של Windows Azure ובהם Blobs, Queues ו- Tables. וה Development Fabric שמתוכו ניתן יהיה לעקוב אחרי כל ה services שרצים על ההדמייה הלוקאלית של windows azure שלנו, לראות את הלוגים שלהם וכו'. בצורה הזאת, גם ניתן לפתח ולדבג בקלות גם ללא token שמאפשר שימוש ב Windows Azure "האמיתי". הוא גם מפשט את תהליך ה publish ל data centers של מיקרוסופט (עליו אני אדבר בפוסט אחר). למעשה, כעת כל מה שאתם צריכים זה ליצור פרוייקט חדש ולבחור מה templates של Windows Azure ואתם יכולים לפתח ולדבג בדיוק כפי שאתם רגילים:
שימו לב, שישנם גם samples בנתיב דומה לזה: C:\Program Files\Windows Azure SDK\v1.0
תיכנסו, תעשו extract ל samples.zip ותריצו את buildall.cmd ואת createtables.cmd.
שימו לב שה samples זה הרבה מעבר ל samples: הAPI עצמו של Windows Azure כולל את המינימום הנדרש, מאחר שרוב הדברים נעשים באמצעות קריאות SOAP ו REST שמולם מתקשרים עם, למשל, שירותי ה storage של windows azure (גם האמיתיים וגם ה development). ב samples, יש למשל את StorageClient שכולל מימוש של אובייקטים שחוסכים כל פעם לעשות את התקשורת ידנית והופכים את העבודה עם ה Storage Services (עליהם אני אדבר בפוסט אחר) לפשוטה. למעשה, מדובר בכלי נדרש (שאני מקווה שיהפוך לחלק מה API עצמו) שכמעט תמיד כשבונים אפליקציה עבור windows azure ורוצים להשתמש ב storage services מומלץ להשתמש בו כדי לתקשר מולם.
כמו כן, יש שם מימוש של ה asp.net providers עם ה Data Services, בצורה המתאימה ל Windows Azure ופרוייקט Storage Client שמתבסס על ה providers של powershell ומאפשר ממש לנווט בנתונים המאוכסנים ב Storage Services.
מה הלאה?
אני אמשיך לפרסם פוסטים על Windows Azure ועל העבודה מולו. למעשה, כבר עכשיו אתם יכולים להתחיל לפתח אפליקציות שרצות בענן, גם כאשר הענן הוא רק המחשב שלכם.
בהצלחה.
במצבים מסויימים, נרצה לעשות serialization לאיזשהם אובייקטים שיוצרו ע"י LINQ to SQL. במילים אחרות, לקחת את האובייקטים שמופו מהטבלאות ולעשות להם סריאליזציה, למשל, כדי לשלוח אותם במסגרת Web Service. השגיאה עליה אני מדבר, תקרה בעיקר באובייקטים שמופו שיש להם realationships עם אובייקטים אחרים. ה Exception, ייראה בערך ככה:
System.InvalidOperationException: A circular reference was detected while serializing an object of type BL.User.
השגיאה הזאת נובעת, בגלל הצורה שבה ממומשים ה realationships בין האובייקטים. כל אובייקט שמופה ויש לו relationship עם טבלה אחרת יש לו property שבו הוא מחזיר את האובייקט אליו הוא מופה. במילים אחרות - באובייקט User יש property שמחזיר אובייקט UserInSite. עד פה, הכל בסדר.
הבעייה מתחילה באובייקט UserInSite. הבעייה היא, שגם לו יש relationship עם User. וגם לו יש property שמחזיר, נחשו מה, אובייקט User. וזה, מה שקוראת לו השגיאה - circular reference. במקרה כזה, נרצה לבטל את המעגל הזה.
אין צורך לוותר על ה relationship עצמו, צריך רק להוריד כיוון אחד.
מה שיש לעשות, זה לשים את האטריביוט [NonSerialized] מעל להצהרה בקובץ קוד של ה LINQ to SQL data classes (הקובץ שמסתיים ב .designer.cs).
אפשרות נוספת, היא להפוך את ה property ל internal, וכך הוא לא יעבור serialization. את זה עושים (כדי שלא יידרס) בדרך הבאה:
בחלון של המודל, לחצו על ה relationship המבוקש, בחלון ה properties, ב child property ב access תבחרו internal.
בכל מקרה, אחת משתי הדרכים הללו תגרום לכך שלא יהיה circular reference.
ובא לציון גואל וסריאליזציה ליעקב (גם אם הוא ממופה ב LINQ to SQL ויש לו relationship לאובייקט אחר).
במצבים מסויימים, נרצה לעשות serialization לאיזשהם אובייקטים שיוצרו ע"י LINQ to SQL. במילים אחרות, לקחת את האובייקטים שמופו מהטבלאות ולעשות להם סריאליזציה, למשל, כדי לשלוח אותם במסגרת Web Service. השגיאה עליה אני מדבר, תקרה בעיקר באובייקטים שמופו שיש להם realationships עם אובייקטים אחרים. ה Exception, ייראה בערך ככה:
System.InvalidOperationException: A circular reference was detected while serializing an object of type BL.User.
השגיאה הזאת נובעת, בגלל הצורה שבה ממומשים ה realationships בין האובייקטים. כל אובייקט שמופה ויש לו relationship עם טבלה אחרת יש לו property שבו הוא מחזיר את האובייקט אליו הוא מופה. במילים אחרות - באובייקט User יש property שמחזיר אובייקט UserInSite. עד פה, הכל בסדר.
הבעייה מתחילה באובייקט UserInSite. הבעייה היא, שגם לו יש relationship עם User. וגם לו יש property שמחזיר, נחשו מה, אובייקט User. וזה, מה שקוראת לו השגיאה - circular reference. במקרה כזה, נרצה לבטל את המעגל הזה.
אין צורך לוותר על ה relationship עצמו, צריך רק להוריד כיוון אחד.
מה שיש לעשות, זה לשים את האטריביוט [NonSerialized] מעל להצהרה בקובץ קוד של ה LINQ to SQL data classes (הקובץ שמסתיים ב .designer.cs).
אפשרות נוספת, היא להפוך את ה property ל internal, וכך הוא לא יעבור serialization. את זה עושים (כדי שלא יידרס) בדרך הבאה:
בחלון של המודל, לחצו על ה relationship המבוקש, בחלון ה properties, ב child property ב access תבחרו internal.
בכל מקרה, אחת משתי הדרכים הללו תגרום לכך שלא יהיה circular reference.
ובא לציון גואל וסריאליזציה ליעקב (גם אם הוא ממופה ב LINQ to SQL ויש לו relationship לאובייקט אחר).