Van Queues en Messages, de Dingen die Voorbij Gaan: BizTalk Subscriptions
Een van de belangrijkste architectuurverschillen tussen de .Net versies van BizTalk (i.e. 2004 en 2006) en vroegere (COM) versies is de aanwezigheid van de MessageBox en het Subscription mechanisme dat berichten aflevert aan instanties van BizTalk services. Subscriptions liggen dicht tegen het hart van BizTalk aan en is het onderwerp van dit artikel. In veel opzichten is het redelijk rechttoe rechtaan, maar het heeft zo zijn subtiele kanten. In dit artikel worden de belangrijkste kenmerken van subscriptions behandeld en wordt de toepassing ervan in BizTalk belicht. Voor het ontwikkelen in BizTalk lijkt het wellicht niet direct nodig om hier veel kennis van te hebben; het meeste geschiedt toch achter de schermen en je hebt er als ontwikkelaar slechts zeer beperkt invloed op. Echter, inzicht in dit onderwerp maakt het wel makkelijker om bepaalde eigenaardigheden van BizTalk (beter) te begrijpen.
Echter, inzicht in dit onderwerp maakt het wel makkelijker om bepaalde eigenaardigheden van BizTalk (beter) te begrijpen
Subscription Basics
Het basisidee achter subscriptions is zeer eenvoudig. Berichten worden ontvangen (via zgn. receive ports of orchestration receive ports) en overgedragen aan de BizTalk engine. Die engine stelt een aantal contextuele eigenschappen van het bericht vast en vraagt daarna de filtervoorwaarden om een aantal overeenkomstige subscriptions. Elke subscription bestaat uit een aantal regels (in termen van properties) waaraan berichten moeten voldoen. Voor elke overeenkomende subscription wordt een record aangemaakt in een applicatiespecifieke queue, waarna het wordt verbonden met een bepaalde instantie van een service.
Berichten worden vervolgens uit de queue gehaald met behulp van een aantal gescheiden threads en daarna gerouteerd naar de aangegeven instanties van services. Het subscription mechanisme is in wezen een rules engine, die uit een set van vooraf opgestelde regels, bepaalt welke berichten aan welke service instanties moeten worden aangeboden.
Message Properties
Het subscription mechanisme koppelt subscriptions aan messages, voornamelijk op basis van contextuele properties. Als een message door BizTalk wordt ontvangen, dan worden de properties ervan in kaart gebracht en opgeslagen in de MessageProps tabel. Dit kunnen properties zijn die door de gebruiker als zodanig zijn aangemerkt (promoted), maar ook properties die door BizTalk standaard worden meegegeven, zoals datum en tijdstempels van message bestanden.
Dit is niet het enige waar de MessageProps tabel goed voor is. Het wordt ook gebruikt om vast te leggen tot welke batch een enkelvoudige message behoort en wat het volgnummer van de message is binnen die batch. In BizTalk worden overigens alle messages gezien als behorende tot een batch, ook al is die maar een enkele message groot en kunnen sommige adapters ook niet meer aan dan een enkelvoudige message.
Subscription Matching
Nadat BizTalk de properties van een inkomende message heeft opgeslagen, gaat het met behulp van de stored procedure bts_FindSubscriptions op zoek naar alle subscriptions die voldoen aan de gevraagde kenmerken. Elke subscription wordt als een record in de Subscription tabel opgeslagen met een unieke subscription ID (een guid) en een tijdstempel. Subscriptions kunnen met een Group ID worden gegroepeerd en zijn dan voorzien van een priority code om aan te geven in welke volgorde ze mogen worden gevalideerd.
Vergelijkingen
Subscriptions worden gedefinieerd in termen van zgn. property vergelijkingen. Elke vergelijking is een uitdrukking die gebruikt wordt bij subscription matching. Een vergelijking is bijvoorbeeld een uitdrukking die berichten uitzoekt waarin een orderQty property een waarde heeft van groter dan 5000.
BizTalk ondersteunt de volgende vergelijkingen:
- AND / OR
- Gelijk (==)
- Exists
- Groter of gelijk (>=)
- Groter (>)
- Kleiner of Gelijk (<=)
- Kleiner (<)
- Ongelijk (<>)
In de Filter editor worden AND/OR en Exists gezien als soorten vergelijkingen. Exists vergelijkingen worden getoond als '=EXISTS'. Ze kijken of een property bestaat en niet naar de waarde ervan, wat handig is als je van tevoren niet weet of de property in een bericht zal voorkomen. 'Exists' wordt vaak gebruikt bij vergelijkingen van subscriptions van dynamic send ports (i.e. send ports die pas op het laatste moment worden aangemaakt en waarvan dus maar erg weinig zeker is).
Vergelijkingen worden verzameld in AND en OR groepen. BizTalk ondersteunt geen nesting en is daarom beperkt in het aantal vergelijkingen dat kan worden gemaakt.
Services, Service Instances en Ports
Subscriptions worden aangemaakt via services. Ze zorgen ervoor dat berichten naar bepaalde service instances worden gestuurd. Het is in deze context belangrijk om even stil te staan bij het begrip services in relatie tot BizTalk. Het blijkt namelijk dat dit hier niet zo vanzelfsprekend is als je zou mogen verwachten. Om te beginnen heeft het begrip “service” hier al meer dan een enkele betekenis.
BizTalk is feitelijk gewoon een Windows service
BizTalk is feitelijk gewoon een Windows service (BTSNTSvc.exe). Een Windows service is een programma dat buiten de security context van de op dat moment ingelogde gebruiker loopt. Een van de taken van de service is het beheer van BizTalk hosting. Een BizTalk host is een instelbare en beheersbare eenheid binnen BizTalk die tijdens runtime als sjabloon dient voor een of meer “host instances”. Een host instance beheert actieve BizTalk componenten, zoals adapters. Een BizTalk host houdt die host instances bij elkaar en maakt zo enkelvoudig beheer van een schaalbare groep host instances op meerdere (geclusterde) machines mogelijk.
BizTalk kent twee soorten hosts. ‘In-process’ hosts zijn er om host instances binnen een enkel BTSNTSvc.exe proces aan te maken. ‘Isolated’ hosts instantiëren host instances voor andere processen, zoals IIS-gerelateerde processen. Host instances worden gebruikt voor diverse typen BizTalk componenten, waaronder zgn. Host Instance Subservices, waarvan er vijf zijn:
- Caching
- Tracking
- End Point Manager
- MSMQT
- XLANG/s
De Caching en Tracking subservices zijn alleen voor intern gebruik door andere BizTalk componenten, maar de andere sub-services kunnen door ontwikkelaars worden gebruikt bij messaging en orchestration. De End Point Manager (EPM) subservice ondersteunt messaging tussen twee externe punten. De MSMQT sub-service maakt het mogelijk om een native MSMQ interface rechtstreeks in de BizTalk MessageBox te integreren. Zowel EPM als MSMQT maakt het mogelijk dat adapters en pipelines kunnen worden geconfigureerd en geactiveerd door middel van Receive Ports en Locations en Send Ports. De XLANG/s sub-service, tenslotte, beheert de business processen in een orchestration.
Voor het monitoren van subscription, tracking en activity gebruikt BizTalk nog een service die we hier voor de duidelijkheid maar een “subscriber service’ zullen noemen om het te kunnen onderscheiden van de Windows of de host instance services (in de BizTalk documentatie worden ze gewoon “services” genoemd, wat het er niet erg veel duidelijker op maakt). De host instance sub-services leveren de functionaliteit die nodig is om subscriber services te definiëren en instances van die subscriber services tijdens runtime aan te maken.
Een subscriber service wordt net als een gewone service d.m.v. WSDL (Web Service Description Language) gemodelleerd. De service gedraagt zich als een verzameling van een of meer ports. Elk port is een punt waarnaar messages worden gestuurd of waarvandaan messages naar de MessageBox worden geplaatst. BizTalk categoriseert subscriber services in “service classes”, waarvan er in de adm_ServiceClass tabel vier worden gedefinieerd:
- Messaging InProcess
- Messaging Isolated
- MSMQT
- XLANG/s
De twee Messaging service classes worden door de EPM host instance sub-service ondersteund. Hiernaast levert de HAT een ietwat afwijkende set van service classes, waaronder nog wat extra “interne” classes, zoals de “Tracking” class en de “Routing Failure Report” class . Als het subscription mechanisme een bepaald bericht niet naar een subscriber kan sturen genereert BizTalk een routing failure report message in de MessageBox. In het 'Operations/Messages' menu in de HAT kunnen die messages worden bekeken.
Tijdens runtime maken de sub-services van de host instances van de subscriber service class instances aan. Deze 'service instances' ontvangen via het subscription mechanisme berichten van de MessageBox en kunnen op hun beurt ook weer berichten naar de MessageBox sturen. Het hele idee van service classes en instances komt wellicht nogal vaag over, maar heeft alles van doen met de runtime service classes en hun instances. Dit wordt misschien wat duidelijker als we in XLANG/s termen naar de orchestrations kijken. Orchestrations worden in een BizTalk Visual Studio project gedefinieerd en worden gecompileerd tot .Net classes die weer in de BTXService interface worden geïmplementeerd. Tijdens runtime maakt de XLANG/s sub-service instances van deze classes aan. Elke instance kan worden gezien als een apart proces waarin messages worden ontvangen, verwerkt en verstuurd. Messages worden via het subscription mechanisme direct aan de service instances afgeleverd. Analoog hieraan wordt er, telkens als er een in de BizTalk Explorer een Send port wordt aangemaakt, een nieuwe EPM service gedefinieerd die de messages bij de Send port aflevert.
Tijdens runtime kunnen van elke service class meerdere instances naast elkaar bestaan. Elke instance wordt voor de duur van zijn leven, vastgelegd in de Instances tabel. In het geval van Orchestrations kan dit leven worden “verlengd” door gebruik te maken van “dehydration”. Hierbij wordt de toestand van een Orchestration instance niet langer volledig in het geheugen, maar beknopt (“dehydrated’) in een (tijdelijk) bestand op de schijf opgeslagen.
Service instances en ports zijn de sleutel tot het begrijpen van subscriptions
Service instances en ports zijn de sleutel tot het begrijpen van subscriptions. De BizTalk engine gebruikt elke subscription om messages n.a.v. bepaalde service instances naar ports te sturen. Subscriptions met een Instance ID zijn direct gerelateerd aan een Active of een Dehydrated service instance. Orchestration service instances maken deze instance-specifieke subscriptions dynamisch aan. Telkens als de flow van een Orchestration een Receive shape bereikt worden er dynamisch subscriptions aangemaakt voor de met die shape verbonden port(s). De subscription instance wordt weer verwijderd als de message op de poort is ontvangen. Voor subscriptions zonder Instance ID wordt een nieuwe service instance aangemaakt, telkens als er een message binnenkomt die aan die subscription voldoet.
Een subscription kan ook worden gekoppeld aan een port. Ports zijn immers gedefinieerd op het niveau van services. In het geval van EMP ‘Messaging’ services is er een enkele port per service. Bij Orchestrations kunnen meerdere ports worden gedefinieerd, waarbij een van die ports als de ‘Activate’ port wordt aangeduid. Dit heeft een enkele ‘Activate’ subscription tot gevolg, welke resulteert in een nieuwe instance telkens als een binnenkomende message voldoet aan een subscription. Subscriptions worden aangemaakt voor EPM ‘Send’ ports en orchestration ‘Receive’ ports, maar het subscription mechanisme behandelt ze in beide gevallen hetzelfde.
Subscription Evaluatie en Message En-queuing
Telkens als er voor een bericht een bijpassende subscription wordt gevonden, evalueert de BizTalk engine die subscription. Evaluatie is a eenvoudig proces dat het bericht in een applicatiespecifieke queue plaatst.
In BizTalk wordt elk scenario gezien als een aparte 'Host' met een afzonderlijke set queue tabellen (waaronder ook de ‘suspended’ en de ‘scheduled’ queues) in de MessageBox. In de queue wordt de volgorde vastgelegd waarin de messages worden opgepakt. De queue zelf bevat alleen verwijzingen naar de messages. De messages zelf worden opgeslagen in de MessageParts tabel.
Message De-queuing
BizTalk leest messages uit de queue via een of meer 'de-queuing' threads. De de-queuing threads routeren elke message naar de betreffende service instance. BizTalk maakt gebruik van een locking mechanisme om ervoor te zorgen dat de threads elkaar niet in de weg zitten en dat deadlocks worden uitgesloten. Door het systeem van MessageBox en MessageParts staat de-queuing verder helemaal los van de aflevering van messages. De BizTalk engine houdt voortdurend de queues in de gaten voor nieuwe messages. Telkens als BizTalk de de-queuing stored procedure aanroept wordt de queue doorzocht op messages die zijn gekoppeld aan een bepaalde service class en die bestemd zijn voor een bepaalde host instance (voor die subscriptions die instance specifiek zijn) en leest die allemaal batchgewijs in. De maximum grootte van een batch is 20 records.
Prioriteit
Berichtbatches worden op volgorde van prioriteitsnummer uit een queue gehaald. Prioriteit wordt op het niveau van de subscription vastgelegd van 1 tot 10, waarbij 1 de hoogste en 10 de laagste prioriteit is. Standaard krijgt een subscription prioriteit 5, maar in de Send Port definitie stelt BizTalk de gebruiker in staat die prioriteit aan te passen, tenzij deze is gekoppeld aan een orchestration. Orchestration ports leveren subscriptions altijd met prioriteit 7 af en dit kan niet worden gewijzigd. Omdat berichten uit service specifieke batches worden gehaald kunnen prioriteiten nooit over verschillende servicetypen heen worden gelegd.
Nu we het toch over prioriteit hebben is het misschien interessant een ander fenomeen te behandelen. Hierboven maakte ik al melding van het feit dat subscriptions kunnen worden gegroepeerd. In de stored procedure bts_FindSubscriptions worden de subscriptions geordend op volgorde van message batch, daarna op subscription Group ID (een guid) en daarna op subscription group priority. Als we aannemen dat subscriptions in deze volgorde worden geëvalueerd, dan bepaalt dat de volgorde waarin messages in de queue worden geplaatst en dus de volgorde waarin messages worden behandeld.
Conclusie
In dit artikel heb ik geprobeerd het basismechanisme achter subscriptions duidelijk te maken. Het kan zijn dat e.e.a. niet zo spannend zal zijn overgekomen, maar bedenk dan wel dat het de basis vormt voor zaken als ordered delivery (komt de header eerst aan en dan pas de regels), convoying (geen reeks van kleine berichtjes, maar een grote, waarin meerdere kleine zijn opgenomen), filtering (alleen berichten met bepaalde kenmerken komen over) en time windowing (berichten komen alleen tussen bepaalde tijden over). Er moet nog wel iets overblijven om over te schrijven, natuurlijk…