Integratiepatronen met Apache Camel

1. Overzicht

Dit artikel behandelt enkele essentiële bedrijfsintegratiepatronen (EIP's) die worden ondersteund door Apache Camel. Integratiepatronen helpen door oplossingen te bieden voor gestandaardiseerde manieren om systemen te integreren.

Als je eerst de basisprincipes van Apache Camel moet doornemen, bezoek dan zeker dit artikel om de basis op te frissen.

2. Over EIP's

Enterprise-integratiepatronen zijn ontwerppatronen die bedoeld zijn om oplossingen te bieden voor integratie-uitdagingen. Camel biedt implementaties voor veel van deze patronen. Bezoek deze link om de volledige lijst met ondersteunde patronen te zien.

In dit artikel behandelen we de integratiepatronen van Content Based Router, Message Translator, Multicast, Splitter en Dead Letter Channel.

2. Op inhoud gebaseerde router

Content Based Router is een berichtenrouter die een bericht naar zijn bestemming stuurt op basis van een berichtkop, een deel van de payload of eigenlijk alles van berichtuitwisseling dat we als inhoud beschouwen.

Het begint met keuze() DSL-instructie gevolgd door een of meer wanneer() DSL-verklaringen. Elk wanneer() bevat een predikaatuitdrukking die, indien voldaan, zal resulteren in de uitvoering van ingesloten verwerkingsstappen.

Laten we dit EIP illustreren door een route te definiëren die bestanden uit één map gebruikt en ze naar twee verschillende mappen verplaatst, afhankelijk van de bestandsextensie. Er wordt naar onze route verwezen in Spring XML-bestand met behulp van aangepaste XML-syntaxis voor Camel:

Routedefinitie is opgenomen in ContentBasedFileRouter klasse waarin bestanden worden gerouteerd van de bronmap naar twee verschillende bestemmingsmappen, afhankelijk van hun extensie.

Als alternatief kunnen we hier de Spring Java-configuratiebenadering gebruiken in plaats van het Spring XML-bestand. Om dat te doen, moeten we een extra afhankelijkheid aan ons project toevoegen:

 org.apache.camel camel-spring-javaconfig 2.18.1 

De nieuwste versie van het artefact is hier te vinden.

Daarna moeten we verlengen CamelConfiguration klasse en overschrijven routes () methode die zal verwijzen ContentBasedFileRouter:

@Configuration openbare klasse ContentBasedFileRouterConfig breidt CamelConfiguration uit {@Bean ContentBasedFileRouter getContentBasedFileRouter () {retourneer nieuwe ContentBasedFileRouter (); } @Override openbare lijst routes () {retourneer Arrays.asList (getContentBasedFileRouter ()); }}

De extensie wordt geëvalueerd met behulp van Simple Expression Language via gemakkelijk() DSL-instructie die bedoeld was om te worden gebruikt voor het evalueren van expressies en predikaten:

openbare klasse ContentBasedFileRouter breidt RouteBuilder uit {privé statische laatste String SOURCE_FOLDER = "src / test / source-folder"; private static final String DESTINATION_FOLDER_TXT = "src / test / bestemmingsmap-txt"; private static final String DESTINATION_FOLDER_OTHER = "src / test / bestemmingsmap-anders"; @Override public void configure () gooit uitzondering {from ("file: //" + SOURCE_FOLDER + "? Delete = true"). Choice () .when (simple ("$ {file: ext} == 'txt'" )) .to ("file: //" + DESTINATION_FOLDER_TXT) .otherwise () .to ("file: //" + DESTINATION_FOLDER_OTHER); }}

Hier gebruiken we bovendien anders() DSL-instructie om alle berichten te routeren die niet voldoen aan de predikaten gegeven met wanneer() verklaringen.

3. Berichtvertaler

Omdat elk systeem zijn eigen gegevensformaat gebruikt, is het vaak nodig om het bericht dat van een ander systeem komt te vertalen naar het gegevensformaat dat wordt ondersteund door het doelsysteem.

Camel ondersteunt MessageTranslator router waarmee we berichten kunnen transformeren met behulp van een aangepaste processor in de routeringslogica, een specifieke bean gebruiken om de transformatie uit te voeren of door transformeren() DSL-verklaring.

Een voorbeeld van het gebruik van een aangepaste processor is te vinden in het vorige artikel, waar we een processor hebben gedefinieerd die een tijdstempel toevoegt aan de bestandsnaam van elk inkomend bestand.

Laten we nu demonstreren hoe u Message Translator kunt gebruiken met transformeren() uitspraak:

openbare klasse MessageTranslatorFileRouter breidt RouteBuilder uit {privé statische laatste String SOURCE_FOLDER = "src / test / source-folder"; private static final String DESTINATION_FOLDER = "src / test / bestemmingsmap"; @Override public void configure () gooit uitzondering {from ("file: //" + SOURCE_FOLDER + "? Delete = true") .transform (body (). Append (header (Exchange.FILE_NAME))) .to ("file : // "+ DESTINATION_FOLDER); }}

In dit voorbeeld voegen we de bestandsnaam toe aan de bestandsinhoud via transformeren() instructie voor elk bestand uit de bronmap en het verplaatsen van getransformeerde bestanden naar een doelmap.

4. Multicast

Multicast stelt ons in staat stuur hetzelfde bericht naar een reeks verschillende eindpunten en verwerk ze op een andere manier.

Dit is mogelijk door gebruik te maken van multicast () DSL-instructie en vervolgens door de eindpunten en verwerkingsstappen daarin op te sommen.

Standaard wordt de verwerking op verschillende eindpunten niet parallel uitgevoerd, maar dit kan worden gewijzigd met parallelle verwerking() DSL-verklaring.

Camel gebruikt standaard het laatste antwoord als het uitgaande bericht na de multicasts. Het is echter mogelijk om een ‚Äč‚Äčandere aggregatiestrategie te definiëren die moet worden gebruikt voor het samenstellen van de antwoorden van de multicasts.

Laten we eens kijken hoe Multicast EIP eruit ziet in een voorbeeld. We multicast-bestanden van de bronmap naar twee verschillende routes waar we hun inhoud transformeren en naar verschillende bestemmingsmappen sturen. Hier gebruiken we direct: component waarmee we twee routes met elkaar kunnen verbinden:

openbare klasse MulticastFileRouter breidt RouteBuilder uit {privé statische laatste String SOURCE_FOLDER = "src / test / source-folder"; private static final String DESTINATION_FOLDER_WORLD = "src / test / bestemmingsmap-wereld"; private static final String DESTINATION_FOLDER_HELLO = "src / test / bestemmingsmap-hallo"; @Override public void configure () gooit uitzondering {from ("file: //" + SOURCE_FOLDER + "? Delete = true") .multicast () .to ("direct: append", "direct: prepend"). End ( ); from ("direct: append") .transform (body (). append ("World")) .to ("file: //" + DESTINATION_FOLDER_WORLD); from ("direct: prepend") .transform (body (). prepend ("Hello")) .to ("file: //" + DESTINATION_FOLDER_HELLO); }}

5. Splitser

De splitter stelt ons in staat splitst het inkomende bericht in een aantal stukken en verwerk ze elk afzonderlijk. Dit is mogelijk door gebruik te maken van splitsen () DSL-verklaring.

In tegenstelling tot Multicast, zal Splitter het inkomende bericht veranderen, terwijl Multicast het laat zoals het is.

Om dit aan de hand van een voorbeeld te demonstreren, zullen we een route definiëren waar elke regel uit een bestand wordt opgesplitst en getransformeerd in een individueel bestand dat vervolgens naar een andere bestemmingsmap wordt verplaatst. Elk nieuw bestand wordt gemaakt met een bestandsnaam die gelijk is aan de bestandsinhoud:

openbare klasse SplitterFileRouter breidt RouteBuilder uit {privé statische laatste String SOURCE_FOLDER = "src / test / source-folder"; private static final String DESTINATION_FOLDER = "src / test / bestemmingsmap"; @Override public void configure () gooit uitzondering {from ("file: //" + SOURCE_FOLDER + "? Delete = true") .split (body (). ConvertToString (). Tokenize ("\ n")) .setHeader ( Exchange.FILE_NAME, body ()) .to ("file: //" + DESTINATION_FOLDER); }}

6. Dead Letter Channel

Het komt vaak voor en het is te verwachten dat er soms problemen kunnen optreden, bijvoorbeeld deadlocks in de database, waardoor een bericht niet zoals verwacht kan worden afgeleverd. In bepaalde gevallen zal het echter helpen om het met een bepaalde vertraging opnieuw te proberen en wordt een bericht verwerkt.

Met Dead Letter Channel kunnen we bepalen wat er met een bericht gebeurt als het niet is afgeleverd. Met behulp van Dead Letter Channel kunnen we specificeren of de geworpen uitzondering moet worden doorgegeven aan de beller en waar de mislukte Exchange moet worden gerouteerd.

Als een bericht niet kan worden afgeleverd, verplaatst Dead Letter Channel (indien gebruikt) het bericht naar het eindpunt met dode letters.

Laten we dit aan de hand van een voorbeeld demonstreren door een uitzondering op de route te gooien:

openbare klasse DeadLetterChannelFileRouter breidt RouteBuilder uit {privé statische laatste String SOURCE_FOLDER = "src / test / source-folder"; @Override public void configure () gooit Uitzondering {errorHandler (deadLetterChannel ("log: dead? Level = ERROR") .maximumRedeliveries (3) .redeliveryDelay (1000) .retryAttemptedLogLevel (LoggingLevel.ERROR)); from ("file: //" + SOURCE_FOLDER + "? delete = true") .process (exchange -> {gooi nieuwe IllegalArgumentException ("Uitzondering gegooid!");}); }}

Hier hebben we een errorHandler die mislukte leveringen registreert en de strategie voor herlevering definieert. Door te zetten retryAttemptedLogLevel (), wordt elke poging tot herlevering gelogd met het gespecificeerde logniveau.

Om dit volledig functioneel te laten zijn, moeten we bovendien een logger configureren.

Na het uitvoeren van deze test zijn de volgende logboekinstructies zichtbaar in een console:

FOUT DeadLetterChannel: 156 - Levering mislukt voor (MessageId: ID-ZAG0025-50922-1481340325657-0-1 op ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). Bij afleverpoging: 0 gevangen: java.lang.IllegalArgumentException: uitzondering opgetreden! FOUT DeadLetterChannel: 156 - Levering mislukt voor (MessageId: ID-ZAG0025-50922-1481340325657-0-1 op ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). Bij afleverpoging: 1 gevangen: java.lang.IllegalArgumentException: uitzondering opgetreden! FOUT DeadLetterChannel: 156 - Levering mislukt voor (MessageId: ID-ZAG0025-50922-1481340325657-0-1 op ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). Bij afleverpoging: 2 gevangen: java.lang.IllegalArgumentException: uitzondering opgetreden! FOUT DeadLetterChannel: 156 - Levering mislukt voor (MessageId: ID-ZAG0025-50922-1481340325657-0-1 op ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). Bij afleverpoging: 3 gevangen: java.lang.IllegalArgumentException: uitzondering opgetreden! FOUT dood: 156 - Exchange [ExchangePattern: InOnly, BodyType: org.apache.camel.component.file.GenericFile, Body: [Body is file based: GenericFile [File.txt]]]

Zoals u kunt zien, wordt elke poging tot herlevering geregistreerd en wordt Exchange weergegeven waarvoor de levering niet is gelukt.

7. Conclusie

In dit artikel hebben we een inleiding gegeven tot integratiepatronen met Apache Camel en deze gedemonstreerd aan de hand van enkele voorbeelden.

We hebben laten zien hoe deze integratiepatronen kunnen worden gebruikt en waarom ze gunstig zijn voor het oplossen van integratie-uitdagingen.

Code uit dit artikel is te vinden op GitHub.