架构

拦截

师成师成· 更新于 2026-09-28· 阅读 32 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

拦截

Camel 中的拦截(Intercept)功能支持在 Exchange 被路由的过程中对其进行拦截。

拦截器的种类

Camel 支持三种拦截器:

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

评论

登录后参与评论

正在加载评论…