Pola Integrasi Dengan Apache Camel

1. Ikhtisar

Artikel ini akan membahas beberapa pola integrasi perusahaan (EIP) penting yang didukung oleh Apache Camel. Pola integrasi membantu dengan memberikan solusi untuk cara standar dalam mengintegrasikan sistem.

Jika Anda perlu mempelajari dasar-dasar Apache Camel terlebih dahulu, kunjungi artikel ini untuk mempelajari dasar-dasarnya.

2. Tentang EIP

Pola integrasi perusahaan adalah pola desain yang bertujuan memberikan solusi untuk tantangan integrasi. Unta menyediakan implementasi untuk banyak pola ini. Untuk melihat daftar lengkap pola yang didukung, kunjungi tautan ini.

Pada artikel ini, kita akan membahas pola integrasi Content Based Router, Message Translator, Multicast, Splitter, dan Dead Letter Channel.

2. Router Berbasis Konten

Content Based Router adalah message router yang merutekan pesan ke tujuannya berdasarkan header pesan, bagian dari payload atau pada dasarnya apapun dari pertukaran pesan yang kami anggap sebagai konten.

Ini dimulai dengan pilihan () pernyataan DSL diikuti dengan satu atau lebih saat () pernyataan DSL. Setiap when () berisi ekspresi predikat yang, jika dipenuhi, akan menghasilkan eksekusi langkah pemrosesan yang terkandung.

Mari kita ilustrasikan EIP ini dengan menentukan rute yang menggunakan file dari satu folder dan memindahkannya ke dua folder berbeda tergantung pada ekstensi file. Rute kami direferensikan dalam file XML Musim Semi menggunakan sintaks XML khusus untuk Camel:

Definisi rute terdapat dalam kelas ContentBasedFileRouter di mana file dirutekan dari folder sumber ke dua folder tujuan yang berbeda bergantung pada ekstensinya.

Alternatifnya, kita bisa menggunakan pendekatan konfigurasi Spring Java di sini sebagai lawan menggunakan file Spring XML. Untuk melakukan itu, kita perlu menambahkan ketergantungan tambahan ke proyek kita:

 org.apache.camel camel-spring-javaconfig 2.18.1 

Versi terbaru artefak dapat ditemukan di sini.

Setelah itu, kita perlu memperluas kelas CamelConfiguration dan mengganti metode routes () yang akan mereferensikan ContentBasedFileRouter :

@Configuration public class ContentBasedFileRouterConfig extends CamelConfiguration { @Bean ContentBasedFileRouter getContentBasedFileRouter() { return new ContentBasedFileRouter(); } @Override public List routes() { return Arrays.asList(getContentBasedFileRouter()); } }

Ekstensi dievaluasi menggunakan Simple Expression Language melalui pernyataan DSL sederhana () yang dimaksudkan untuk digunakan untuk mengevaluasi Ekspresi dan Predikat:

public class ContentBasedFileRouter extends RouteBuilder { private static final String SOURCE_FOLDER = "src/test/source-folder"; private static final String DESTINATION_FOLDER_TXT = "src/test/destination-folder-txt"; private static final String DESTINATION_FOLDER_OTHER = "src/test/destination-folder-other"; @Override public void configure() throws Exception { from("file://" + SOURCE_FOLDER + "?delete=true").choice() .when(simple("${file:ext} == 'txt'")) .to("file://" + DESTINATION_FOLDER_TXT).otherwise() .to("file://" + DESTINATION_FOLDER_OTHER); } }

Di sini kami juga menggunakan pernyataan DSL sebaliknya () untuk merutekan semua pesan yang tidak memenuhi predikat yang diberikan dengan pernyataan when () .

3. Penerjemah Pesan

Karena setiap sistem menggunakan format datanya sendiri, seringkali diperlukan untuk menerjemahkan pesan yang datang dari sistem lain ke dalam format data yang didukung oleh sistem tujuan.

Camel mendukung router MessageTranslator yang memungkinkan kita untuk mengubah pesan menggunakan salah satu prosesor kustom dalam logika perutean, menggunakan kacang tertentu untuk melakukan transformasi atau dengan menggunakan pernyataan DSL transform () .

Contoh dengan menggunakan prosesor khusus dapat ditemukan di artikel sebelumnya di mana kami mendefinisikan prosesor yang menambahkan stempel waktu ke setiap nama file file yang masuk.

Sekarang mari kita tunjukkan cara menggunakan Message Translator menggunakan pernyataan transform () :

public class MessageTranslatorFileRouter extends RouteBuilder { private static final String SOURCE_FOLDER = "src/test/source-folder"; private static final String DESTINATION_FOLDER = "src/test/destination-folder"; @Override public void configure() throws Exception { from("file://" + SOURCE_FOLDER + "?delete=true") .transform(body().append(header(Exchange.FILE_NAME))) .to("file://" + DESTINATION_FOLDER); } }

Dalam contoh ini, kami menambahkan nama file ke konten file melalui pernyataan transform () untuk setiap file dari folder sumber dan memindahkan file yang diubah ke folder tujuan.

4. Multicast

Multicast memungkinkan kita untuk merutekan pesan yang sama ke sekumpulan titik akhir yang berbeda dan memprosesnya dengan cara yang berbeda .

Hal ini dimungkinkan dengan menggunakan pernyataan DSL multicast () dan kemudian dengan membuat daftar titik akhir dan langkah-langkah pemrosesan di dalamnya.

Secara default, pemrosesan pada titik akhir yang berbeda tidak dilakukan secara paralel, tetapi ini dapat diubah dengan menggunakan pernyataan DSL parallelProcessing () .

Camel akan menggunakan balasan terakhir sebagai pesan keluar setelah multicast secara default. Namun, dimungkinkan untuk menentukan strategi agregasi yang berbeda yang akan digunakan untuk mengumpulkan balasan dari multicast.

Mari kita lihat tampilan Multicast EIP di contoh. Kami akan melakukan multicast file dari folder sumber ke dua rute berbeda tempat kami akan mengubah kontennya dan mengirimkannya ke folder tujuan yang berbeda. Di sini kita menggunakan direct: component yang memungkinkan kita menghubungkan dua rute bersama:

public class MulticastFileRouter extends RouteBuilder { private static final String SOURCE_FOLDER = "src/test/source-folder"; private static final String DESTINATION_FOLDER_WORLD = "src/test/destination-folder-world"; private static final String DESTINATION_FOLDER_HELLO = "src/test/destination-folder-hello"; @Override public void configure() throws Exception { 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. Pembelah

Pemisah memungkinkan kita membagi pesan masuk menjadi beberapa bagian dan memprosesnya satu per satu. Hal ini dimungkinkan dengan menggunakan pernyataan DSL split () .

Berbeda dengan Multicast, Splitter akan mengubah pesan masuk, sedangkan Multicast akan membiarkannya apa adanya.

Untuk mendemonstrasikan ini pada sebuah contoh, kami akan menentukan rute di mana setiap baris dari file dipisahkan dan diubah menjadi file individual yang kemudian dipindahkan ke folder tujuan yang berbeda. Setiap file baru akan dibuat dengan nama file yang sama dengan konten file:

public class SplitterFileRouter extends RouteBuilder { private static final String SOURCE_FOLDER = "src/test/source-folder"; private static final String DESTINATION_FOLDER = "src/test/destination-folder"; @Override public void configure() throws Exception { from("file://" + SOURCE_FOLDER + "?delete=true") .split(body().convertToString().tokenize("\n")) .setHeader(Exchange.FILE_NAME, body()) .to("file://" + DESTINATION_FOLDER); } }

6. Saluran Surat Mati

Hal ini biasa terjadi dan diharapkan terkadang masalah dapat terjadi, misalnya database mengalami deadlock, yang dapat menyebabkan pesan tidak terkirim sesuai harapan. Namun, dalam kasus tertentu, mencoba lagi dengan penundaan tertentu akan membantu dan pesan akan diproses.

Dead Letter Channel allows us to control what happens with a message once it fails to be delivered. Using Dead Letter Channel we can specify whether to propagate the thrown Exception to the caller and where to route the failed Exchange.

When a message fails to be delivered, Dead Letter Channel (if used) will move the message to the dead letter endpoint.

Let's demonstrate this on an example by throwing an exception on the route:

public class DeadLetterChannelFileRouter extends RouteBuilder { private static final String SOURCE_FOLDER = "src/test/source-folder"; @Override public void configure() throws Exception { errorHandler(deadLetterChannel("log:dead?level=ERROR") .maximumRedeliveries(3).redeliveryDelay(1000) .retryAttemptedLogLevel(LoggingLevel.ERROR)); from("file://" + SOURCE_FOLDER + "?delete=true") .process(exchange -> { throw new IllegalArgumentException("Exception thrown!"); }); } }

Here we defined an errorHandler which logs failed deliveries and defines redelivery strategy. By setting retryAttemptedLogLevel(), each redelivery attempt will be logged with specified log level.

In order for this to be fully functional, we additionally need to configure a logger.

Setelah menjalankan tes ini, pernyataan log berikut terlihat di konsol:

ERROR DeadLetterChannel:156 - Failed delivery for (MessageId: ID-ZAG0025-50922-1481340325657-0-1 on ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). On delivery attempt: 0 caught: java.lang.IllegalArgumentException: Exception thrown! ERROR DeadLetterChannel:156 - Failed delivery for (MessageId: ID-ZAG0025-50922-1481340325657-0-1 on ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). On delivery attempt: 1 caught: java.lang.IllegalArgumentException: Exception thrown! ERROR DeadLetterChannel:156 - Failed delivery for (MessageId: ID-ZAG0025-50922-1481340325657-0-1 on ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). On delivery attempt: 2 caught: java.lang.IllegalArgumentException: Exception thrown! ERROR DeadLetterChannel:156 - Failed delivery for (MessageId: ID-ZAG0025-50922-1481340325657-0-1 on ExchangeId: ID-ZAG0025-50922-1481340325657-0-2). On delivery attempt: 3 caught: java.lang.IllegalArgumentException: Exception thrown! ERROR dead:156 - Exchange[ExchangePattern: InOnly, BodyType: org.apache.camel.component.file.GenericFile, Body: [Body is file based: GenericFile[File.txt]]]

Seperti yang Anda lihat, setiap upaya pengiriman ulang sedang dicatat untuk menampilkan Exchange yang pengirimannya tidak berhasil.

7. Kesimpulan

Dalam artikel ini, kami menyajikan pengantar pola integrasi menggunakan Apache Camel dan mendemonstrasikannya dalam beberapa contoh.

Kami mendemonstrasikan cara menggunakan pola integrasi ini dan mengapa pola tersebut bermanfaat untuk menyelesaikan tantangan integrasi.

Kode dari artikel ini dapat ditemukan di GitHub.