Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Techdays 2013 NL - Serious Request met Windows Azure

3,391 views

Published on

Bijna iedereen in Nederland kent de Serious Request actie van 3FM en het Nederlands Rode Kruis. Wat weinig mensen weten is dat de afgelopen 2 edities voor een belangrijk deel op Windows Azure draaide. In deze presentatie wordt de architectuur achter de oplossing toegelicht, welke Azure componenten er gebruikt zijn. Wat we geleerd hebben van de eerste editie, wat we daarom aanpast hebben voor de tweede editie, en wat er nog meer mogelijk was omdat het Windows Azure platform zich in de tussentijd ook verder ontwikkeld heeft. Ook worden er veel ervaringen gedeeld waar je als ontwikkelaar tegen aan kan lopen bij het opzetten van een Windows Azure oplossing.

Published in: Technology
  • SECRET: Men usually out of emotion, not logic. Take advantage of this and get your Ex back today! See how at: ♥♥♥ http://ow.ly/f23I301xGAo
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Techdays 2013 NL - Serious Request met Windows Azure

  1. 1. Serious Request met Windows AzureMichaël HompusPrincipal Developer, Winvision
  2. 2. Michaël HompusPrincipal Developer bij WinvisionSpecialisaties zijn SharePoint, Windows 8 Store Apps,Windows Phone 8 en Windows AzureE-mail: michael.hompus@winvision.nlTwitter: @eNeRGy164Blog: http://blog.hompus.nl
  3. 3. Onderwijs Zorg Goede doelenIn 2012 met Serious Request finalist voor besteMicrosoft-oplossing voor goede doelen wereldwijd
  4. 4. Serious Request met Windows AzureNa deze sessie…• …weten jullie hoe wij Serious Request met Windows Azure gerealiseerd hebben en welke problemen en oplossingen we hierbij zijn tegengekomen• …kunnen jullie de tips en voorbeelden gebruiken om zelf een high available en high performance website op Windows Azure te hosten
  5. 5. AgendaIntroductieDe aanleidingDe keuze voor Windows Azure Architectuur De bezoeker Groeien De kostenLinkjesVragen
  6. 6. Sinds 2004 organiseert 3FM de actie “3FM Serious Request”Elke editie staat in het teken van een stille ramp die samenmet het Nederlandse Rode Kruis wordt uitgekozen
  7. 7. DE AANLEIDING
  8. 8. Het Nederlandse Rode KruisSinds 2009 levert Winvision een Microsoft-platformmet SharePoint, Dynamics CRM en Dynamics AX aanhet Nederlandse Rode KruisOp dit platform worden regelmatig onderdelentoegevoegd om de processen bij het Rode Kruis teondersteunen
  9. 9. De vraag van het Rode Kruis“We zoeken voor 3FM Serious Request een nieuweleverancier voor de plaataanvraag website.Kan Winvision een module aan het platformtoevoegen om online platen aan te vragen?”
  10. 10. Wat zijn de functionele requirements?• Een formulier om platen aan te vragen• Een formulier om alleen te doneren• Het Rode Kruis wil content beheren• Het Rode Kruis wil donaties exporteren• 3FM wil de aangevraagde platen inzien• Aangepast formulier voor het callcenter
  11. 11. Wat waren de eerdere ervaringen? Beschikbaarheid applicaties Zware load & aanvallen op de site Oorzaak: alle systemen stonden op dezelfde servers Veel bezoekers op de website ca. 500.000 unieke bezoekers & 60.000 donaties Veel verschillende browsers en devices Grote toestroom tijdens actie Exponentiele groei van bezoekers naar het einde toe Korte levensduur De actie is maar een week op de radio
  12. 12. WAAROM AZURE?
  13. 13. Waarom voor Windows Azure kiezen? Beschikbaarheid applicaties Loosely coupled architectuur in het “DNA” van de services Updates uitrollen zonder down time Veel bezoekers op de website ASP.NET MVC met Razor voor hoge productiviteit HTML5, CSS3 en jQuery voor compatibiliteit in browsers Grote toestroom tijdens actie Proactief meegroeien met de bezoekersaantallen Mogelijkheid om “onbeperkt” hardware toe te voegen Korte levensduur Datacenters gehost en gemanaged door Microsoft Geen hardware of licenties nodig
  14. 14. ARCHITECTUURWelke applicaties hebben we?Welke kenmerken heeft de front- en backend?Hoe verbinden we deze omgevingen?Wat zijn voor- en nadelen van ontkoppelen?
  15. 15. Welke applicaties hebben we? Website vraag een plaat aan Website voor directe donatie Website voor het callcenter Beheersite voor Rode Kruis Beheersite voor 3FM
  16. 16. Frontend BackendVraag een plaat aan NRK Beheersite Blob Storage Directe Donatie Worker role Queues SQL Database Callcenter 3FM Beheersite Shared Cache
  17. 17. Welke kenmerken heeft de frontend?• Anoniem verkeer• Geen directe communicatie met de database• Een transactie is afgerond als het bericht op de Queue staat• Data wordt gelezen van Blob Storage en uit de Shared CacheGeoptimaliseerd om te schalen
  18. 18. Welke kenmerken heeft de backend?• Geautoriseerd verkeer• Redundantie in servers is er alleen voor beschikbaarheid• Directe communicatie met de database• Data wordt geschreven naar de Blob Storage en de Shared CacheGeoptimaliseerd om met data te werken
  19. 19. Hoe verbinden we deze omgevingen?• Storage Queues• Worker role• SQL Database• Blob Storage• Shared Cache
  20. 20. Storage Queues• Berichten worden pas van de Queue verwijderd als ze succesvol verwerkt zijn• Indien de server crasht zal het bericht later door een andere server alsnog opgepakt worden• Elke actie kost geld, ook als de Queue leeg isTip: Gebruik een “back-off” strategie om in tijdenvan inactiviteit het aantal transacties te beperken
  21. 21. Back-off strategie voor queue’sint currentBackoff = 0;while (true) { List<CloudQueueMessage> messages = cloudQueue.GetMessages(1).ToList(); if (messages.Any()) { currentBackoff = 0; // Reset back off foreach (CloudQueueMessage message in messages) { cloudQueue.DeleteMessage(message); // We zijn klaar, verwijder het bericht } } else { // Er zijn geen berichten, ga 2 seconde langer wachten if (currentBackoff < 300) { currentBackoff = currentBackoff + 2 }; Thread.Sleep(TimeSpan.FromSeconds(currentBackoff)); }}
  22. 22. Blob Storage• Zowel beschikbaar voor privé als publieke bestanden• Bestanden worden redundant opgeslagen• Mogelijkheid om de bestanden geografisch redundant op te slaan (Amsterdam - Dublin)• Je betaald voor transacties én opslagTip: Zet bij een blob een goede Cache control headerTip: Je kan bestanden niet in de root opslaan, maar wel ineen container met de naam “$root”
  23. 23. SQL Database (SQL Azure)• Gelimiteerd in aantal concurrent connecties• Je betaald voor de grootte van de database• Microsoft levert availability van de service, geen (historische) back-up van je data• Firewall configuratie om rechtstreeks toegang te verlenen• Goede online tooling voor aanpassing en performance analyseTip: Gebruik SQL Sync om je data te replicerenTip: Gebruik DacServices om een back-up van je database temaken
  24. 24. Online SQL Database management
  25. 25. SQL Data Sync
  26. 26. Backup database naar blob storageCloudBlob backupFile = backupContainer.GetBlobReference(storageName);// Backup kan alleen naar een lokale fileLocalResource localResource = RoleEnvironment.GetLocalResource(LocalStorageName);string filename = localResource.RootPath + backupFile.Name;DacServices services = new DacServices(connectionString);services.ExportBacpac(filename, "SeriousRequest");backupFile.Properties.ContentType = "binary/octet-stream";backupFile.UploadFile(filename); // Sla de back-up op blob storageFile.Delete(filename); // Ruim de locale file wel weer op!
  27. 27. Shared Cache (AppFabric Cache)• Gedistribueerde cache die overal vandaan te benaderen is, dus ook buiten je deployment• Dit in tegenstelling tot de nieuwe cache role• Gelimiteerd in aantal connecties en grootteTip: Connecties moet je disposen!Tip: Je kan ook data cachen in (JSON) bestandenop Blob Storage, dit is véél goedkoper
  28. 28. Wat zijn voordelen van ontkoppelen?• Vlak voor het begin van de actie kwam een verzoek voor 2 nieuwe koppelingen – UPC – MyOrder• Voor elke import was het alleen nodig om een CSV-regel om te zetten in een bericht op de queue
  29. 29. Wat zijn uitdagingen bij ontkoppelen?• Hoe garandeer je een unieke sleutel?• Hoe voorkom je dat worker roles hetzelfde tegelijkertijd willen uitvoeren?
  30. 30. Hoe garandeer je een unieke sleutel?• Guid.NewGuid() is redelijk uniekUitdaging:“In het financiële systeem heb je maar 5 karaktersaan ruimte voor je unieke sleutel”• Decimaal zijn dat maar 100.000 unieke waarden• Base36 levert 365 = 60.466.176 unieke waarden
  31. 31. Hoe garandeer je een unieke sleutel?• Met een queue!Let op! Berichten op de queue verlopen namaximaal 7 dagenTip: De NuGet package SnowMaker geeftbatches van unieke id’s uit aan nodes engebruikt een blob als lockmechanisme
  32. 32. SnowMaker sequence diagram
  33. 33. Betaalkenmerken genererenprivate void AddPaymentIds(CloudQueue cloudQueue){ for (int i = 0; i < Threshold; i++) { long newId = SnowMaker.NextId("PaymentId"); Alphadecimal paymentId = new Alphadecimal((ulong)newId); string value = paymentId.ToString("5"); cloudQueue.AddMessage(new CloudQueueMessage(value), TimeSpan.FromDays(7)); }}
  34. 34. Worker roles niet hetzelfde, tegelijk laten doen• Je wil de Worker role redundant uitvoeren voor availability, maar dat betekent ook dat alle functionaliteit door meerdere servers tegelijk wordt uitgevoerdTip: Zet je staging omgeving uitTip: Gebruik een lease op een centrale Blob Fileals lock-mechanisme
  35. 35. Gebruik lease als lock-mechanismeCloudBlockContainer lockContainer = blobClient.GetContainerReference("lock");CloudBlob lockFile = lockContainer.GetBlobReference("emailLock");string leaseId = lockFile.AcquireLease(60);ProcessEmail(lockFile, leaseId);lockFile.ReleaseLease(leaseId);
  36. 36. Lock file op Blob Storage
  37. 37. Stuur een e-mail met SendGridprivate void ProcessEmail(CloudBlob lockFile, string leaseId){ var transportInstance = Web.GetInstance( new NetworkCredential(username, password)); var mailMessage = Mail.GetInstance(); mailMessage.AddTo(toAddress); mailMessage.From = new MailAddress(fromAddress); mailMessage.Subject = subject; mailMessage.EnableGoogleAnalytics("sr12", "email", "", "", ""); mailMessage.Html = replacedBody; transportInstance.Deliver(mailMessage); lockFile.RenewLease(leaseId, 60);}
  38. 38. Verzonden e-mails met SendGrid 65.000 30.000 25.000 15.000 9.000 10.000 4.000 200 750 300 100 150 75 50dec-16 dec-17 dec-18 dec-19 dec-20 dec-21 dec-22 dec-23 dec-24 dec-25 dec-26 dec-27 dec-28 dec-29 Requests Opens
  39. 39. De flow van een aanvraag Queue Betaalkenmerken Queue Betaling Bevestiging Request Confirm Worker rol Database
  40. 40. De flow van de Rode Kruis beheersite Database Worker rol Cache Blob storage
  41. 41. De flow van de 3FM beheersite Database Worker rol Cache
  42. 42. VEEL BEZOEKERSHoe voorkom je manipulatie van data?Hoe kan je dataverkeer beperken?Hoe kan je meerdere form-factors ondersteunen?
  43. 43. Hoe ondersteun je alle browsers?• Oude browsers kunnen best met HTML5 en CSS3 overweg zolang we “progressive enhancement” toepassen• jQuery zorgt voor een goede abstractie om tegen de DOM aan te programmeren• Wel een eigen versie van de jquery.validate plugin om met IE7 backwards compatibility mode van Internet Explorer overweg te kunnen
  44. 44. Internet Explorer 10 Internet Explorer 7
  45. 45. Hoe bewaar je state in de client?• Met een wizard moet je de state meenemen naar de andere stappen• Gebruik de MvcSerializer uit MVC Futures• State kan alleen signed, of encrypted en signed worden
  46. 46. Serialize State (View.cshtml)@using (Html.BeginForm()){ @Html.Serialize("state", Model, SerializationMode.EncryptedAndSigned)}
  47. 47. Serialize State (Output)<input name="state" type="hidden"value="CC1D907C69732C67A0850F661D39D576F4FAC714ABD06A93FA40BE430125196A2C71A2D74E6A2FDB23E796A6198BA9A3BDBE88D194256F804173C08029BE15D1082580898EF6B4DD67AB14E3523B6D1AA81E880C7E2832A34BC9C86B1565F40EC34C5D993727EAC3ACAC25976DF63AF4EE28FA20B082E5D1A1BDE492397206670A6A6F27D25AF4E432A2D682BF7E37ACF2B110B8B317CA69EA1F5247F076B8095D950B77D48B7307FAF0BAE020F57C57C00F07FB63F96AA31A7DB8C7F26032208CA7ABC9AA28F936501FA01B5BCF388B30A2A26188FE7B4BF6B1C8F50EB7480E66EDFAD2D8FCD249A41F5F16D2B9ABF27994DDAA3B23B3106DADDF81C8D5AD5580C08074C815A02030FFE9C3E20A16034F159F2F63DD29A6AF99B7CBB69FEA0CC30F84A81ACE28C6A7C8548B4AB3F7FF07614D920C82DFEDD0E1FE17675CA1E6E3E3955E7983EFC94DB9D7775C624D9FF637BA84932BDDEA9770D57C9F11957599B4B10915E3B9FB6FE85BBE133000F75B68F9E32EEC6A4412D1F72767D39CC1211846003767BFA43F3FE63F0858BD509B70407517C4DB34DFC8A20E039026BDE6D0BB4F871D4E9A18CD405C83E562756A04304C7AE7BA4721972BA2E41F0D963291AAF02FD2AA51B278CB0F5C7DF6C6752BDE7C642F98875CA1D9F4433CC85DBD0FA56DD2F2983704A63039C1313BFCE62B1D1C9A28E16AB63BA109EF2BA4590AA0810D5B587B5636409B87FC4581142DA163A600EC8255EE82BFEFCE774BFC03F79FD75D51EA3BB3E526EBF02CBB027E0510DA27152D87AE01C5C25724DB8A62B8EE81BD346189574D87D63F84065778D80267D133441A4B8453B5497E467AB766964EBBC53735848CC6602D90B3C6C26C1253213F85546E2625D6CF4776EFCDD176CB3D85597D5AF6D007136DAF736E5DE787C052CC23E20BE832CB0264AD5DB0B3C5F3199FDAD983138CB23DBC8CA538855BD0EF9E7C5A2725942DB236D819720540AE53E67B397CD6CECD286A1DD7FAD1E8526E6C6A452B044A11FE305CA60095900C51F2E0D2CC0C8E6E4F899E8E65AD734538F08E7B32F1EA7FCA78846BF0C87A7EB55A883D8E090C8899815AEA76F831F9489507D46ADA89B256C317168CC7023767DB2C69FF2B22A424E1D3349CE5104D121E32F48ABDB0C789411670FEB373DB7FA272040959D04A6082AC9A3417E00B833CE02DC49B452D3F83515F312FCE24F1DF7C9E18E15A78A7DDC06502A57011DAB9E3C2E869FB3FBC0BF8CE06C2BA943EEE1C6910DE4BDC17498A00618CFE621EA6222FC6E3F981C3F7594783A7AB74D67D0EECA3E474A8B1CFF3EB55A18B8531C230D89EA315B3FF69A04CD8D8575DFB641497A630E4DABEC3E58EC9A44D5F5DEAC64559EB21169A9D2CC4C7A923C4F40E275AFD4283299365D9091A832783689A1BFA8FC304B65E085DE35FD2087BC159A0A3A58C92D67BBD8455D643686BF36C0FA915F0CCC63499177585442FF7ED3D1AD63D5CAD4CE743B07FD4322F7CFFEC35722E1AE37EE4B994FB3E43D7874929AC33E1634FB469C323F5509A5158FDF2A428767589D46989BAD0CF35CB" />
  48. 48. Serialize State (Controller.cs)[HttpPost, ValidateInput(false)]public ActionResult Wizard(WizardViewModel updatedModel, string state){ ModelState.Clear(); var model = (WizardViewModel)new MvcSerializer().Deserialize(state, SerializationMode.EncryptedAndSigned); model.Betaling = updatedModel.Betaling; if (!TryValidateModel(model.Betaling)) { break; } return View(model);}
  49. 49. Hoe voorkom je manipulatie van data?• Serverside validatie is een must• Clientside validatie is een nice-to-have• Voorkom cross-site request forgery met een AntiForgeryTokenTip: Indien de pagina in een IFrame komt (bijv.Facebook) is er een extra handeling nodig
  50. 50. Anti Forgery (View.cshtml)@using (Html.BeginForm()){ @Html.AntiForgeryToken()}
  51. 51. Anti Forgery (Output)<input name="__RequestVerificationToken" type="hidden"value="4U_xgmVmpHiOZSBnkpV8mPulShQ5nRvK2aAmwV31xLPqJQ721W67UTvQnk00Jal97mxzkLo8gn81tDI4PnQ2bNg4abg1" />Cookie:__RequestVerificationToken=WgBwIxKirHqEO-L94t0T2FpFJcePf2xinI6MFxi6J5NuCQRExvj_duLa7KWGlARQ5VaB20siCSgxL_e0OOWrEnIcAYQ1
  52. 52. Anti Forgery (Controller.cs)[HttpPost, ValidateAntiForgeryToken]public ActionResult Wizard(WizardViewModel updatedModel, string state){ SetP3PCompactPolicy(); return View(model);}
  53. 53. Hoe beveilig je een site met SSL?• Je wil voorkomen dat persoonlijke gegevens onderschept kunnen worden• Je configureert de certificaten in je package in Visual Studio• SSL certificaten moet je zelf uploaden, deze gaan niet mee met je packageTip: Je moet de hele chain uploaden
  54. 54. Visual Studio 2012 – Role properties Azure Management Portal
  55. 55. Hoe kan je dataverkeer optimaliseren?• Gebruik in je rol IIS 7.5 of 8 voor “gratis” gzip compressie• Compressie van JSON is niet standaard geactiveerd, dit kan je activeren met een startup taskTip: Gebruik een CDN voor librariesTip: Controleer je site met een tool, bijvoorbeeldGoogle PageSpeed Insights
  56. 56. ServiceDefinition.csdef<!-– Windows Server 2008 R2 --><ServiceConfiguration ... osFamily="2" ...> <WebRole ...> <Startup> <!– Registreer start-up task --> <Task commandLine="EnableCompression.cmd" executionContext="elevated" taskType="simple" /> </Startup> </WebRole></ServiceConfiguration>
  57. 57. EnableCompression.cmd%windir%system32inetsrvappcmd set config /section:urlCompression/doDynamicCompression:True /commit:apphost%windir%system32inetsrvappcmd set config -section:system.webServer/httpCompression/+"dynamicTypes.[mimeType=application/json; charset=utf-8,enabled=True]"/commit:apphostexit /b 0
  58. 58. Hoe kan je meerdere form-factors ondersteunen?• Met ASP .NET MVC 4 zijn de “DisplayModes” geïntroduceerd• De juiste view wordt dynamisch bepaald aan de hand van condities en de bestandsnaam• Werkt ook voor partial viewsLet op! Er zit een bug in de RTM release, installeerde “Microsoft.AspNet.Mvc.FixedDisplayModes”NuGet package
  59. 59. “Desktop”
  60. 60. “Facebook”
  61. 61. “Mobile”
  62. 62. DisplayMode (Global.asax.cs)// Vervang de standaard Mobile DisplayModeIDisplaymode currentMobileMode = DisplayModeProvider.Instance.Modes .First(dm => dm.DisplayModeId == DisplayModeProvider.MobileDisplayModeId);DisplayModeProvider.Instance.Modes.Remove(currentMobileMode);DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Mobile") { ContextCondition = MobileCheck });
  63. 63. DisplayMode (Global.asax.cs)public bool MobileCheck(HttpContextBase context){ HttpRequestBase request = context.Request; if (request.Browser.IsMobileDevice) { // Standaard detecteert .NET een tablet ook als een mobile device bool isTablet = request.UserAgent.Contains("iPad") || (request.UserAgent.Contains("Android") && !request.UserAgent.Contains("mobile")); // Indien de gebruiker op de link “switch naar desktopsite” geklikt heeft bool forceDesktop = request.QueryString.AllKeys.Contains("mode") && request.QueryString["mode"] == "desktop"; return !isTablet && !forceDesktop; } return false;}
  64. 64. 3 DisplayModes voorde _Layout.cshtml file
  65. 65. OPSCHALENWelke grootte VM kies je?Wat moet je weten bij het schalen?Hoe weet je dat je moet schalen?Hoeveel servers hadden we nodig?
  66. 66. Welke grootte VM kies je?Extra Small Small MediumShared CPU 1 CPU 2 CPU768 MB 1,75 GB 3,5 GB20 GB 225 GB 490 GB5 Mbps 100 Mbps 200 Mbps€0,015/uur €0,090/uur €0,1790/uur 6x Extra Small 2x Small
  67. 67. Wat moet je weten bij het schalen?• Voor een hogere availability kan je beter meerdere kleinere VM’s gebruiken• Je hebt minimaal 2 VM’s nodig voor de 99,95% garantie• Je hebt minimaal 3 VM’s nodig voor de 99,95% garantie als je zelf een update uitrolt• Updates worden standaard server-voor-server uitgeroldTip: Het gaat veel sneller als je alle machines tegelijkupdatet maar doe dit alleen in staging
  68. 68. 3 DisplayModes voor de _Layout.cshtml file
  69. 69. Hoe weet je dat je moet schalen?• Gebruik de Windows Azure Diagnostics – Trace Listener – Performance counters – IIS Logs – Event logs – Crash dumps• Gebruikt blob en/of table storageTip: Gebruik hiervoor een apart storage account
  70. 70. Performance logs opgeslagen op Blob Storage Paraleap AzureWatch
  71. 71. New Relic: Time consumption Response times
  72. 72. Management Portal: Blob Performance counter web role
  73. 73. Hoe is het verloop in donaties?
  74. 74. Hoeveel servers hadden we nodig? 2011 2012Vraag een plaat aan 12 3Directe Donatie 10 4Callcenter 3 3Beheersite NRK 2 2Beheersite 3FM 2 2Worker roles 4* 2Staging 2 6Piek 35 22
  75. 75. DE KOSTENWat heeft de hosting ons gekost?Wat kost een donatie?
  76. 76. Wat heeft de hosting in 2011 gekost?Compute 598,29Shared Cache 173,39SQL Database 10,11Network (Zone 1) 16,57Network (Zone 2) 2,91Storage Capacity 0,10Storage Transactions 3,97Totaal €805,34 Totaal €853,10
  77. 77. Hoe kunnen we 2011 en 2012 vergelijken? 1 miljoen 45%Pageviews 2011 Groei 2012 100.000 500 per minuutDonaties 2011 Piek donaties 2012 € 5,50 per 1000 donaties € 4,10 per 1000 donaties2011 2012
  78. 78. Daarom voor Windows Azure kiezen! Beschikbaarheid applicaties Applicaties hebben elkaar niet beïnvloed, geen down time Dagelijks updates uitgerold Veel bezoekers op de website 1.500.000+ pageviews 160.000+ donaties verwerkt Grote toestroom tijdens actie Makkelijk kunnen opschalen tot 35 servers Korte levensduur Lage hosting kosten
  79. 79. LINKJES
  80. 80. LinkjesEmail michael.hompus@winvision.nl Video’sTwitter Microsoft Showcase Serious Request 2011 https://twitter.com/eNeRGy164 http://aka.ms/xjvkqaBlog Microsoft Showcase Serious Request 2012 http://blog.hompus.nl http://aka.ms/xa1q10Winvision Podcast http://www.winvision.nl DotNed Podcast3FM Serious Request http://link.hompus.nl/dotnedpodcast http://seriousrequest.3fm.nlNederlandse Rode Kruis http://www.rodekruis.nl
  81. 81. LinkjesNuGet ServicesSnowMaker SendGrid http://nuget.org/packages/SnowMaker http://sendgrid.comASP .NET MVC Fixed DisplayModes NewRelic http://nuget.org/packages/Microsoft.AspNe http://newrelic.com t.Mvc.FixedDisplayModes AzureWatch http://www.paraleap.com/AzureWatchToolsAzure Storage Explorer http://azurestorageexplorer.codeplex.comCloud Storage Studio http://www.cerebrata.com/Products/CloudS torageStudio
  82. 82. VRAGEN?
  83. 83. BEDANKT!Vul een feedback formulier in, dit wordt gewaardeerd!

×