Follow Us on Twitter

Performancewinst door het toepassen van split-join

Augustus 2015 - Onlangs hadden wij een probleem op een project met een service die heel erg traag functioneerde. Deze service had tot taak om geconsolideerde klantencontracten vanuit een database op te halen. Er zijn verscheidene oorzaken die ten grondslag kunnen liggen aan een trage service. Dit kan variëren van gebrek aan resources, een mindere tuning van de resources, overbelasting en een hele reeks van andere oorzaken, maar ook een minder geschikt ontwerp kan tot gevolg hebben dat de performance er onder lijdt.

Door een ander ontwerp toe te passen hebben we een grote stap kunnen maken in de performance van de betrokken services. In de oorspronkelijke opzet had de service een hele reeks aan andere service-calls tot gevolg. Elke call was sequentieel, en leidde tot een databaseconnectie. Ter illustratie, een service-call kan met gemak leiden tot enkele duizenden databaseconnecties.

Hieronder volgt een uiteenzetting welke stappen zijn genomen om tot een verbetering te komen.

Casus 

Probleem: De datalevering aan de webapplicatie is langzaam en voor verbetering vatbaar. Een typische call van de webapplicatie voor informatie, omvat enkele honderden lopende contracten. Alle contracten voor een gebruiker moeten in zijn geheel voor de gebruiker beschikbaar zijn, dit is een harde eis. Het doel is een zodanige performance te verkrijgen die de eindgebruiker associeert met een website.

Dit artikel schetst een oplossing voor het performance probleem door het toepassen van horizontale parallelle processen voor de verrijking van een canoniek object. En van verticale parallellisatie voor de contractobjecten. Met andere woorden, de lijsten met contracten worden zoveel mogelijk parallel afgehandeld.

Scope

Dit artikel is voornamelijk bedoeld voor ontwikkelaars, hoewel architecten er wellicht ook nuttige informatie uit kunnen halen.

Systeemschets

Het systeem bestaat onder meer uit een webapplicatie, Oracle middleware producten, Oracle database. De behandelde toepassing is een webapplicatie waarin lopende contracten getoond worden. De gebruiker kan binnen de toepassing de contracten inzien en bewerken.

De contracten worden door middel van zogenaamde requestor services via de Oracle Service Bus opgevraagd. Een request in dit geval is de vraag voor alle contracten van de gebruiker. Een contract kan worden verrijkt met andere objecten, afhankelijk van de inhoud van het contract. De orchestratie van de contractverrijking vindt plaats in de SOA Suite.

Elk object heeft een canonieke business service op de Oracle Service Bus waarmee verschillende operaties op de database aangeroepen worden.

Een call voor een contract heeft tot gevolg dat er tot zes afzonderlijke business-services aangeroepen worden die de communicatie met de Oracle database verzorgen.

Dit gaan we verhuizen naar OSB, de extra functionaliteit die SOA Suite biedt hebben we hier niet nodig, de orchestratie van de calls is erg eenvoudig, telkens slechts de vraag, is de verrijking nodig, dit kan ook op de bus. Persistentie is ook niet nodig, het zijn simpele getters die aangeroepen worden. Raken we al een mooi stuk overhead kwijt.

Tot nu toe vonden deze verrijkingscalls steeds sequentieel plaats, dit werkt uiteraard vertragend, moeten we van af.

Split-Join

Een split-join is een component in OSB voor het opsplitsen van service berichten. De opgesplitste berichten kunnen vervolgens parallel afgehandeld worden. Synchronisatie van de antwoorden vind plaats op het moment de laatste gesplitste call afgerond is. Er zijn dynamische split-joins en statische split-joins. Bij een statische split-join is bekend exact hoeveel parallele processen er nodig zijn.

De verrijkingsslag die op de SOA-Suite gebeurde is uitermate geschikt om met behulp van een statische split-join af te handelen. We weten namelijk precies welke calls gemaakt moeten worden, er zit geen iteratie in dit proces.

Opzet statische split-join voor de verrijking van een canoniek object.

Er zijn in dit voorbeeld vier externe services die aangeroepen kunnen worden, de response variabelen voor deze calls zijn globaal geïnitialiseerd omdat de aanroep van de externe service optioneel is, afhankelijk van het bericht dat binnenkomt. Aan het eind van de split-join worden de responsevariabelen samengevoegd, daarom moeten we er zeker van zijn dat deze bestaan.

Elke scope is een parallel proces waarin een request wordt klaargezet en een externe service wordt aangeroepen. Dit proces is klaar zodra de traagste service is afgerond, winst!

Nu hebben we nog het geval dat een requestor message meerdere verrijkte objecten opvraagt. Kortom de requestor vraagt om een lijst van objecten.

Dit moet ook parallel gebeuren, alleen we weten tijdens het bouwen niet hoeveel processen er zijn. Tijdens het bouwen is immers niet bekend hoe lang de lijst is. Dit kan ook met een split-join opgelost worden, een dynamische split-join.

De externe service die we hier aanroepen is de service die zorgt voor de verrijking van het object. Deze service hebben we parallel opgezet met een statische split-join.

Wederom een aantal variabelen initialiseren. De parallellisatie gebeurt in de For-Each die volgt . De For-Each werkt op dezelfde manier als de sequentiële variant. Binnen de lus zetten we het request klaar, de externe service wordt aangeroepen en het antwoord verwerkt.

De split-join zal op deze manier proberen om de calls naar de externe service zoveel mogelijk parallel uit te voeren. Dit is niet altijd wenselijk aangezien de resources in principe eindig zijn. We willen dus kunnen controleren hoeveel processen er maximaal parallel zullen lopen. De techniek die daarvoor gebruikt wordt is ‘throttling’.

Bij throttling bepalen we in dit geval hoeveel aangeroepen externe processen er parallel mogen lopen. De rest moet wachten, hiervoor hebben we een spill-over queue nodig, deze kunnen we op dezelfde pagina inrichten. In dit voorbeeld staat het maximum voor de processen op tien en is de ruimte op de queue 1000 berichten. Het spreekt voor zich dat de verhoudingen nauw verband houden met de hoeveelheid calls die binnenkomen. Verder moet de maximum concurrency nooit de beschikbare resources overstijgen.

Knock-on effecten

Uit het bovenstaande voorbeeld blijkt dat we een verrijkingsservice hebben met een maximum van 5 parallelle processen. Deze service wordt vervolgens ook weer aangeroepen vanuit een dynamische split-join met een maximum concurrency van 10 processen. Dit betekent dat er potentieel 50 verbindingen met de achterliggende database opgezet moeten worden om aan de databehoefte te voldoen. In de Weblogic console, bij datasource, zal aangegeven moeten worden hoeveel databaseconnecties er beschikbaar zijn. Het is mogelijk om connecties met de database open te houden. Als er een vrij constante stroom verkeer is dan is dit zeker aan te raden.

Het is ook aan te raden om het aantal sessies en processen op de database te reviewen en mogelijk te verhogen, dit is uiteraard ook afhankelijk van de systeemload in het algemeen. Verder moet er ook rekening gehouden worden met data integriteit. Per geval moet er gekeken worden of bepaalde parallelle processen een absolute must zijn binnen een transactie. Om de integriteit te waarborgen kan de Quality of Service setting in de console op ‘Exactly once’ gezet worden. Dit zorgt ervoor dat het proces in de transactie wordt opgenomen.

Conclusie

Bovenstaand redesign van sequentieel proces naar parallelle verwerking levert veel snelheidswinst op. In ons geval is de responstijd gedaald met gemiddeld een factor 5. Eveneens is er winst te halen door het goed op elkaar afstemmen van de diverse deelsystemen waardoor de concurrency eventueel nog omhoog kan gaan.

Waardering:
 
Tags:

Reacties

Nieuwe reactie inzenden

De inhoud van dit veld is privé en zal niet openbaar worden gemaakt.

Meer informatie over formaatmogelijkheden

CAPTCHA
Deze vraag is om te testen of u een persoon bent en om spam te voorkomen
Image CAPTCHA
Enter the characters shown in the image.