460 likes | 531 Views
Windows communication foundation. Session és példányosítás. Példányosítási módok. példányosítás. A WCF felelős egy bejövő üzenet egy adott szolgáltatás példányhoz kötéséért. Amikor egy kérés érkezik, a WCF eldönti, hogy egy létező szolgáltatás példány fel tudja-e dolgozni a kérést.
E N D
Windows communicationfoundation Session és példányosítás
példányosítás • A WCF felelős egy bejövő üzenet egy adott szolgáltatás példányhoz kötéséért. • Amikor egy kérés érkezik, a WCF eldönti, hogy egy létező szolgáltatás példány fel tudja-e dolgozni a kérést. • Ennek eldöntésére egy döntési mátrixot állít fel.
Példánykezelés • Minden szolgáltatásnak van egy példánykezelési modellje. • Három ilyen modell létezik: • az „egyke” (single), amelyben egyetlen CLR objektum szolgálja ki az összes klienst; • a „hívásonkénti” (per call), melyben minden egyes klienshívás kiszolgálására új CLR-objektum jön létre; • és a „munkamenetenkénti” (per session), melynél minden egyes munkamenethez egy kiszolgáló CLR objektum jön létre. • A példánykezelési modell kiválasztása függ az alkalmazás által támasztott követelményektől és a szolgáltatás tervezett használatától.
Példánykezelés 2. • A példányosítás módjának meghatározása a szolgáltatás oldalon történik, azon belül is a szolgáltatás viselkedésének (Service Behavior) leírójában. • Ez azt jelenti, hogy a szolgáltatás összes végpontjában ugyanez a példányosítási mód lesz érvényes.
Hívásonkénti (per call) mód • Hívásonként módban minden egyes kérésnek saját szolgáltatás példánya van. • A kliens egy proxyn keresztül intéz kéréseket a szolgáltatás felé. • Amikor a kérés megérkezik a szolgáltatás hoszthoz, a hoszt a szolgáltatást implementáló osztályból létrehoz egy új példányt. • Miután a kérés befejeződött és a válasz is visszaküldésre került a kliens felé, a példányra már nincs szükség. • Minden szolgáltatás leíró osztálynak implementálni kell az IDisposable interfészt. Amikor az adott példány elvégezte a dolgát, meghívódik a Dispose metódus.
Hívásonkénti (per call) mód • A WCF-ben a hívásonkénti mód az alapértelmezett. • Ennek egyik oka, hogy így a fejlesztőnek nem kell foglalkoznia a konkurens feldolgozással. • Viszont számos hátránya is van ennek a módnak: • Teljesítmény szempontjából nem előnyös • Ha egy szolgáltatás példány zárol egy erőforrást a teljes életciklusa idejére, akkor az az erőforrás a többi példány számára elérhetetlenné válik. (pl. fájl, adatbázis, stb.) • A problémák kiküszöbölésére léteznek megoldások.
Hívásonkénti (per call) mód • Az egyik megoldás egy proxy objektum rendelése a szolgáltatáshoz. • Az alapértelmezett programozási modell szerint, ha egy adott szolgáltatás példányra nincs szükség, akkor arra meghívódik a Dispose metódus. Ez érvényteleníti a referenciát, ami nem mindig kívánatos. • A WCF-ben a kliens a proxyra tart fent egy referenciát, ami nem szűnik meg minden hívás után. • A proxy feladata az, hogy a szolgáltatás példányt újra létrehozza, ha szükséges.
Hívásonkénti (per call) mód • A példányosítási mód megadási a szolgáltatás oldalán történik: [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] classUpdateService: IUpdateService{...} • Habár elméletben a kliensnek nem kell tudnia a példányosítás módjáról, a hívásonkénti példányosítási mód azt jelenti, hogy a hívások között nem őrződik meg az állapot. • Ha bármilyen okból mégis szükség van az állapot megőrzésére, a szolgáltatás feladat biztosítani azt. (pl. adatbázisba mentés)
Munkamenetenkénti (per session) mód • Az előbbiekben már felmerült az igény a hívások között az állapotok megőrzésére, így létezik egy munkamenetenkénti (per session) példányosítási mód is. • A WCF képes egy privát session-t létesíteni a kliens és a szolgáltatás példány között. • Minden klienshez az első kérésekor hozzárendelődik egy szolgáltatás példány és elkezdődik egy munkamenet. • Minden további kérés ennek a munkamenetnek a része lesz, és ugyanaz a szolgáltatás példány fogja végrehajtani azt.
Munkamenetenkénti (per session) mód • A munkamenetenkénti mód beállításához 2 dolog szükséges: • A szerződés rögzíti, hogy szükséges a munkamenet. Ez azért fontos, mert a kliensnek tartalmazni kell egy azonosítót, hogy megtalálja az adott szolgáltatás objektumot. • A munkamenetenkénti mód beállítása a szolgáltatás oldalon: [ServiceContract(SessionMode = SessionMode.Required)] publicinterfaceIUpdateService { // Interfacedefinitioncodegoes here }
Munkamenetenkénti (per session) mód • A másik dolog: • A WCF-nek is meg kell mondani, hogy munkamenetenkénti módot szeretnénk használni, és a szolgáltatás példányt tartsa meg az egész munkamenet alatt. • Ennek megadása a következőképpen történik: [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] publicclassUpdateService : IUpdateService { // Implementationcodegoes here }
Munkamenetenkénti (per session) mód • A kapcsolat igazából nem a kliens és a szolgáltatás között áll fenn, hanem egy adott proxy példány között, amit a kliens és a szolgáltatás használ. • Amikor a WCF-ben létrehozunk egy proxyt, annak lesz egy azonosítója, amit a szolgáltatás hoszt arra használ, hogy a kéréseket a megfelelő példányokhoz továbbítsa. • Mivel ez az azonosító a proxy példányhoz rendelt, így ha a kliens egy proxyból több példányt is létrehoz, azok nem alkotnak egy munkamenetet. Minden proxynak saját szolgáltatás példánya lesz.
Munkamenetenkénti (per session) mód • Ahogy már korábban említettük, a szolgáltatás példány addig létezik, amíg a kliensnek szüksége van rá. • Egy munkamenet megszűnésének legtermészetesebb módja, amikor a kliens bezárja a proxyt. • Ekkor egy üzenet kerül elküldése a szolgáltatás felé, hogy a munkamenet lezárult. • De mi van akkor, ha valamilyen okból a kliens nem zárja be a proxyt? • Ebben az esetben a munkamenet 10 percnyi inaktivitás után automatikusan megszűnik. Ezután ha a kliens újra használni szeretné a proxyt, CommunicationObjectFaultedException –t kap.
Munkamenetenkénti (per session) mód • Ez a 10 perc csak egy alapértelmezett érték, a kötéstől függően változhat. • Ha a kötés egy megbízható munkamenetet (reliable session) biztosít, akkor az InactivityTimeout tulajdonságot ennek megfelelően átállíthatjuk. • Erre egy példa: NetTcpBindingbinding = newNetTcpBinding(); binding.ReliableSession.Enabled = true; binding.ReliableSession.InactivityTimeout = TimeSpan.FromMinutes(60);
Munkamenetenkénti (per session) mód • Ugyanezt a config fájlban is megtehetjük: <netTcpBinding> <binding name="timeoutSession"> <reliableSession enabled="true„inactivityTimeout="01:00:00"/> </binding> </netTcpBinding> • Az InactivityTimeout –ot mind a kliens, mind a szolgáltatás oldalán beállíthatjuk, a kettő közül mindig a kisebb érték a mérvadó. • A basicHttpBinding nem támogatja a munkamenetek létrehozását.
Singleton mód • Ebben a módban csak egyetlen szolgáltatás példány jön létre. • Ennek az egy példánynak a feladata az összes szolgáltatás felé beérkező kérés feldolgozása. • Az a példány örökké él, és csak akkor szűnik meg, amikor a hoszt processz bezáródik. • Beállítása: [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] publicclassUpdateService : IUpdateService { // Implementationcodegoes here }
Singleton mód • Singleton módban lehetőség nyílik konstruktoron keresztül inicializálni a példányt. • Arra is lehetőség van, hogy létrehozzuk a szolgáltatás példányt, még mielőtt a hoszt elindul. Induláskor átadjuk ezt a példányt a hosztnak. • A ServiceHost osztály egyik konstruktora egy singleton példányt vár paraméterként. Az így létrehozott hoszt az összes kérést a paraméterében megkapott szolgáltatás példányhoz fogja továbbítani. • Ennek megadása: SingletonUpdateServicesingletonInstance = new SingletonUpdateService(); ServiceHosthost = new ServiceHost(singletonInstance); host.Open();
Singleton mód • Ilyen módban más objektumoknak lehetőségük van arra, hogy elérjék a szolgáltatás példány metódusait és beállítsák a paramétereit. • A ServiceHost osztály ehhez biztosít egy SingletonInstanceproperty-t, ami az adott példányra hivatkozik. SingletonUpdateService instance = host.SingletonInstance as SingletonUpdateService; instance.Counter+= 50;
Singleton mód • Ha a lokális hoszt változó nem elérhető, a példány akkor is elérhető. Az OperationContext osztály biztosít egy readonlyHostproperty-t, amiből ugyanúgy elérhető a példány. ServiceHosthost = OperationContext.Current.HostasServiceHost; if(host != null) { SingletonUpdateServiceinstance = host.SingletonInstanceasSingletonUpdateService; if(instance != null) instance.Counter+= 1; }
Singleton mód • Ha minden kérést egyetlen példány szolgál ki, akkor oda kell figyelni a konkurrens hozzáférésre. • Ha egy szolgáltatáshoz egyszerre több kérés érkezik, ugyanaz a példány fogja őket feldolgozni, csak különböző szálakon. • Ez azt jelenti, hogy az aktuális metóduson kívüli összes változót (az osztály adattagjait) egyszerre több szál is módosíthatja, aminek hatására az értékük nem lesz megfelelő. • Ennek elkerülésére különböző technikákat érdemes használni, mint pl. zárolás.
A szolgáltatás megvédése • Amikor egy szolgáltatás kikerül egy valós közegbe, akarva – akaratlanul is érhetik támadások. • „Denialof Service” (DoS) támadás: kimeríti a szolgáltatás erőforrásait, hogy az több bejövő kérést már ne tudjon feldolgozni. • A WCF az ilyen problémák enyhítésére számos megoldást nyújt: pl. a bejövő kérések lelassításával, gátolásával vagy erőforrás kvótákkal
Bejövő kérések gátolása • Ennek a megoldásnak a célja kétszeres. • Először is megvédi a szolgáltatást attól, hogy a kliensek elárasszák kérésekkel. • Másrészt pedig egyenletes terhelés elosztást biztosít a szolgáltatásnak. • Ezt úgy valósítják meg, hogy korlátozzák a szolgáltatáshoz beérkezhető kérések számát azért, hogy azokat a szolgáltatás belátható időn belül fel tudja dolgozni. • A WCF alapbeállítása, hogy nem gátolja a bejövő kéréseket. • Ha a gátolás engedélyezett, akkor a WCF minden egyes bejövő kérésnél ellenőrzi a számlálót.
Bejövő kérések gátolása • A WCF alapbeállítása, hogy nem gátolja a bejövő kéréseket. • Ha a gátolás engedélyezett, akkor a WCF minden egyes bejövő kérésnél ellenőrzi a számlálót. • Ha túllépte a beállított értéket, akkor a tovább kéréseket egy várakozó sorra teszi. • Amint a számláló értéket a küszöbérték alá csökken, sorra veszi a következőt a várakozási sorról olyan sorrendben, ahogy a kérések beérkeztek. • Általában ha a szolgáltatás elérte a maximális értékét, akkor a kliens requesttimeout-ot kap.
Bejövő kérések gátolása • A szolgáltatás leírójában 3 beállítás kontrollálja a szolgáltatás által egy időben feldolgozható kérések számát. • Ezek mindegyike a config file ServiceThrottlingBehavior részében található. • Ezek a következők: • MaxConcurrentCalls: szabályozza az egyszerre feldolgozható kérések számát. Alapértéke 16. Ez az érték minden típusú kérésre érvényes. • MaxConcurrentSessions: szabályozza a sessiont igénylő csatornák maximális számát. Alapértéke 10. Ezen túli session igénylés TimeoutExepction-t dob. Ha a beérkező kérés típusa nem alkalmas session kezelésre (pl. basicHttpBinding), akkor ennek az értéknek nincs jelentősége. • MaxConcurrentInstances: a szolgáltatás példányok maximális számát adja meg. Alapértéke Int32.MaxValue. Ennek hatása a példányosítás módjától függ. Munkamenetenkénti esetben a MaxConcurrentSession érték a mérvadó, Singleton módban pedig az érték mindig 1.
Bejövő kérések gátolása • Beállítása a config fájlban: <behaviors> <serviceBehaviors> <behaviorname="throttlingBehavior"> <serviceThrottlingmaxConcurrentCalls="10" maxConcurrentInstances="10" maxConcurrentCalls="10" maxConcurrentInstances="10" maxConcurrentSessions="5"/> </behavior> </serviceBehaviors> </behaviors>
Bejövő kérések gátolása • Beállítás kódból: ServiceHosthost = newServiceHost( typeof(UpdateService),newUri("http://localhost:8080/UpdateService")); host.AddServiceEndpoint( "IUpdateService",newWSHttpBinding(), String.Empty); ServiceThrottlingBehaviorthrottlingBehavior = newServiceThrottlingBehavior(); throttlingBehavior.MaxConcurrentCalls= 10; throttlingBehavior.MaxConcurrentInstances = 10; throttlingBehavior.MaxConcurrentSessions = 5; host.Description.Behaviors.Add(throttlingBehavior); host.Open();
Bejövő kérések gátolása • A szolgáltatás hoszt megnyitása után lehetőség van a bejövő kérések gátolási beállításainak megtekintésére, de azok módosítására nem. • Ezt a szolgáltatás diszpécserén keresztül tehetjük meg. • A ServiceHost osztály a ChannelDispatcherspropertyjében diszpécserek egy kollekcióját biztosítja, amely ChannelDispateched objektumokat tartalmaz. • A ChannelDispatcher objektumnak van egy ServiceThrottle tulajdonsága. Ezen a ServiceThrottle objektumon keresztül elérhetőek az összes beállítások.
Bejövő kérések gátolása • A beállítások elérése a következőképpen történik: ChannelDispatcherdispatcher = OperationContext.Current.Host.ChannelDispatchers[0] asChannelDispatcher; ServiceThrottlethrottle = dispatcher.ServiceThrottle; Trace.WriteLine(String.Format("MaxConcurrentCalls = {0}", throttle.MaxConcurrentCalls)); Trace.WriteLine(String.Format("MaxConcurrentSessions = {0}", throttle.MaxConcurrentSessions)); Trace.WriteLine(String.Format("MaxConcurrentInstances = {0}", throttle.MaxConcurrentInstances));
kvóták • A kvóta mechanizmus magában foglalja a szolgáltatás által felhasznált memória menedzselését is. • A DoS lényege, hogy hatalmas mennyiségű memóriát emészt fel, így a szolgáltatás már nem tudja kiszolgálni a további bejövő kéréseket, melyek így OutOfMemoryException vagy StackOverflowException kivételeket kapnak. • Amikor beállítunk egy kvóta értéket, QuotaExceededException keletkezik. • Ahelyett, hogy ilyenkor a szolgáltatás leállna, csak szimplán figyelmen kívül hagyja az üzenetet, és megy tovább.
kvóták • Számos beállítás van hatással a kvótára: • MaxReceivedMessageSize: ez közvetlenül a kötésre állítódik be. Megadja, hogy mekkora lehet egy üzenet mérete. Alapértéke 65536 byte. Értékét akár a config fájlból, akár kódból is beállíthatjuk. • Config fájlból: <bindings> <netTcpBinding> <bindingname="netTcp„ maxReceivedMessageSize="128000"/> </netTcpBinding> </bindings>
kvóták • Kódból: NetTcpBindingbinding = newNetTcpBinding(); binding.MaxReceivedMessageSize = 128000; ServiceHosthost = newServiceHost( typeof(UpdateService), newUri("net.tcp://localhost:1234/UpdateService")); host.AddServiceEndpoint( "IUpdateService",binding, String.Empty); host.Open();
kvóták • ReaderQuotas: a kötés ezen tulajdonsága a beérkező üzenetek komplexitásának mértékét határozza meg. • Beállítható tulajdonságok:
Műveletek elhatárolása • Alapértelmezetten egy session csak azt jelenti, hogy a szolgáltatás meg tudja állapítani, hogy melyik klienstől érkezett a kérés. • Azonban vannak esetek, amikor a műveletek végrehajtási sorrendje is számít. • A WCF biztosít erre egy mechanizmust a szerződés tervezőjének, mellyel megadhatja, hogy mely metódusok nem lehetnek elsők, vagy utolsók. Erre az IsInitiating és az IsTerminatingpropertyk szolgálnak, melyeket a OperationContract attribútumon belül állíthatunk. • Ha egy metódus IsInitiating attribútum értéke true, és még nem jött létre session, a metódus hívásakor, akkor egyből létrejön egy. Ha már létezik session, akkor a metódus azon belül hívódik meg.
Műveletek elhatárolása • Ha a metódus IsTerminating attribútum értéke true, amikor a metódus befejezi futását, a session lezárul. Ez még nem jelenti azt, hogy megszűnik a szolgáltatás példány, a kliensnek ettől függetlenül még meg kell hívniuk a proxy Close metódusát. Habár minden további utasítás efelé a proxy felé InvalidOperationException-nel tér vissza. • Ezekkel a propertykkel beállítható a műveletek kezdő és végpontja. • Az IsInitiating alapértéke true, az IsTerminating alapértéke false.
Műveletek elhatárolása • Beállítása: [ServiceContract(SessionMode = SessionMode.Required)] publicinterfaceIProcessOrders { [OperationContract] voidInitializeOrder(int customerId); [OperationContract(IsInitiating=false)] voidAddOrderLine(stringproductId, int quantity); [OperationContract(IsInitiating=false)] doubleGetOrderTotal(); [OperationContract(IsInitiating=false, IsTerminating=true)] boolSubmitOrder(); }
Példány deaktiválása • Ahogy az előző ábrán is látható, egy példány egy Context objektumon belül töltődik be, és a session információk a kliens üzeneteit nem közvetlenül a példányhoz kötik, hanem a Context objektumhoz. • Amikor egy session létrejön, akkor a szolgáltatás hoszt egy új kontextust készít. • Ez a kontextus megszűnik, ha a session megszűnik. • Ez azt jelenti, hogy alapértelmezetten az kontextus addig él, ameddig a benne lévő példány. • A WCF lehetőséget nyújt arra, hogy a két életciklust különválasszuk.
Példány deaktiválása • Létrehozható olyan kontextus is, amiben nincs egyetlen példány sem. • A kontextus deaktiváció menedzselése az OperationBehaviorReleaseInstanceModepropertyjén keresztül történik. • A ReleaseInstanceMode-ban számos érték beállítható: • BeforeCall • AfterCall • BeforeAndAfterCall • None • Az alapértelmezett érték a None. Ez azt jelenti, hogy a szolgáltatás példány azután is létezik, miután feldolgozta a kérést.
Példány deaktiválása • BeforeCall mód esetén minden kérés előtt új példány jön létre. Ha egy példány már eleve létezik, akkor deaktiválódik és meghívódik rá a Dispose metódus. Ezalatt a kliens blokkolódik. Az a mód általában akkor használatos, ha a metódus egy kritikus erőforráshoz szeretne hozzáférni és biztosnak kell lenni abban, hogy az összes előző hozzáférés törlődött. • AfterCall mód esetén a metódus lefutása után az aktuális példány deaktiválódik és meghívódik rá a Dispose metódus. Ezzel azt biztosíthatjuk, hogy a metódus által használt erőforrást azonnal felszabadítjuk, amint nincs már rá szükség, így a következő hívásnál egyből elérhető lesz.
Példány deaktiválása • BeforeAndAfterCall mód esetén a mód az előbbi kettő ötvözése. Gyakorlatilag ugyanaz, mint a hívásonkénti (per call) példányosítási mód. A különbség csak annyi, hogy ezt a módot metódusonként tudjuk beállítani, így minden egyes metódusra más és más lehet a példányosítási mód. • Beállítása: publicclassUpdateService : IUpdateService { [OperationBehavior(ReleaseInstanceMode=ReleaseInstanceMode. BeforeAndAfterCall)] publicvoid Update() { // Implementationcodegoes here } }
Példány deaktiválása • Lehetőség van az aktuális szolgáltatás példány futásidejű deaktiválására is. • Ehhez az kontextus biztosít egy ReleaseServiceInstance metódust. • Amikor ezt meghívódik, az aktuális példány megjelölésre kerül, hogy rá a deaktiválás vár, miután a metódus befejezte a futását. • Meghívása: OperationContext.Current.InstanceContext.ReleaseServiceInstance();