info@yenlo.com
ned
Menu
API Management 7 min

Integreren van API’s met Apache Camel: Implementatie van het Scatter and Gather-patroon met Java-DSL

Ontdek hoe je API-integratie kunt optimaliseren met behulp van het Scatter en Gather-patroon. In deze blog laten we zien hoe je dit implementeert met Apache Camel, waarbij reacties van meerdere API's worden gestroomlijnd naar één enkele output. Een praktische gids voor zowel technische experts als zakelijke leiders.

Ajanthan Eliyathamby Yenlo
Ajanthan Eliyathamby
Integratie Expert
High Level Integration Flow Diagram

Een van de belangrijkste Enterprise Integration Patterns in API-integratie is het Scatter and Gather-patroon. Dit patroon houdt in dat meerdere reacties van backends worden verzameld en samengevoegd tot één enkele uitvoer, die vaak via de frontend toegankelijk is.

In dit artikel verkennen we een scenario waarin we offertes verzamelen voor het uurtarief voor het verven van huizen van API’s van verschillende aanbieders, deze samenvoegen en in een uniforme weergave aan de frontend presenteren. We gaan dit doen met Apache Camel bovenop Spring Boot.

Voor een beter begrip van het Scatter and Gather-patroon kun je het document hier raadplegen.

Initiële projectopzet

Om het initiële project te genereren, kunnen we de Spring Boot Initializer gebruiken. Laten we de onderstaande selectie gebruiken voor projectgeneratie. Onder afhankelijkheden kun je zien dat ik Apache Camel heb geselecteerd.

Spring Project Initializer

Zodra we de Apache Camel-afhankelijkheid selecteren, wordt de onderstaande afhankelijkheid in pom.xml opgenomen.

<dependency>
<groupId>org.apache.camel.springboot</groupId>
   <artifactId>camel-spring-boot-starter</artifactId>
    <version>4.4.1</version>
</dependency>

Voor de build in dit artikel zullen we Apache Maven 3.8.6 en Java versie: 18.0.2 op een Apple M1 Pro gebruiken, met als doel Java versie 11.

Voor meer inzichten in de projectstructuur kun je het vorige artikel over Apache Camel Deel-1 en Deel-2 raadplegen.

Diagram van het integratieproces op hoog niveau

High Level Integration Flow Diagram

Zoals geïllustreerd in het bovenstaande diagram, hebben we een front-end API genaamd GET /quotes/painting. Deze API zal gelijktijdig de drie leverancier-API’s aanroepen, de reactie van elk ophalen, samenvoegen en vervolgens een geconsolideerde reactie naar de client sturen.

Implementatie

Begrip van de pom.xml

Nr.AfhankelijkheidBeschrijving
1camel-spring-boot-starterHelpt bij de integratie van Camel-routes binnen de Spring Boot-toepassingen
2camel-rest-starterOndersteunt Camel-routes om als RESTful Services te configureren
3lombokVoor het gemak van ontwikkeling zoals slf4j-logging. Biedt getters en setters door simpelweg annotaties te gebruiken
4camel-netty-httpCamel ondersteunt restcomponenten zoals Jetty, Servlet, Undertow en hier gebruik ik Netty
5Properties -> java.versionDefinieert 11, zodat de compatibiliteit met JDK 11 wordt bevestigd en de klassebestanden worden gegenereerd voor de specifieke hoofdversie.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.3</version>
       <relativePath/>
    </parent>
    <groupId>com.quotes</groupId>
    <artifactId>PaintingServiceQuotes</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>PaintingServiceQuotes</name>
    <description>Project for Painting Service Costs</description>
    <properties>
       <java.version>11</java.version>
    </properties>
    <dependencyManagement>
       <dependencies>
          <dependency>
             <groupId>org.apache.camel.springboot</groupId>
             <artifactId>camel-spring-boot-bom</artifactId>
             <version>4.4.1</version>
             <type>pom</type>
             <scope>import</scope>
          </dependency>
          <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-dependencies</artifactId>
             <version>3.2.3</version>
             <type>pom</type>
             <scope>import</scope>
          </dependency>
       </dependencies>
    </dependencyManagement>
    <dependencies>
       <dependency>
          <groupId>org.apache.camel.springboot</groupId>
          <artifactId>camel-spring-boot-starter</artifactId>
       </dependency>
       <dependency>
          <groupId>org.apache.camel.springboot</groupId>
          <artifactId>camel-rest-starter</artifactId>
       </dependency>
       <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
       </dependency>
       <dependency>
          <groupId>org.apache.camel</groupId>
          <artifactId>camel-netty-http</artifactId>
          <version>4.4.1</version>
       </dependency>
    </dependencies>
    <build>
       <plugins>
          <plugin>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
       </plugins>
    </build>
</project>

Implementatie van de Spring Boot hoofdtoepassingsklasse

package com.quotes.painting;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PaintingServiceQuotesApplication {

    public static void main(String[] args) {
       SpringApplication.run(PaintingServiceQuotesApplication.class, args);
    }

}

Backends API Route Builder

We implementeren de Backend API Route builder om de leverancier specifieke API’s te hebben. Zoals je kunt zien, hebben we 3 leverancier-API’s gedefinieerd, en zij zullen application/json-reacties terugsturen.

package com.quotes.painting.builders.api.back;

import lombok.extern.slf4j.Slf4j;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class BackApiBuilder extends RouteBuilder {

    @Value("${custom.netty.http.port}")
    private int nettyHttpPort;

    @Override
    public void configure() throws Exception {

        log.info("BackApiBuilder Route Builder Initiated.");

        restConfiguration()
                .component("netty-http")
                .port(nettyHttpPort);

        // 1. API of the Vendor One
        rest("/quotes")
            .get("/vendor-one")
            .produces("application/json")
            .to("direct:vendorOne");

        from("direct:vendorOne")
            .log("Received GET request for vendorOne.")
                .setBody(constant("{\"vendor\": \"vendorOne\", \"serviceType\": \"Painting\", \"Hourly\": \"25$\"}"));

        // 2. API of the Vendor Two
        rest("/quotes")
            .get("/vendor-two")
            .produces("application/json")
            .to("direct:vendorTwo");

        from("direct:vendorTwo")
            .log("Received GET request for vendorTwo.")
                .setBody(constant("{\"vendor\": \"vendorTwo\", \"serviceType\": \"Painting\", \"Hourly\": \"35$\"}"));

        // 3. API of the Vendor Three
        rest("/quotes")
            .get("/vendor-three")
            .produces("application/json")
            .to("direct:vendorThree");

        from("direct:vendorThree")
            .log("Received GET request for vendorThree.")
            .setBody(constant("{\"vendor\": \"vendorThree\", \"serviceType\": \"Painting\", \"Hourly\": \"15$\"}"));

    }
}

Front API Route Builder

De volgende klasse bevat de hoofdimlementatie van de scatter and gather patroonlogica. In dit voorbeeld heb ik de multicast-camelcomponent gebruikt. Standaard werkt multicast op een enkele thread, maar parallelle verwerking kan ook worden ingeschakeld. Ook is “AggregationStrategy” gebruikt om de antwoordpayloads te combineren en terug te sturen als geconsolideerde JSON-reactie.

package com.quotes.painting.builders.api.front;

import com.quotes.painting.aggregaters.JsonAggregator;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class FrontApiBuilder extends RouteBuilder {

    @Value("${custom.netty.http.port}")
    private int nettyHttpPort;

    @Override
    public void configure() throws Exception {

        log.info("FrontApiBuilder Route Builder Initiated.");

        restConfiguration()
                .component("netty-http")
                .port(nettyHttpPort);

        rest("/quotes")
            .get("/painting")
            .produces("application/json")
            .to("direct:paintingQuotes");

        from("direct:paintingQuotes")
            .log("PaintingQuotes Request Initiated.")
            .multicast()
                .parallelProcessing()
                .aggregationStrategy(new JsonAggregator())
                .to("netty-http:http://localhost:8090/quotes/vendor-one")
                .to("netty-http:http://localhost:8090/quotes/vendor-two")
                .to("netty-http:http://localhost:8090/quotes/vendor-three")
            .end()
            .log("Process Completed: ${body}");
    }
}

Implementatie van Aggregation Strategy

Deze klasse helpt bij het samenvoegen van de reacties van de backend API’s.

package com.quotes.painting.aggregaters;

import org.apache.camel.AggregationStrategy;
import org.apache.camel.Exchange;
import org.springframework.stereotype.Component;

@Component
public class JsonAggregator implements AggregationStrategy {

    @Override
    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
        if (oldExchange == null) {
            return newExchange;
        }
        String oldBody = oldExchange.getIn().getBody(String.class);
        String newBody = newExchange.getIn().getBody(String.class);
        String body = null;
        if (!oldBody.startsWith("[")) {
            body = "[ " + oldBody + ", " + newBody + " ]";
        } else{
            body = oldBody.replace("]", "") + ", " + newBody + " ]";
        }
        oldExchange.getIn().setBody(body);
        return oldExchange;
    }
}

Resources/application.yml-configuratie

camel:
  springboot:
    name: PaintingServiceQuotes

logging:
  level:
    org:
      apache:
        camel: INFO

custom:
    netty:
        http:
            port: 8090

Bouw en Test

Voer het typische maven build-commando uit in de hoofdmap van het project:

mvn clean install

Voer vervolgens het onderstaande commando uit om de Spring Boot-toepassing te starten:

java -jar target/PaintingServiceQuotes-0.0.1-SNAPSHOT.jar

Gebruik een client of curl-commando om de front-end API aan te roepen, die zal reageren met de geaggregeerde reactie.

API Execution View of Client

Dat is het einde van de discussie over hoe we het scatter and gather Enterprise Integration Pattern kunnen implementeren met Apache Camel. Hopelijk heeft deze stapsgewijze handleiding een gedetailleerde uitleg gegeven over hoe we dit met Camel kunnen bereiken.

Tot de volgende blog. Blijf op de hoogte voor meer inzichten en updates.

ned
Sluiten
Wat staat er op ons menu