如何使用变量
变量
自 Camel 4.4 起可用
在 Camel 4.4 中,我们引入了变量(variable)的概念。
变量是一种键值对,可以保存一个值。该值既可以是每个 Exchange 私有的,也可以按路由、按命名分组或按 CamelContext 共享。
| 您也可以将交换属性(exchange properties)用作变量,但交换属性同时也会被 Camel 内部以及一些 EIP 和组件使用。而新引入的变量则是专供最终用户使用的。 |
|---|
变量仓库
变量存储在一个或多个 org.apache.camel.spi.VariableRepository 中。默认情况下,存在以下仓库:
ExchangeVariableRepository—— 每个Exchange一个私有仓库,保存在每个Exchange生命周期内私有的变量。RouteVariableRepository—— 以route作为 id。单个仓库,按Route保存变量。GroupVariableRepository—— 以group作为 id。单个仓库,按命名分组保存变量。这允许在一组路由之间共享变量 —— 范围比按路由共享更广,但比全局作用域更窄。GlobalVariableRepository—— 以global作为 id。整个CamelContext使用的单个全局仓库。
ExchangeVariableRepository 比较特殊,它是每个交换私有的,并且是路由过程中使用时的默认仓库。RouteVariableRepository 是一个单个仓库,保存路由作用域的变量。
还有 org.apache.camel.spi.BrowsableVariableRepository,它是 VariableRepository 的一个扩展,提供了浏览当前变量的 API。Camel 将其用于 Camel CLI 和 JMX,以便从管理工具、CLI 和开发者控制台中查看当前变量。 |
|---|
自定义变量仓库
您可以实现自定义的 org.apache.camel.spi.VariableRepository 并进行插件式集成,从而开箱即用地与 Apache Camel 一起使用。例如,您可以构建一个自定义仓库,将变量存储在数据库中,从而实现持久化。
每个仓库必须拥有自己唯一的 id。不过,也可以用其他仓库替换默认的 global、route 或 group 仓库。
id exchange、header、global、route 和 group 被 Camel 内部保留,不应作为自定义仓库的 id 使用。 |
|---|
从 Java API 获取和设置变量
要从当前 Exchange 获取局部变量,可以通过 Java API 来实现:
String myValue = "...";
exchange.setVariable("myKey", myValue);
// and later to get the variable
Object val = exchange.getVariable("myKey");
// you can get the value as a specific type
String str = exchange.getVariable("myKey", String.class);Exchange 上的 API 默认会从其本地私有仓库中获取变量。不过,你也可以从其他仓库(例如 global)中获取变量,如下所示:
Object val = exchange.getVariable("global:myGlobalKey");你还可以通过加上 global: 前缀来赋值一个全局变量,如下所示:
exchange.setVariable("global:myGlobalKey", someObjectHere);CamelContext 上也提供了获取变量的 API。不过,默认情况下该 API 会从 global 仓库中获取,因为目前无法从正在路由的任何在途 Exchange 中获取变量。
Object val = context.getVariable("myGlobalKey");
// you can get the value as a specific type
String str = context.getVariable("myGlobalKey", String.class);你还可以使用 route: 将变量赋值给特定的路由,如下所示:
exchange.setVariable("route:myRouteId:myRouteKey", someObjectHere);你也可以获取路由变量:
Object val = context.getVariable("route:myRouteId:myRouteKey");
// you can get the value as a specific type
String str = context.getVariable("route:myRouteId:myRouteKey", String.class);你可以使用 group: 将变量赋给某个具名分组,如下所示:
exchange.setVariable("group:teamA:myKey", someObjectHere);同时你也可以获取组变量:
Object val = exchange.getVariable("group:teamA:myKey");
// you can get the value as a specific type
String str = exchange.getVariable("group:teamA:myKey", String.class);分组变量在所有使用相同组名的路由之间共享,因此非常适合在一组相关路由之间共享状态。
通过 DSL 设置和获取变量
也可以在 Camel 路由中使用变量,方式如下:
- setVariable EIP
- removeVariable EIP
- convertVariableTo EIP
这些 EIP 使得在路由中设置和删除变量成为可能。你也可以通过 Simple 语言访问变量。
在下面的路由中,我们在 Exchange 上设置了一个变量,随后用它来构建一条易于阅读的事件消息:
- Java
- XML
- YAML
from("kafka:order.in")
.setVariable("customerId", jq(".customer.custId"))
.setVariable("country", jq(".customer.address.co"))
.transform().simple("Order received from customer ${variable.customerId} in ${variable.country}")
.to("kafka:order.event");<route>
<from uri="kafka:order.in"/>
<setVariable name="customerId">
<jq>.customer.custId</jq>
</setVariable>
<setVariable name="country">
<jq>.customer.address.co</jq>
</setVariable>
<transform>
<simple>Order received from customer ${variable.customerId} in ${variable.country}</simple>
</transform>
<to uri="kafka:order.event"/>
</route>- route:
from:
uri: kafka:order.in
steps:
- setVariable:
name: customerId
jq:
expression: .customer.custId
- setVariable:
name: country
jq:
expression: .customer.address.co
- transform:
simple:
expression: "Order received from customer ${variable.customerId} in ${variable.country}"
- to:
uri: kafka:order.event当在 Camel 路由中使用 route 变量时,如果未显式声明,则 routeId 默认为当前路由。例如,下面的示例中,第一个路由会在第二个路由中设置一个变量(route:second:foo)。这样,第二个路由无需指定其路由 id route:foo 即可获取该变量:
- Java
- XML
- YAML
from("direct:start").routeId("first")
// sets variable in another route
.setVariable("route:second:foo").constant("Hello World")
.to("mock:end");
from("direct:second").routeId("second")
// use variable from this route
.setBody().variable("route:foo");<route id="first">
<from uri="direct:start"/>
<setVariable name="route:second:foo">
<constant>Hello World</constant>
</setVariable>
<to uri="mock:end"/>
</route>
<route id="second">
<from uri="direct:second"/>
<setBody>
<variable>route:foo</variable>
</setBody>
</route>- route:
id: first
from:
uri: direct:start
steps:
- setVariable:
name: route:second:foo
expression:
constant:
expression: Hello World
- to:
uri: mock:end
- route:
id: second
from:
uri: direct:second
steps:
- setBody:
expression:
variable:
expression: route:foo启动时配置初始变量
当 Camel 启动时,只能为 global、route 和 group 这几种仓库配置初始变量。
这可以在 application.properties 中完成,如下所示:
camel.variable.greeting = Random number
camel.variable.random = 999变量默认设置在 global 仓库中,但也可以设置路由作用域的变量,只需使用 route. 作为前缀。由于属性键中不能使用冒号(:),因此使用点号来分隔路由 ID 和变量名,例如 myRoute.gold。
camel.variable.route.myRoute.gold = true
camel.variable.greeting = Random number
camel.variable.random = 999这里的 gold 变量设置在 route 仓库上,其他变量则设置在 global 仓库上。
你还可以设置作用域为组的变量,使用 group. 作为前缀。其中的点号用于分隔组 ID 和变量名,例如 teamA.threshold。
camel.variable.group.teamA.threshold = 100
camel.variable.group.teamB.region = EU变量的值也可以从文件系统中加载,例如 JSON 文件。为此,你需要在值前加上 resource:file: 或 resource:classpath: 前缀,以分别从文件系统或类路径中加载,如下所示:
camel.variable.user-template = resource:file:/etc/user.jsonCamel(Camel 4.8)会自动将值转换为相应的类型:
- 全部为数字的值会被转换为 int 或 long
- true/false 会被转换为 boolean
- 其他情况则保持为字符串值
还支持使用 #bean: 语法引用其他已存在的 Bean:
camel.variable.cheese = #bean:myCheeseBean或者通过 #class: 或 #type: 语法创建一个新的 bean:
camel.variable.cheese = #class:com.foo.MyClassName或者,如果该值必须是特定类型,可以通过 #valueAs 指定,写法如下:
camel.variable.amount = #valueAs(float):1.23在 EIP 中使用变量
以下常用的用于发送、接收和转换消息的 EIP,对选择使用变量而非当前 Exchange 提供了专门的支持:
- from
- to
- toD
- enrich
- poll
- pollEnrich
- wireTap
- unmarshal
- marshal
这样做的意图是让收集数据更加方便、简单,无需任何繁复的操作即可保留现有数据,例如通过使用头信息、Exchange 属性临时存储数据,或使用 Claim Check EIP 等技术。
使用变量与 EIP 时的重要概念
重要的是要理解,变量只针对消息正文(body)。这是有意为之的,目的是保持简单,主要将消息正文作为用户数据来处理。
下表总结了各 EIP 对变量的支持情况:
| EIP | VariableSend | VariableReceive |
|---|---|---|
| From | yes | |
| To | yes | yes |
| ToD | yes | yes |
| Enrich | yes | yes |
| Poll | yes | |
| PollEnrich | yes | |
| WireTap | yes | |
| Unmarshal | yes | yes |
| Marshal | yes | yes |
上面列出的 EIP 在发送和接收数据时支持使用变量。这是通过 variableSend 和 variableReceive 选项来指定变量名称实现的。
这些 EIP 有两种工作模式,其中 variableSend 和 variableReceive 略有不同,因此请注意下表:
| VariableSend | VariableReceive |
|---|---|
| 发送的头信息: 消息 | 接收到的头信息: 变量 |
| 发送的正文: 变量 | 接收到的正文: 变量 |
VariableSend 的设计意图是像常规 Camel 那样发送,其中发送的头信息来自当前 Message,而正文来自变量。换句话说,只是消息正文取自变量,而不是当前 Message 的正文。
VariableReceive 的工作模式有所不同。其思路是将所有接收到的数据都存储为变量,这意味着当前的 Message 完全不会被修改。接收到的消息体存储在变量中,接收到的消息头(传输头等)也以只读消息头的形式存储为变量。变量的命名格式为 header:variableName.headerName。例如,若变量为 myVar,消息头为 Content-Type,则该消息头会以完整名称 header:myVar.Content-Type 存储为一个变量。
使用 Variable Receive 的示例
当 EIP 使用 VariableReceive 时,Exchange 上的 Message 不会被使用,而是从变量中获取消息体和消息头。例如,给定包含以下内容的 Message:
header.foo=123
header.bar=456
body=Hello World并且通过以下路由调用远程服务,该服务返回一个新的 header(level)和 body:'Bye World'
- Java
- XML
- YAML
from("direct:service")
.to("http:myservice")
.to("log:after");<route>
<from uri="direct:service"/>
<to uri="http:myservice"/>
<to uri="log:after"/>
</route>- route:
from:
uri: direct:service
steps:
- to:
uri: http:myservice
- to:
uri: log:after调用该路由后,Message 会被更新为如下内容:
header.foo=123
header.bar=456
header.level=gold
body=Bye World不过,如果使用 VariableReceive=myVar 将调用远程服务返回的数据存入变量,那么结果会变成如下所示:
- Java
- XML
- YAML
from("direct:service")
.toV("http:myservice", null, "myVar")
.to("log:after");<route>
<from uri="direct:service"/>
<to uri="http:myservice" variableReceive="myVar"/>
<to uri="log:after"/>
</route>- route:
from:
uri: direct:service
steps:
- to:
uri: http:myservice
variableReceive: myVar
- to:
uri: log:after当前 Exchange 上的 Message 不会被更改:
header.foo=123
header.bar=456
body=Hello World并且该变量包含从远程 HTTP 服务接收到的全部数据,这些数据被划分到两个变量中:
myVar=Bye World
header:myVar.level=gold注意,头部(header)的存储语法为 header:variable.key。在上面的示例中,变量名为 myVar,头部键为 level,因此结果为:header:myVar.level。 |
|---|
使用变量存储传入的消息体
你可以配置 from,将消息体存储到变量中,而不是存储到 Message 中。这样,通过变量即可快速访问原始的传入消息体。注意,此时 Message 上的消息体将为 null。
下面来自单元测试的示例展示了如何实现这一点。注意 Java DSL 如何使用 fromV 来指定变量的名称。在 XML 和 YAML DSL 中,则通过 variableReceive 参数来指定。
- Java
- XML
- YAML
fromV("direct:start", "myKey")
.transform().simple("Bye ${body}")
.to("mock:foo")
.setBody(variable("myKey"))
.to("mock:result");<route>
<from uri="direct:start" variableReceive="myKey"/>
<transform>
<simple>Bye ${body}</simple>
</transform>
<to uri="mock:foo"/>
<setBody>
<variable>myKey</variable>
</setBody>
<to uri="mock:result"/>
</route>- route:
from:
uri: direct:start
variableReceive: myKey
steps:
- transform:
expression:
simple:
expression: "Bye ${body}"
- to:
uri: mock:foo
- setBody:
expression:
variable:
expression: myKey
- to:
uri: mock:result在上面的示例中,转换 Bye ${body} 的结果将是 Bye,因为 Message 没有消息体——传入的消息体被存储在变量 myKey 中。 |
|---|
评论
登录后参与评论
KnowForge