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
org.apache.camel.springboot
camel-spring-boot-starter
3.18.1
本例是借由camel-jetty-http 实现的REST web server,因此我们需要引入该依赖:
org.apache.camel
camel-netty-http
3.18.1
本例实现的API有POST请求,传递的数据格式为json,需要引入camel-jackson依赖
org.apache.camel
camel-jackson
3.18.1
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以转换数据
org.apache.camel
camel-http
3.18.1
org.apache.camel
camel-jackson
3.18.1
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 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方法指明转换器和输出格式以处理输出