windows azure multi-tenant applicatie
DESCRIPTION
Windows Azure Multi-tenant applicatie. Bouwen van een multi-tenant applicatie in de praktijk. Edo van Asseldonk. Ordina Microsoft Solutions. Agenda. Introductie / Architectuur URL strategie / Tenant id Data opslag (SQL Azure, Table Storage) Maatwerk (UI / Business Logica) Cache - PowerPoint PPT PresentationTRANSCRIPT
Windows AzureMulti-tenant applicatieBouwen van een multi-tenant applicatie in de praktijk
1
Edo van AsseldonkOrdina Microsoft Solutions
3Agenda
• Introductie / Architectuur
• URL strategie / Tenant id
• Data opslag (SQL Azure, Table Storage)
• Maatwerk (UI / Business Logica)
• Cache
• Access Control Service
4MultitenancyDefinitie
5MultitenancyDefinitie
…single instance of software serving multiple clients (tenants)…
…een enkele softwareinstantie die meerdere klanten bedient…
6MultitenancyDefinitie
7MultitenancyDefinitie
8MultitenancyDefinitie
Iedere tenant heeft:
• eigen data
• eigen opmaak
• eigen business logic
• gemeenschappelijke codebase
• gemeenschappelijk datamodel
9ArchitectuurOverview
Casus / Demo
10ArchitectuurOverview
Webrole SQL Azure
Table Storage
ACS
11ArchitectuurOverview
Webrole SQL Azure
Table Storage
ACS
12ArchitectuurOverview
Eisen:
• Tenantapplicatie bereikbaar via eigen url
• Eén database schema
• Eigen stijl per tenant
• Self service
• Share everything
URL Strategie / Tenant id
14URL Strategie / Tenant idOpties
Optie 1: Tenant bepalen a.d.h.v. ingelogde gebruiker
http://www.MyMultitenantApp.nl/Home
Geen referentie naar naam van tenant
15URL Strategie / Tenant idOpties
https://www.chatter.com/nl/
16URL Strategie / Tenant idOpties
https://eu1.salesforce.com/home/home.jsp
17URL Strategie / Tenant idOpties
Optie 2: TenantId in URL
http://www.MyTenantApp.nl/Home?tenantId=17
http://www.MyTenantApp.nl/17/Home
http://www.MyTenantApp.nl/A219F47cd-898B/Home
http://www.MyTenantApp.nl/WazugNL/Home
18URL Strategie / Tenant idOpties
Optie 3: Tenant bepalen a.d.h.v. URL
http://tenant1.MyTenantApp.nl/Home
http://tenant2.MyTenantApp.nl/Home
19URL Strategie / Tenant idOpties
Optie 3: Tenant bepalen a.d.h.v. URL
http://tenant1.MyTenantApp.nl/Home
http://tenant2.MyTenantApp.nl/Home
http://www.customdomain.nl/
-> verwijst naar http://tenant2.MyTenantApp.nl
20URL Strategie / Tenant idOpties
http://gaming.stackexchange.com/
21URL Strategie / Tenant idOpties
http://programmers.stackexchange.com/
22URL Strategie / Tenant idOpties
http://www.stackoverflow.com/
23URL StrategieIn Windows Azure
[MyMultitenantApp].cloudapp.net
24URL StrategieIn Windows Azure
[MyMultitenantApp].cloudapp.net
[Tenant].cloudapp.net
[Tenant].[MyMultitenantApp].cloudapp.net
25URL StrategieIn Windows Azure
[MyMultitenantApp].cloudapp.net
Domain name: MyMultitenantApp.nl
26URL StrategieIn Windows Azure
[MyMultitenantApp].cloudapp.net
Domain name: MyMultitenantApp.nl
URLs:
http://tenant1.MyMultitenantApp.nl
http://tenant2.MyMultitenantApp.nl
http://www.customdomain.nl
Data
Database
28DatabaseIntroductie
Alle tenants in dezelfde database
Twee issues:
• Limiet aan databasegrootte
• Limiet aan verwerkingscapaciteit (CPU, I/O etc.)
29DatabaseIntroductie
Oplossing:
• Database splitsen
30DatabaseIntroductie
Oplossing:
• Database splitsen -> SQL Azure Federations
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
DatabaseIntroductie
45SQL Azure FederationsRoot database
Root DBRoot
Table1Root
Table2
RootTable…
46SQL Azure FederationsCreate federation
Member DBRoot DBRoot
Table1Root
Table2
RootTable…
47SQL Azure FederationsCreate tables
Member DBRoot DBRoot
Table1Root
Table2
RootTable…
MemberTable1
Member Table2
Member Table…
48SQL Azure FederationsSplit federation member
Member DBRoot DBRoot
Table1Root
Table2
RootTable…
MemberTable1
Member Table2
Member Table…
49SQL Azure FederationsSplit federation member
Member DBRoot DBRoot
Table1Root
Table2
RootTable…
MemberTable1
Member Table2
Member Table…
50SQL Azure FederationsSplit federation member
Member DBRoot DBRoot
Table1Root
Table2
RootTable…
MemberTable1
Member Table2
Member Table…
Member DBMemberTable1
Member Table2
Member Table…
Member DBMemberTable1
Member Table2
Member Table…
51SQL Azure FederationsSplit federation member
Root DBRoot
Table1Root
Table2
RootTable…
Member DBMemberTable1
Member Table2
Member Table…
Member DBMemberTable1
Member Table2
Member Table…
52SQL Azure FederationsVoordelen
•DB Size van max 150 GB -> 75 TB
•CPU Cores en I/O verveelvoudigd
•Real-time
•Geen downtime
53SQL Azure FederationsVoordelen
•DB Size van max 150 GB -> 75 TB
•CPU Cores en I/O verveelvoudigd
•Real-time
•Geen downtime
54SQL Azure FederationsVoordelen
•DB Size van max 150 GB -> 75 TB
•CPU Cores en I/O verveelvoudigd
•Real-time
•Geen downtime
55SQL Azure FederationsVoordelen
•DB Size van max 150 GB -> 75 TB
•CPU Cores en I/O verveelvoudigd
•Real-time
•Geen downtime
56SQL Azure FederationsKosten
Sinds halverwege februari 2012 prijsverlaging.
Tot 78% goedkoper.
Hoe meer tenants per member database, hoe goedkoper.
Eén database Meerdere databasesFORMAAT KOSTEN FORMAAT KOSTEN
10 GB $45,95 p/m 10 x 1 GB 10 x $9.99 = $99,90 p/m
57SQL Azure FederationsKosten
Sinds halverwege februari 2012 prijsverlaging.
Tot 78% goedkoper.
Hoe meer tenants per member database, hoe goedkoper.
Eén database Meerdere databasesFORMAAT KOSTEN FORMAAT KOSTEN
10 GB $45,95 p/m 10 x 1 GB 10 x $9.99 = $99,90 p/m
50 GB $125,88 p/m 10 x 5 GB $259,80 p/m
58SQL Azure FederationsKosten
Sinds halverwege februari 2012 prijsverlaging.
Tot 78% goedkoper.
Hoe meer tenants per member database, hoe goedkoper.
Eén database Meerdere databasesFORMAAT KOSTEN FORMAAT KOSTEN
10 GB $45,95 p/m 10 x 1 GB 10 x $9.99 = $99,90 p/m
50 GB $125,88 p/m 10 x 5 GB $259,80 p/m
50 GB $125,88 p/m 50 x 1 GB $499,50 p/m
59SQL Azure FederationsKosten
Sinds halverwege februari 2012 prijsverlaging.
Tot 78% goedkoper.
Hoe meer tenants per member database, hoe goedkoper.
Eén database Meerdere databasesFORMAAT KOSTEN FORMAAT KOSTEN
10 GB $45,95 p/m 10 x 1 GB 10 x $9.99 = $99,90 p/m
50 GB $125,88 p/m 10 x 5 GB $259,80 p/m
50 GB $125,88 p/m 50 x 1 GB $499,50 p/m
150 GB $225,78 p/m 150 x 1 GB $1498,50 p/m
60SQL Azure FederationsSamenwerking met Entity Framework
string federationCmdText =
"USE FEDERATION tenant_federation (tenantId = [id])
WITH FILTERING=ON, RESET";
((IObjectContextAdapter)myDatabaseContext).ObjectContext.Connection.Open();
_databaseContext.Database.ExecuteSqlCommand(federationCmdText);
var query = from o in _myDatabaseContext.Orders where o.TenantId = id select o;
61SQL Azure FederationsGeen Federations in SQL Server 2008R2
string federationCmdText =
"USE FEDERATION tenant_federation (tenantId = [id])
WITH FILTERING=ON, RESET";
((IObjectContextAdapter)myDatabaseContext).ObjectContext.Connection.Open();
_databaseContext.Database.ExecuteSqlCommand(federationCmdText);
var query = from o in _myDatabaseContext.Orders
where o.TenantId = id
select o;
62SQL Azure FederationsGeen where-clause nodig in gefilterde verbinding
string federationCmdText =
"USE FEDERATION tenant_federation (tenantId = [id])
WITH FILTERING=ON, RESET";
((IObjectContextAdapter)myDatabaseContext).ObjectContext.Connection.Open();
_databaseContext.Database.ExecuteSqlCommand(federationCmdText);
var query = from o in _myDatabaseContext.Orders
where o.TenantId = id
select o;
63SQL Azure FederationsEntity Framework: geen support parallelle queries
64ArchitectuurOverview - > Table Storage
Webrole SQL Azure
Table Storage
ACS
65Table storageMeten en Afrekenen
Afrekenen op basis van verbruik
(aantal page requests, aantal objecten etc.)
Verbruik inzichtelijk maken voor tenants
Statcounter.com is niet geschikt voor multitenancy
Maatwerk
67Maatwerk
Maatwerk per tenant op twee gebieden:
• User Interface
• Business Logic
68MaatwerkUser Interface
Tenant-logo
<div id="header">
<a href="/" class="logo" style="background: url(@ViewBag.Logo) no-repeat;">
69MaatwerkUser Interface
Tenant-logo
<div id="header">
<a href="/" class="logo" style="background: url(@ViewBag.Logo) no-repeat;">
Niet doen op deze manier:
Kleuren
Fonts
Posities van html-elementen
Etc.
70MaatwerkUser Interface - Themes
71MaatwerkUser Interface - Themes
72MaatwerkUser Interface - Themes
public class ThemesViewEngine : BuildManagerViewEngine{ private readonly string _themeName;
public ThemesViewEngine(string themeName) { _themeName = themeName; }
protected override IView CreateView( ControllerContext controllerContext, string viewPath, string masterPath) { masterPath = string.Format("~/Themes/{0}/_Layout.cshtml", _themeName);
return new RazorView(controllerContext, viewPath, masterPath, …); }
73MaatwerkUser Interface - Themes
public class ThemesViewEngine : BuildManagerViewEngine{ private readonly string _themeName;
public ThemesViewEngine(string themeName) { _themeName = themeName; }
protected override IView CreateView( ControllerContext controllerContext, string viewPath, string masterPath) { masterPath = string.Format("~/Themes/{0}/_Layout.cshtml", _themeName);
return new RazorView(controllerContext, viewPath, masterPath, …); }
74MaatwerkUser Interface - Themes
public class ThemesViewEngine : BuildManagerViewEngine{ private readonly string _themeName;
public ThemesViewEngine(string themeName) { _themeName = themeName; }
protected override IView CreateView( ControllerContext controllerContext, string viewPath, string masterPath) { masterPath = string.Format("~/Themes/{0}/_Layout.cshtml", _themeName);
return new RazorView(controllerContext, viewPath, masterPath, …); }
75MaatwerkUser Interface - Themes
public class ThemesViewEngine : BuildManagerViewEngine{ private readonly string _themeName;
public ThemesViewEngine(string themeName) { _themeName = themeName; }
protected override IView CreateView( ControllerContext controllerContext, string viewPath, string masterPath) { masterPath = string.Format("~/Themes/{0}/_Layout.cshtml", _themeName);
return new RazorView(controllerContext, viewPath, masterPath, …); }
76MaatwerkUser Interface - Themes
public class UseThemesViewEngine : ActionFilterAttribute{ public ITenantSettings Settings { get; set; }
public override void OnActionExecuting( ActionExecutingContext context) { ViewEngines.Engines.Clear();
var engine = new ThemesViewEngine(Settings.ThemeName);
ViewEngines.Engines.Add(engine)); }}
77MaatwerkUser Interface - Themes
public class UseThemesViewEngine : ActionFilterAttribute{ public ITenantSettings Settings { get; set; }
public override void OnActionExecuting(…) { ViewEngines.Engines.Clear();
var engine = new ThemesViewEngine(Settings.ThemeName);
ViewEngines.Engines.Add(engine)); }}
78MaatwerkUser Interface - Themes
Public class HomeController : Controller{ [UseThemesViewEngine] public ActionResult Index() { }}
79MaatwerkUser Interface - Themes
public class UseThemesViewEngine : ActionFilterAttribute{ public ITenantSettings Settings { get; set; }
public override void OnActionExecuting( ActionExecutingContext context) { ViewEngines.Engines.Clear();
var engine = new ThemesViewEngine(Settings.ThemeName);
ViewEngines.Engines.Add(engine)); }}
80Maatwerk
Maatwerk per tenant op twee gebieden:
• User Interface
• Business Logic
81MaatwerkBusiness Logic
Probleem
• Tenant settings moeten continue doorgegeven worden
• If-statements en switch-statements door alle code verweven
82MaatwerkBusiness Logic
var data = Foo.Bar(TenantSettings)
If(settings.IsGoldPartner) …else …
TenantSettings
Business Logic layer
MVC Controller
83MaatwerkBusiness Logic
var data = Foo.Bar(TenantSettings)
If(settings.IsGoldPartner) …else …
TenantSettings
Business Logic layer
MVC Controller
84MaatwerkBusiness Logic
var data = BLL.GetData(TenantId)
return DA.GetData(TenantId)
from t in tablewhere tenantid == TenantIdselect t
select * from tablewhere tenantid = TenantId
TenantId
TenantId
TenantId
Business Logic layer
MVC Controller
Data Access
Database
85MaatwerkBusiness Logic
var data = BLL.GetData(TenantId)
return DA.GetData(TenantId)
from t in tablewhere tenantid == TenantIdselect t
select * from tablewhere tenantid = TenantId
TenantId
TenantId
TenantId
Business Logic layer
MVC Controller
Data Access
Database
86MaatwerkBusiness Logic
public class BusinessLogic{ public void DoeIets(string messageText) { var message = CreateMessage(messageText);
var tenantSettings = TenantSettingsHelper.GetSettings();
if(tenantSettings.UseSpamFilter) { if(tenantSettings.SpamFilterType == Fast) { spamfilter = new FastSpamfilter().Execute(message); } else if(TenantSettings.SpamFilterType == Slow) { spamfilter = new SlowSpamfilter().Execute(message); } }
SaveMessage(message); }
87MaatwerkBusiness Logic
IoC container (autofac):builder.Register(c => NewSpamfilter()) .As<ISpamfilter>;
private Ispamfilter NewSpamfilter(){ if(!TenantSettings.UseSpamFilter) return new NoSpamfilter();
if(TenantSettings.SpamFilterType == Fast) return new FastSpamfilter();
else if(TenantSettings.SpamFilterType == Slow) return new SlowSpamfilter();
88MaatwerkBusiness Logic
public class BusinessLogic{ ISpamfilter _spamfilter; public Messenger(ISpamfilter spamfilter) { _spamfilter = spamfilter }
public void DoeIets(string messageText) { var message = CreateMessage(messageText); _spamfilter.Execute(message); SaveMessage(message); }
89Cache
App Fabric Cache doet de basics
Wissen van totale cache niet mogelijk
Geen persistentie
Geen inzicht in bytes per tenant
Versienummer in cachekey:
Bijvoorbeeld [tenantId]_[objecttype]_[versienr]_[objectId]2ED669F2-9EBC-4248-8BE2-56CDFDA89B75_employee_v1.1_924F32EA-7793-46C1-986E-69FF0857F39C
2ED669F2-9EBC-4248-8BE2-56CDFDA89B75_employee_v1.2_924F32EA-7793-46C1-986E-69FF0857F39C
Cache
90Cache
• App Fabric Cache doet de basics, maar niet meer dan dat.
• Wissen van totale cache niet mogelijk.
• Geen persistentie.
• Geen inzicht in bytes per tenant.
Versienummer in cachekey:
Bijvoorbeeld [tenantId]_[objecttype]_[versienr]_[objectId]
91Access Control Service
Access Control
92Access Control ServiceIntro
Iedere tenant -> Relying Party Application in ACS
Management Service (beschikbaar als Odata Service)
Geen gebruikcijfers
93Access Control ServiceAudienceMode
Na inloggen stuurt ACS een token naar de website.
Check (url in token == AllowedAudienceUri)
Uitzetten!
<microsoft.identityModel>
<service>
<audienceUris mode="Never">
94ContactBereikbaar voor vragen
Edo van Asseldonk
Email: [email protected]
Twitter: @edovanasseldonk
Blog: http://edo-van-asseldonk.blogspot.com
95
www.ordina.nl