EIP就是企业整合模式,全记录在一本书里,http://www.enterpriseintegrationpatterns.com/。不过camel in action 列举了5个重要整合模式,学一学应该差不多了吧
第一个是AGGREGATOR,就是将多个消息处理为一个消息,有三点需要配置:
1,用一个表达式Expression配置哪些消息是关联的
2,用一个逻辑表达式Predicate或者是时间相关的条件配置何时产生合并的消息
3,用一个AggregationStrategy来描述如何合并为一条
例子:合并不同消息里的字符,A,B,C,合并为ABC
public void configure() throws Exception {
from("direct:start")
.log("Sending ${body} with correlation key ${header.myId}")
.aggregate(header("myId"),new MyAggregationStrategy())
.completionSize(3)
.log("Sending out ${body}")
.to("mock:result");
}
header("myId"),是一个表达式,表示拥有相同值的消息是关联的,需要被放在一块处理
new MyAggregationStrategy 是一个类,表示合并策略
完成条件为3,表示聚集了3条同类消息,就会输出一条合并消息
camel每得到一个新的identify的Id,就会创建一个aggregator,用来保存消息,当相同identifyId的消息积累到3条时就输出一条
相同功能的spring 配置为
<route>
<from uri="direct:start"/>
<log message="Sending ${body} with key ${header.myId}"/>
<aggregate strategyRef="myAggregationStrategy" completionSize="3">
<correlationExpression>
<header>myId</header>
</correlationExpression>
<log message="Sending out ${body}"/>
<to uri="mock:result"/>
</aggregate>
</route>
单元测试方法。template模拟地发送了4条消息:
public void testABC() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:result");
mock.expectedBodiesReceived("ABC");
template.sendBodyAndHeader("direct:start", "A", "myId", 1);
template.sendBodyAndHeader("direct:start", "B", "myId", 1);
template.sendBodyAndHeader("direct:start", "F", "myId", 2);
template.sendBodyAndHeader("direct:start", "C", "myId", 1);
assertMockEndpointsSatisfied();
}
结果
INFO route1 - Sending A with correlation key 1
INFO route1 - Sending B with correlation key 1
INFO route1 - Sending F with correlation key 2
INFO route1 - Sending C with correlation key 1
INFO route1 - Sending out ABC
来看最重要的部分,如何实现ABC的合并策略
import org.apache.camel.Exchange;
import org.apache.camel.processor.aggregate.AggregationStrategy;
public class MyAggregationStrategy implements AggregationStrategy {
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 = oldBody + newBody;
oldExchange.getIn().setBody(body);
return oldExchange;
}
}
每当有新的消息到来时,aggregate方法都会被触发,这个例子中,被触发了四次
Arrived oldExchange newExchange Description
A null A The first message arrives for the first group
B A B The second messages arrives for the first group
F null F The first message arrives for the second group
C AB C The third message arrives for the first group
简单解释一下:当有新的identifyId到来时,oldExchange未被创建,所以为null,此时简单返回newExchange即可。
当新旧exchange陡不为空时,将旧的exchange更新一下。
注意:aggregate方法是线程安全的,任何时候只能有一个线程执行此方法。
如果触发条件始终未达到,那么可以组合一些其他的条件来触发。
其他触发条件如下:
completionTimeout ,completionInterval ,completionPredicate ,completionFromBatchConsumer
详情请见:http://camel.apache.org/aggregator2
camel还提供了一些属性来查看触发执行情况:
Property Type Description
Exchange.AGGREGATEDSIZE Integer The total number of arrived messages aggregated.
Exchange.AGGREGATEDCOMPLETED BY String The condition that triggered the completion. Possiblevalues are"size","timeout","interval","predicate"
Exchange.AGGREGATEDCORRELATION KEY String The correlation identifier as aString.
这样就可以用来了解运行的情况,比如记录log:
.log("Completed by ${property.CamelAggregatedCompletedBy}")
由于在触发之前Aggregator里面存放了消息,它们都是存储在内存里的,所以服务器断了就无法恢复了。有2种方式可以用来存储消息:
AggregationRepository接口定义了增加和删除数据的方式,camel默认使用MemoryAggregationRepository,将消息存在内存里。
RecoverableAggregationRepository接口定义了恢复数据的方式,camel默认使用camel-hawtdb component来支持恢复。
HawtDB是一种轻量级的key/value文件数据库,它给camel的一些特性提供了存储,比如Aggregator。
使用HawtDB的方式如下:
AggregationRepository myRepo = new
HawtDBAggregationRepository("myrepo", "data/myrepo.dat");
第一个参数是实例名称,必须独一无二,因为一个文件里可能有多个实例。
第二个参数是存储的文件名称。
可以参考:http://camel.apache.org/hawtdb来配置
应用方式如下:
AggregationRepository myRepo = new
HawtDBAggregationRepository("myrepo", "data/myrepo.dat");
from("file://target/inbox")
.log("Consuming ${file:name}")
.aggregate(constant(true), new MyAggregationStrategy())
.aggregationRepository(myRepo)
.completionSize(3)
.log("Sending out ${body}")
.to("mock:result");