この記事は2019 新卒 エンジニア Advent Calendar 2019の 8 日目の記事です。
今年 4 月に新卒 Web エンジニアとして働き始め、アサインされたプロジェクトでApache Camelというフレームワークを利用することとなり、一から勉強しています。Webエンジニアとしては、MVCフレームワークとはかなり違う雰囲気に戸惑いましたが、使っていくうちに「ハマるアプリケーションにはハマる」フレームワークという印象を受けました。
この度 2019 年 11 月 28 日にメジャーバージョンアップとなるApache Camel 3.0.0がリリースされましたので、今回はこれを簡単に紹介したいと思います。
Apache Camel とは
そもそも Apache Camel とは、EIP (Enterprise Integration Pattern)をベースとした「統合フレームワーク」です。詳細は省きますが、ひとまず「様々な部品の間をつないで 1 つの大きなアプリケーションを構成するためのフレームワーク」くらいの認識でよいかと思います。
Camel を触ったことがない方には、先にこちらの記事などで Camel で用いられる主要な概念について把握されることをおすすめします。 https://qiita.com/mkyz08/items/db3083a761cb92828926
Getting Started
サンプルには以下の環境を用いました。Kotlin から Camel の Java DSL を使います。また、Camel は単体でも動作しますが、Spring / Spring Boot の手厚いサポートがあるので、今回はこれを使います。
- Kotlin 1.3.61
- Gradle (Kotlin DSL)
- Spring Boot 2.2.2
- Apache Camel 3.0.0
- IntelliJ IDEA CE 2019.3
Spring Initializrで雛形をつくる
Camel 2.X の依存関係は Spring Initializr で追加することができますが、本稿執筆時点ではCamel3 は未対応のようですので、後ほど別途追加します。(2019/12/12 追記)→Spring Boot 2.2以降でDependenciesにApache Camelを追加した際にCamel 3が指定されるようになっていました。
Generate でダウンロードされたプロジェクトの Zip アーカイブを展開し、IDE で開きます。
Camel 3 を追加する
Spring Boot 用の Starter パッケージが用意されているので、こちらを利用します。
Camel 2.X では groupId がorg.apache.camel
でしたが、org.apache.camel.springboot
に移されています。
dependencies {
(略)
implementation("org.apache.camel.springboot:camel-spring-boot-starter:3.0.0")
}
camel-spring-boot-starter
により、Spring アプリケーションの起動と同時にSpringCamelContext
が生成され、Spring Bean に登録されたRouteBuilder
を検索して自動的に Route を登録してくれます。
Camel 3ではこの他のパッケージも大きく構成が変更され、camel-core
のサイズが削減されました。これにより、起動・終了の高速化が図られています。
Route を定義する
RouteBuilder
を継承するクラスを作成し、configure
メソッド内に Java DSL で Route を定義していきます。
Timer Component が 2 秒おきにメッセージを発し、それを Log Component で出力するという簡単な Route 定義です。
package com.example.camel3test
import org.apache.camel.builder.RouteBuilder
import org.springframework.stereotype.Component
@Component
class Camel3TestRouteBuilder : RouteBuilder() {
override fun configure() {
from("timer:tick?period=2000&fixedRate=true")
.to("log:foo")
}
}
Camel を Spring Boot 上で使う場合で、SpringApplication に何も記述しない場合にはすぐにアプリケーションが終了してしまいます。これを起動したままにする設定を加えます。
camel.springboot.main-run-controller = true
./gradlew bootRun
で起動すると、定義通り動作していることが確認できます。
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.2.RELEASE)
2019-12-08 19:39:05.694 INFO 78358 --- [ main] c.e.camel3test.Camel3TestApplicationKt : Starting Camel3TestApplicationKt on MK-MacBook-Air.local with PID 78358 (/Users/mmck328/camel3-test/build/classes/kotlin/main started by mmck328 in /Users/mmck328/camel3-test)
2019-12-08 19:39:05.698 INFO 78358 --- [ main] c.e.camel3test.Camel3TestApplicationKt : No active profile set, falling back to default profiles: default
2019-12-08 19:39:06.649 INFO 78358 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.apache.camel.spring.boot.CamelAutoConfiguration' of type [org.apache.camel.spring.boot.CamelAutoConfiguration$$EnhancerBySpringCGLIB$$35f35be2] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-08 19:39:07.393 INFO 78358 --- [ main] o.a.c.s.boot.SpringBootRoutesCollector : Loading additional Camel XML routes from: classpath:camel/*.xml
2019-12-08 19:39:07.394 INFO 78358 --- [ main] o.a.c.s.boot.SpringBootRoutesCollector : Loading additional Camel XML rests from: classpath:camel-rest/*.xml
2019-12-08 19:39:07.401 INFO 78358 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0 (CamelContext: camel-1) is starting
2019-12-08 19:39:07.402 INFO 78358 --- [ main] o.a.c.management.JmxManagementStrategy : JMX is enabled
2019-12-08 19:39:07.646 INFO 78358 --- [ main] o.a.c.s.boot.SpringBootCamelContext : StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
2019-12-08 19:39:07.683 INFO 78358 --- [ main] c.s.b.CamelSpringBootApplicationListener : Starting CamelMainRunController to ensure the main thread keeps running
2019-12-08 19:39:07.687 INFO 78358 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: route1 started and consuming from: timer://tick?fixedRate=true&period=2000
2019-12-08 19:39:07.687 INFO 78358 --- [inRunController] org.apache.camel.main.BaseMainSupport : Using properties from classpath:application.properties
2019-12-08 19:39:07.695 INFO 78358 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Total 1 routes, of which 1 are started
2019-12-08 19:39:07.699 INFO 78358 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0 (CamelContext: camel-1) started in 0.295 seconds
2019-12-08 19:39:07.708 INFO 78358 --- [ main] c.e.camel3test.Camel3TestApplicationKt : Started Camel3TestApplicationKt in 2.545 seconds (JVM running for 3.005)
2019-12-08 19:39:08.711 INFO 78358 --- [ - timer://tick] foo : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:39:10.694 INFO 78358 --- [ - timer://tick] foo : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:39:12.693 INFO 78358 --- [ - timer://tick] foo : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:39:14.693 INFO 78358 --- [ - timer://tick] foo : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
...
Type-safe Endpoint DSL で Route を定義する
Camel 3 から Type-safe Endpoint DSL が導入されました。先ほどの Route を書き換えてみましょう。
まず、camel-endpointdsl
の依存関係を追加します。
dependencies {
(略)
implementation("org.apache.camel:camel-endpointdsl:3.0.0")
}
そしてRouteBuilder
を定義しますが、Type-safe Endpoint DSL を使うためにEndpointRouteBuilder
を継承します。従来の URL 形式の Endpoint 定義に比べて IDE のサポートも受けやすく、コードも見やすくなった印象です。
package com.example.camel3test
import org.apache.camel.builder.endpoint.EndpointRouteBuilder
import org.springframework.stereotype.Component
@Component
class Camel3TestEndpointRouteBuilder : EndpointRouteBuilder() {
override fun configure() {
from(timer("tock").period(2000).fixedRate(true))
.to(log("bar"))
}
}
同じ動作の Route を定義できました。
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.2.RELEASE)
2019-12-08 19:55:01.686 INFO 78368 --- [ main] c.e.camel3test.Camel3TestApplicationKt : Starting Camel3TestApplicationKt on MK-MacBook-Air.local with PID 78368 (/Users/mmck328/camel3-test/build/classes/kotlin/main started by mmck328 in /Users/mmck328/camel3-test)
2019-12-08 19:55:01.690 INFO 78368 --- [ main] c.e.camel3test.Camel3TestApplicationKt : No active profile set, falling back to default profiles: default
2019-12-08 19:55:02.630 INFO 78368 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.apache.camel.spring.boot.CamelAutoConfiguration' of type [org.apache.camel.spring.boot.CamelAutoConfiguration$$EnhancerBySpringCGLIB$$bb1bf434] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-12-08 19:55:03.596 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootRoutesCollector : Loading additional Camel XML routes from: classpath:camel/*.xml
2019-12-08 19:55:03.597 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootRoutesCollector : Loading additional Camel XML rests from: classpath:camel-rest/*.xml
2019-12-08 19:55:03.602 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0 (CamelContext: camel-1) is starting
2019-12-08 19:55:03.603 INFO 78368 --- [ main] o.a.c.management.JmxManagementStrategy : JMX is enabled
2019-12-08 19:55:03.820 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootCamelContext : StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
2019-12-08 19:55:03.858 INFO 78368 --- [ main] c.s.b.CamelSpringBootApplicationListener : Starting CamelMainRunController to ensure the main thread keeps running
2019-12-08 19:55:03.861 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: route1 started and consuming from: timer://tock?fixedRate=true&period=2000
2019-12-08 19:55:03.863 INFO 78368 --- [inRunController] org.apache.camel.main.BaseMainSupport : Using properties from classpath:application.properties
2019-12-08 19:55:03.865 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: route2 started and consuming from: timer://tick?fixedRate=true&period=2000
2019-12-08 19:55:03.873 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Total 2 routes, of which 2 are started
2019-12-08 19:55:03.876 INFO 78368 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0 (CamelContext: camel-1) started in 0.271 seconds
2019-12-08 19:55:03.881 INFO 78368 --- [ main] c.e.camel3test.Camel3TestApplicationKt : Started Camel3TestApplicationKt in 2.745 seconds (JVM running for 3.21)
2019-12-08 19:55:04.971 INFO 78368 --- [ - timer://tock] bar : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:55:04.971 INFO 78368 --- [ - timer://tick] foo : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:55:06.869 INFO 78368 --- [ - timer://tock] bar : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:55:06.869 INFO 78368 --- [ - timer://tick] foo : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:55:08.869 INFO 78368 --- [ - timer://tock] bar : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
2019-12-08 19:55:08.869 INFO 78368 --- [ - timer://tick] foo : Exchange[ExchangePattern: InOnly, BodyType: null, Body: [Body is null]]
...
その他変更点
その他、公式のマイグレーションガイドで変更点を確認できます。
個人的に印象に残った点を抜粋します。(もう少し色々使ううちに違いに気づくかもしれません)
-
CamelContext
はアプリケーション内で1つに制限されるようになり、これに伴っていくつかのAPIを変更 - 各コンポーネントのリネーム
-
http4
->http
- 旧
http
(Apache HttpClient v3ベース)は廃止
- 旧
-
hdfs2
->hdfs
-
mongodb3
->mongodb
-
netty4
->netty
- ...
-
- Hystrix EIPの変更
.hystrix()
->.circuitBraker()
まとめ
以上、Apache Camel 3について紹介しました。全体的に洗練された印象で、Type-safe Endpoint DSLはHTTP Componentなど定義が長くなりがちなComponentではより効果を発揮しそうです。むしろこの記事、Type-safe Endpoint DSLを試しただけなのでは?