拦截
拦截
Camel 中的拦截(Intercept)功能支持在 Exchange 被路由的过程中对其进行拦截。
拦截器的种类
Camel 支持三种拦截器:
intercept:拦截路由过程中发生的每一个处理步骤interceptFrom:仅拦截进入的步骤(即 from)interceptSendToEndpoint:仅在 Exchange 即将被发送到给定端点时进行拦截。
interceptSendToEndpoint 是动态的,因此当构建出 Camel 在启动时并不知晓的动态 URI 时,它同样会被触发。
interceptFrom 不是动态的,它只会在 Camel 启动时拦截所有已知的路由。因此,如果你使用 Camel Java API 构建一个 Consumer 并从该端点消费消息,那么 interceptFrom 不会被触发。
拦截器作用域
所有拦截器都可以进行全局配置,也可以通过路由配置(Route Configuration)进行配置。
拦截器的通用特性
所有这些拦截器都支持以下特性:
- 使用
onWhen指定谓词(Predicate),以便仅在特定条件下触发拦截器 stop强制停止对 Exchange 的继续路由,并将其标记为成功完成(实际上就是 Stop EIP)。skip与interceptSendToEndpoint搭配使用时,会跳过向原本要发送的端点发送消息。afterUri与interceptSendToEndpoint搭配使用时,允许随后将消息发送到某个端点。interceptFrom和interceptSendToEndpoint支持通过精确 URI、通配符和正则表达式进行端点 URI 模式匹配。详见下文。- 被拦截的端点 URI 会以
Exchange.INTERCEPTED_ENDPOINT为键存储为交换属性(exchange property)。
拦截器元数据
在使用 Apache Camel 拦截器时,框架会自动填充交换属性,提供有关拦截发生位置的上下文信息:
Exchange.INTERCEPTED_ROUTE_ENDPOINT:被拦截的路由端点 URI。Exchange.INTERCEPTED_ROUTE_ID:被拦截路由的 ID。Exchange.INTERCEPTED_NODE_ID:被拦截路由的节点 ID。
使用 intercept
Intercept 会在路由过程中的每一个处理步骤上拦截 Exchange。
来看下面的示例:
- Java
- XML
- YAML
// global interceptor for all routes
intercept().to("log:hello");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder"); <!-- global interceptor for all routes -->
<intercept>
<to uri="log:hello"/>
</intercept>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
</route>- intercept:
steps:
- to:
uri: log:hello
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder实际发生的情况是,Exchange 在每个处理步骤之前都会被拦截,也就是说它会在以下位置之前被拦截:
.to("bean:validateOrder").to("bean:processOrder")
因此,在这个例子中,Exchange 一共被拦截了两次。
使用谓词控制拦截时机
如果你只想"在某些时候"进行拦截,那么可以使用谓词。
例如,在下面的示例中,我们只在消息体包含字符串 Hello 时才进行拦截:
- Java
- XML
- YAML
intercept().onWhen(body().contains("Hello")).to("mock:intercepted");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder");<intercept>
<onWhen>
<simple>${body} contains 'Hello'</simple>
</onWhen>
<to uri="mock:intercepted"/>
</intercept>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
</route>- intercept:
onWhen:
expression:
simple:
expression: "${body} contains 'Hello'"
steps:
- to:
uri: log:hello
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder使用上下文信息控制何时拦截
- Java
- XML
- YAML
intercept().choice()
.when(header(Exchange.INTERCEPTED_ROUTE_ID).isEqualTo("criticalRoute"))
.to("direct:specialHandling")
.otherwise()
.to("direct:standardHandling");<intercept>
<choice>
<when>
<simple>${header.CamelInterceptedRouteId} == 'criticalRoute'</simple>
<to uri="direct:specialHandling"/>
</when>
<otherwise>
<to uri="direct:standardHandling"/>
</otherwise>
</choice>
</intercept>- intercept:
steps:
- choice:
when:
- expression:
simple:
expression: "${header.CamelInterceptedRouteId} == 'criticalRoute'"
steps:
- to:
uri: direct:specialHandling
otherwise:
steps:
- to:
uri: direct:standardHandling拦截后停止路由
拦截之后也可以停止路由。现在假设如果消息体包含 Hello 这个词,我们希望记录日志并停止,那么可以这样做:
- Java
- XML
- YAML
intercept().onWhen(body().contains("Hello"))
.to("log:test")
.stop(); // stop continue routing
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder"); <intercept>
<onWhen>
<simple>${body} contains 'Hello'</simple>
<to uri="log:test"/>
<stop/> <!-- stop continue routing -->
</onWhen>
</intercept>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
</route>- intercept:
onWhen:
expression:
simple:
expression: "${body} contains 'Hello'"
steps:
- to:
uri: log:test
- stop: {}
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder使用 interceptFrom
interceptFrom 用于拦截任何路由中的任何传入 Exchange(它会拦截所有 from EIP)。
这样你就可以对接收到的 Exchange 执行一些自定义行为。你可以为某个给定的 Endpoint 指定一个特定的 uri,这样它就只对该特定路由生效。
那么我们先从日志示例开始。我们希望记录所有传入的消息,因此使用 interceptFrom 将其路由到 Log 组件。
- Java
- XML
- YAML
interceptFrom()
.to("log:incoming");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder"); <interceptFrom>
<to uri="log:incoming"/>
</interceptFrom>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
</route>- interceptFrom:
steps:
- to:
uri: log:incoming
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder如果只想应用于某个特定端点(例如所有 JMS 端点),可以这样做:
- Java
- XML
- YAML
interceptFrom("jms*")
.to("log:incoming");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder");
from("file:inbox")
.to("ftp:someserver/backup") <interceptFrom uri="jms*">
<to uri="log:incoming"/>
</intercept>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
</route>
<route>
<from uri="file:inbox"/>
<to uri="ftp:someserver/backup"/>
</route>- interceptFrom:
uri: "jms*"
steps:
- to:
uri: log:incoming
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder
- route:
from:
uri: file:inbox
steps:
- to:
uri: ftp:someserver/backup在这个例子中,只有来自 JMS 路由的消息会被拦截,因为我们在 interceptFrom 中指定了 jms* 这个模式(使用了通配符)。
模式语法的详细说明将在后文介绍。
使用 interceptSendToEndpoint
你还可以在 Apache Camel 向端点发送消息时进行拦截。
这可以用来在消息发送到目标位置之前执行一些自定义处理。
拦截器也可以配置为不发送到目标位置(skip),这意味着消息将被改道处理。
谓词也可以用来控制何时进行拦截,这一点前文已经介绍过。
afterUri 选项用于需要处理来自目标位置的响应消息的情况。该功能是后来添加到拦截器中的,方式是将消息发送到另一个端点。
我们先从一个基本示例开始,我们想要拦截消息发送到 kafka 的情况:
- Java
- XML
- YAML
interceptSendToEndpoint("kafka*")
.to("bean:beforeKafka");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder")
.to("kafka:order"); <interceptSendToEndpoint uri="kafka*">
<to uri="bean:beforeKafka"/>
</interceptSendToEndpoint>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
<to uri="kafka:order"/>
</route>- interceptSendToEndpoint:
uri: "kafka*"
steps:
- to:
uri: bean:beforeKafka
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder
- to:
uri: kafka:order如果还想在消息发送到目标目的地之后对它进行处理,那么该示例就显得有些奇怪,因为此时必须使用 afterUri,如下所示:
- Java
- XML
- YAML
interceptSendToEndpoint("kafka*")
.to("bean:beforeKafka")
.afterUri("bean:afterKafka");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder")
.to("kafka:order"); <interceptSendToEndpoint uri="kafka*" afterUri="bean:afterKafka">
<to uri="bean:beforeKafka"/>
</interceptSendToEndpoint>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
<to uri="kafka:order"/>
</route>- interceptSendToEndpoint:
uri: "kafka*"
afterUri: "bean:afterKafka"
steps:
- to:
uri: bean:beforeKafka
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder
- to:
uri: kafka:order跳过发送到原始端点
有时你想要拦截并跳过向特定端点发送消息。
例如,为了避免向 kafka 发送任何消息,而是将它们改道到 mock 端点,可以按如下方式操作:
- Java
- XML
- YAML
interceptSendToEndpoint("kafka*").skipSendToOriginalEndpoint()
.to("mock:kafka");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder")
.to("kafka:order"); <interceptSendToEndpoint uri="kafka*" skipSendToOriginalEndpoint="true">
<to uri="mock:kafka"/>
</interceptSendToEndpoint>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
<to uri="kafka:order"/>
</route>- interceptSendToEndpoint:
uri: "kafka*"
skipSendToOriginalEndpoint: true
steps:
- to:
uri: mock:kafka
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder
- to:
uri: kafka:order有条件地跳过发送到端点
你可以将谓词与跳过发送到原始端点结合起来使用。例如,假设你有一些偶尔出现的“测试”消息,而你希望避免将这些消息发送到下游的 Kafka 系统,那么可以按如下方式操作:
- Java
- XML
- YAML
interceptSendToEndpoint("kafka*").skipSendToOriginalEndpoint()
.onWhen(simple("${header.biztype} == 'TEST'")
.log("TEST message detected - is NOT send to kafka");
from("jms:queue:order")
.to("bean:validateOrder")
.to("bean:processOrder")
.to("kafka:order"); <interceptSendToEndpoint uri="kafka*" skipSendToOriginalEndpoint="true">
<onWhen><simple>${header.biztype} == 'TEST'</simple></onWhen>
<log message="TEST message detected - is NOT send to kafka"/>
</interceptSendToEndpoint>
<route>
<from uri="jms:queue:order"/>
<to uri="bean:validateOrder"/>
<to uri="bean:processOrder"/>
<to uri="kafka:order"/>
</route>- interceptSendToEndpoint:
uri: "kafka*"
skipSendToOriginalEndpoint: true
onWhen:
expression:
simple:
expression: "${header.biztype} == 'TEST'"
steps:
- log:
message: "TEST message detected - is NOT send to kafka"
- route:
from:
uri: jms:queue:order
steps:
- to:
uri: bean:validateOrder
- to:
uri: bean:processOrder
- to:
uri: kafka:order使用模式匹配拦截端点
interceptFrom 和 interceptSendToEndpoint 支持按以下规则进行端点模式匹配,并按给定顺序依次匹配:
- 按 URI 名称精确匹配
- 按通配符匹配
- 按正则表达式匹配
按 URI 精确匹配时进行拦截
这种方式只匹配 URI 完全相同的特定端点。
例如,要拦截发送到特定 JMS 队列的消息,可以这样做:
- Java
- XML
- YAML
interceptSendToEndpoint("jms:queue:cheese")
.to("log:smelly"); <interceptSendToEndpoint uri="jms:queue:cheese">
<to uri="log:smelly"/>
</interceptSendToEndpoint>- interceptSendToEndpoint:
uri: "jms:queue:cheese"
steps:
- to:
uri: log:smelly使用通配符匹配端点时进行拦截
通过通配符匹配,你可以匹配一系列端点或某一类型的所有端点。例如,使用 file:* 将匹配所有基于文件的端点。
- Java
- XML
- YAML
interceptFrom("file:*")
.to("log:from-file"); <interceptFrom uri="file:*">
<to uri="log:from-file"/>
</interceptFrom>- interceptFrom:
uri: "file:*"
steps:
- to:
uri: log:from-file通配符匹配的规则是:模式以 \* 结尾,只要 URI 以相同的模式开头即视为匹配。
例如,你可以更精确一些,只匹配特定文件夹下的文件,如:
- Java
- XML
- YAML
interceptFrom("file:order/inbox/*")
.to("log:new-file-orders"); <interceptFrom uri="file:order/inbox/*">
<to uri="log:new-file-orders"/>
</interceptFrom>- interceptFrom:
uri: "file:order/inbox/*"
steps:
- to:
uri: log:new-file-orders通过正则表达式匹配端点时进行拦截
按正则表达式匹配与按通配符匹配类似,只是将通配符替换为正则表达式。因此,如果我们想要拦截来自 gold 和 silver JMS 队列的入站消息,可以这样做:
- Java
- XML
- YAML
interceptFrom("jms:queue:(gold|silver)")
.to("seda:handleFast"); <interceptFrom uri="jms:queue:(gold|silver)">
<to uri="seda:handleFast"/>
</interceptFrom>- interceptFrom:
uri: "jms:queue:(gold|silver)"
steps:
- to:
uri: seda:handleFast评论
登录后参与评论
KnowForge