1. 什麼是Apache Camel
1.1 什麼是Apache Camel?
Apache Camel 是一款基於Enterprise Integration Patterns的開源集成框架。
Enterprise Integration Patterns 企業集成模式,是一本關於使用消息傳遞進行企業服務集成的書籍。這本書提供了65種模式用於處理異步消息傳遞。Camel支持了其中的絕大部分的模式。
1.2 Apache Camel核心概念
-
CamelContext
CamelContext是一個對象,表示Camel runtime system。一般一個應用中只會有一個CamelContext對象。CamelContext可以管理Route,可以維護一個Name to Component的Map。
-
EndPoint
在Camel的語境下,Endpoint可以是任何能夠提供或者消費消息的事物,如一個JMS隊列,一個web service,一個文件,一個FTP服務器,一個POJO。
-
Component
Component是Endpoint的工廠,Component和Endpoint同樣也是Camel中的接口,各自有諸多的實現如JmsComponent,JmsEndpoint以實現不同的功能。
-
CamelTemplate
CamelTemplate 類是對CamelContext的一個簡單包裝,可以用來給Endpoint發送Message或者Exchange。
-
Message 和 Exchange
Message和Exchange都是Camel提供的接口,Camel同樣也提供了諸多的實現如JmsMessage,JmsExchange等。
-
Processor
Processor接口用以處理Message或者Exchange,Camel同樣也提供了一些實現,開發者也可以自行實現這個接口以完成不同的邏輯。
-
Routes 和 RouteBuilder
Route是一個Message入隊到出隊的所有流程。
開發者可以自行繼承RouteBuilder類以自定義Route。
2. 使用Apache Camel構建REST Web Service
使用Apache Camel構建REST Web服務並不是Camel本身提供的能力,這也不是常見的工程實踐。
2.1 如何實現
Apache Camel是通過一些Camel REST component建立對應的endpoint並定義Camel Route來實現Web服務的。能夠提供Rest DSL的component有:
- camel-rest (需要提供基本的rest component),
- camel-natty-http,
- camel-jetty,
- camel-platform-http,
- camel-servlet,
- camel-undertow
等等。
2.2 初始化項目
該項目是一個使用maven構建的Spring 項目,實現了加法的功能
2.3 引入依賴
首先需要引入camel
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>3.18.1</version>
</dependency>
本例是藉由camel-jetty-http 實現的REST web server,因此我們需要引入該依賴:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-netty-http</artifactId>
<version>3.18.1</version>
<!-- use the same version as your Camel core version -->
</dependency>
本例實現的API有POST請求,傳遞的數據格式為json,需要引入camel-jackson依賴
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<version>3.18.1</version>
<!-- use the same version as your Camel core version -->
</dependency>
2.4 創建Route
本例實現了兩個API如下:
| Http Method | Path | Request Body | Response Body | |
|---|---|---|---|---|
| 1 | POST | /add | {"first": 5, "second": 6} | {"result": 11} |
| 2 | GET | /foo | Bye World. |
import adder.dto.AdderRequest;
import adder.dto.AdderResponse;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestBindingMode;
import org.springframework.stereotype.Component;
@Component
public class AdderRouteBuilder extends RouteBuilder {
@Override
public void configure() {
restConfiguration()
.component("netty-http")
.host("localhost")
.port(8086) // 注1
.bindingMode(RestBindingMode.json)
.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE")
.dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS");
rest()
.post("/add")
.type(AdderRequest.class) // 注2
.outType(AdderResponse.class) // 注3
.produces("application/json") // 注4
.consumes("application/json")
.to("bean:adder?method=add"); // 注5
from("netty-http:http://localhost:8088/foo")
.transform().constant("Bye World."); // 注6
}
}
注1:由於該應用是藉由camel-netty-http component實現的服務,因此需要額外聲明指定端口。如果使用和本身Spring應用一樣的端口(如果沒有指定,那麼默認為8080端口),那麼請求該端口只會獲得Spring本身定義的服務,而不是camel-netty-http提供的服務。
注2:定義輸入的類型,即Request Body的類型,需要自行實現AdderRequest類
注3:定義輸出的類型,即Response Body的類型,需要自行實現AdderResponse類
注4:定義輸出的格式
注5:定義需要調用的Camel endpoint,bean指的是Spring應用的Bean,Spring應用的Bean也可以理解為camel的component。(注意:自定義bean時使用的註解@Component是來源於SpringBoot,而不是Camel)使用Bean component時,其URI格式為 bean:beanName[?options] 具體實現可參考2.5 定義處理方法
注6:同一個endpoint也可以同時監聽其他的端口
2.5 定義處理方法
import adder.dto.AdderRequest;
import adder.dto.AdderResponse;
import org.springframework.stereotype.Component;
@Component
public class Adder {
public AdderResponse add(AdderRequest adderRequest) {
return new AdderResponse(adderRequest.getFirst() + adderRequest.getSecond());
}
}
3. 使用Apache Camel 集成 REST Web Service
3.1 初始化項目
本項目是一個maven構建的Spring項目,消費了上一部分提供的加法功能
3.2 引入依賴
首先需要引入Camel
其次需要引入camel-http以消費http服務,引入camel-jackson以轉換數據
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-http</artifactId>
<version>3.18.1</version>
<!-- use the same version as your Camel core version -->
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<version>3.18.1</version>
<!-- use the same version as your Camel core version -->
</dependency>
3.3 實現controller
| Http Method | Path | Request Body | Response Body | |
|---|---|---|---|---|
| 1 | POST | /plus | {"first-in": 5, "second-in": 6} | {"result-out": 11} |
import calculator.dto.PlusRequest;
import calculator.dto.PlusResponse;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.ExchangeBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CalculatorController {
@Autowired
private ProducerTemplate producerTemplate;
@Autowired
private CamelContext camelContext;
@PostMapping("/plus")
public ResponseEntity<PlusResponse> number(@RequestBody PlusRequest request) {
final Exchange requestExchange = ExchangeBuilder.anExchange(camelContext).withBody(request).build();
return ResponseEntity.ok(producerTemplate.send("direct:addRoute", requestExchange).getIn().getBody(PlusResponse.class));
}
}
"direct:addRoute" 為自定義的一個URI,可以在RouteBuilder中指明
3.4 定義Route
import calculator.dto.PlusResponse;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.dataformat.JsonLibrary;
import org.springframework.stereotype.Component;
@Component
public class CalculatorRouteBuilder extends RouteBuilder {
@Override
public void configure() {
from("direct:addRoute") // 注1
.setHeader(Exchange.HTTP_METHOD, simple("POST"))
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.marshal() // 注2
.json(JsonLibrary.Jackson)
.toD("http://localhost:8086/add")
.unmarshal() // 注3
.json(JsonLibrary.Jackson, PlusResponse.class)
.process();
}
}
注1:from方法為創建一個從指定URI開始的route,相當於在此處聲明瞭自定義的URI
注2:使用marshal方法和json方法指明轉換器以處理輸入
注3:使用unmarshal方法和json方法指明轉換器和輸出格式以處理輸出