属性占位符
Camel 广泛支持属性占位符,几乎可以在 Camel 路由、端点、DSL、路由配置、Bean 集成和其他地方使用。
属性占位符用于定义占位符而不是实际值。这一点非常重要,因为您希望应用程序可以进行外部配置,例如网络地址、端口号、身份验证凭据、登录令牌和一般配置的值。
属性占位符语法
通过使用以下语法在属性占位符中指定键名,可以获取 Camel 属性的值: {{key}}
例如
{{file.uri}}
其中 file.uri
是属性键。
通过在 URI 字符串定义中嵌入一个或多个占位符,属性占位符可用于指定端点 URI 的部分或全部内容。
使用带有默认值的属性占位符
您可以指定一个默认值,以便在不存在具有该键的属性时使用,默认值为冒号后的文本:
{{file.url:/some/path}}
在这种情况下,默认值为 /some/path
。.
使用可选属性占位符
Camel 精心设计的属性占位符功能支持可选占位符,在键名中使用 ?
(问号)作为前缀定义占位符。(问号)作为键名的前缀来定义,如图所示:
{{?myBufferSize}}
如果键的值存在,则使用该值,但如果键不存在,Camel 会理解这一点,例如在端点中使用时:
file:foo?bufferSize={{?myBufferSize}}
如果存在占位符, bufferSize
选项将只在端点中配置。否则,将不会在端点上设置该选项,这意味着端点将被重构为:
file:foo
这样, bufferSize
选项就完全不需要指定,Camel 就可以使用 bufferSize
的标准默认值(如果存在的话)。
反转布尔值
如果属性占位符是一个布尔值,则可以通过在键中使用 !
作为前缀来否定(反转)该值。
integration.ftpEnabled=true
from("ftp:....").autoStartup("{{integration.ftpEnabled}}")
.to("kafka:cheese")
from("jms:....").autoStartup("{{!integration.ftpEnabled}}")
.to("kafka:cheese")
在上述示例中,FTP 路由或 JMS 路由只应启动。因此,如果启用了 FTP,则应禁用 JMS,反之亦然。我们可以使用 !integration.ftpEnabled
作为关键字,否定 JMS 路由中的 autoStartup
。
使用属性占位符
在端点 URI 中使用属性占位符时,应使用本示例中所示的 {{key}}
语法:
cool.end = mock:result
where = cheese
而在 Java DSL 中:
from("direct:start")
.to("{{cool.end}}");
以及 XML DSL:
<route>
<from uri="direct:start"/>
<to uri="{{cool.end}}"/>
</route>
属性占位符也可以只是端点 URI 中的一个部分。常见的使用情况是为端点选项(如文件端点中写缓冲区的大小)使用占位符:
buf = 8192
from("direct:start")
.to("file:outbox?bufferSize={{buf}}");
以及 XML DSL:
<route>
<from uri="direct:start"/>
<to uri="file:outbox?bufferSize={{buf}}"/>
</route>
不过,占位符可以是任何地方的名称,因此也可以是模拟端点的名称
from("direct:start")
.to("mock:{{where}}");
在上面的示例中,模拟端点已被硬编码为以 mock:
开始,而 where
占位符的值为 cheese
,因此解析后的 uri 变成了 mock:cheese
而 where
占位符的值为 cheese
,因此解析后的 uri 变成了 mock:cheese
。.
引用其他属性的属性占位符(嵌套占位符)
您还可以设置相互参照的属性,例如
cool.foo=result
cool.concat=mock:{{cool.foo}}
请注意 cool.concat
是如何指向另一个属性的。
以及 XML 中的路线:
<route>
<from uri="direct:start"/>
<to uri="{{cool.concat}}"/>
</route>
关闭嵌套占位符
如果占位符值包含干扰属性占位符语法 {{
和 }}
的数据(如 JSon 数据),则可以通过在键名中使用 ?nested=false
来显式关闭嵌套占位符,如图所示(如 JSon 数据),则可以通过键名中的 ?nested=false
明确关闭嵌套占位符,如图所示:
<route>
<from uri="direct:start"/>
<to uri="elasticsearch:foo?query={{myQuery?nested=false}}"/>
</route>
在上面的示例中,myQuery 占位符的占位符值如下
{"query":{"match_all":{}}}
请注意 json 查询是如何以 }}
结尾的,这与 Camel 属性占位符语法有冲突。
也可以在 "属性 "组件上全局关闭嵌套占位符,例如
CamelContext context = ...
context.getPropertiesComponent().setNestedPlaceholder(false);
转义属性占位符
如果第三方库(例如 ElasticSearch 中 {"query":{"match_all":{}}}
类型的查询)使用双大括号,则属性占位符可能会出现问题。.
要解决这个问题,可以用反斜杠字符转义双引号,例如 \{{ property-name \}}
.这样,它就不会被解释为属性占位符来解析,而会被解析为 {{ property-name }}
。.
如果由于某种原因,双引号前的反斜杠字符不能被解释为转义字符,可以在其前面添加另一个反斜杠来转义,这样它就会被视为反斜杠。
多次使用属性占位符
当然,您也可以多次使用占位符:
cool.start=direct:start
cool.showid=true
cool.result=result
在这条路径中,我们使用了两次 cool.start
:
from("{{cool.start}}")
.to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
.to("mock:{{cool.result}}");
从 Java 代码中解析属性占位符
如果您需要从 Java 代码中解析属性占位符,那么 Camel 有两个 API 可用于此目的:
-
您可以使用PropertiesComponent
上的方法resolveProperty
来解析 Java 代码中的单个属性。 -
使用CamelContext
上的方法resolvePropertyPlaceholders
来解析字符串中的(一个或多个)属性占位符。
例如,要解析键值为 foo 的占位符,可以这样做:
Optional<String> prop = camelContext.getPropertiesComponent().resolveProperty("foo");
if (prop.isPresent()) {
String value = prop.get();
....
}
此 API 用于查找单个属性,并返回 java.util.Optional
类型。
CamelContext
的另一个 API 能够解析多个占位符,并从输入字符串中插入占位符。让我们举例说明一下:
String msg = camelContext.resolvePropertyPlaceholders("{{greeting}} Camel user, Camel is {{cool}} dont you think?");
输入字符串是一个文本语句,其中有两个将被解析的占位符,例如
greeting = Hi
cool = awesome
将解决:
Hi Camel user, Camel is awesome dont you think?
在 spring xml 文件中为任何类型的属性使用属性占位符
以前,只有 XML DSL 中的 xs:string
类型属性才支持占位符。例如,超时属性通常属于 xs:int
类型,因此无法将字符串值设置为占位符键。现在可以使用特殊的占位符命名空间来实现这一点。
在下面的示例中,我们为命名空间 http://camel.apache.org/schema/placeholder
使用 prop
前缀。.现在,我们可以使用 prop:
作为前缀,在 Spring XML 文件中配置任何类型的 XML 属性。
在下面的示例中,我们希望在组播 EIP 中使用 stopOnException
选项的占位符。 stopOnException
是 xs:boolean
类型,因此我们不能将其配置为:
<multicast stopOnException="{{stop}}">
...
</multicast>
相反,我们必须使用 prop:
命名空间,因此必须在 XML 文件顶部的 <beans>
标记中添加该命名空间。
如下图所示,要配置该选项,我们必须使用 prop:optionName
:
<multicast prop:stopOnException="stop">
...
</multicast>
完整示例如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:prop="http://camel.apache.org/schema/placeholder"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="damn" class="java.lang.IllegalArgumentException">
<constructor-arg index="0" value="Damn"/>
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties" location="classpath:myprop.properties"/>
<route>
<from uri="direct:start"/>
<!-- use prop namespace, to define a property placeholder, which maps to option stopOnException={{stop}} -->
<multicast prop:stopOnException="stop">
<to uri="mock:a"/>
<throwException ref="damn"/>
<to uri="mock:b"/>
</multicast>
</route>
</camelContext>
</beans>
在属性文件中,我们将值定义为
stop = true
将 camel 属性占位符与 spring xml 文件桥接起来
如果您使用的是 Spring Boot,则不适用。这只适用于使用 Spring XML 文件的传统 Camel 和 Spring 应用程序。 |
Spring 框架不允许 Apache Camel 等第三方框架无缝挂钩 Spring 属性占位符机制。不过,您可以通过声明一个 org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer
类型的 Spring Bean 来连接 Spring 和 Camel。的 Spring Bean,它是 Spring org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
类型。
要在 Spring 和 Camel 之间架起桥梁,必须定义一个单独的 Bean,如下图所示:
<!-- bridge spring property placeholder with Camel -->
<!-- you must NOT use the <context:property-placeholder at the same time, only this bridge bean -->
<bean id="bridgePropertyPlaceholder" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="location" value="classpath:org/apache/camel/component/properties/cheese.properties"/>
</bean>
您不能同时使用弹簧 <context:property-placeholder>
命名空间;这是不可能的。
声明此 Bean 后,您可以在 <camelContext>
标记中使用 Spring 样式和 Camel 样式定义属性占位符,如下所示:
<!-- a bean that uses Spring property placeholder -->
<!-- the ${hi} is a spring property placeholder -->
<bean id="hello" class="org.apache.camel.component.properties.HelloBean">
<property name="greeting" value="${hi}"/>
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<!-- in this route we use Camels property placeholder {{ }} style -->
<route>
<from uri="direct:{{cool.bar}}"/>
<bean ref="hello"/>
<to uri="{{cool.end}}"/>
</route>
</camelContext>
请注意,hello Bean 使用了纯 Spring 属性占位符 ${}
符号。而在 Camel 路由中,我们使用了带有 {{key}}
的 Camel 占位符符号。.
使用属性占位符函数
属性 "组件包括以下开箱即用的功能:
-
env
- 从操作系统环境变量中查找属性的函数 -
sys
- 从 Java JVM 系统属性中查找属性的函数 -
bean
- 用于从 bean 方法的返回值中查找属性的函数(需要camel-bean
JAR) -
service
- 使用服务命名习语从操作系统环境变量中查找属性的函数 -
service.name
- 使用服务命名习惯从操作系统环境变量中查找属性的函数,仅返回主机名部分 -
service.port
- 使用服务命名习惯从操作系统环境变量中查找属性的函数,仅返回端口部分
这些函数的目的是方便从环境中查找值,如下例所示:
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{env:SOMENAME}}"/>
<to uri="{{sys:MyJvmPropertyName}}"/>
</route>
</camelContext>
您也可以使用默认值,因此如果属性不存在,您可以定义一个默认值,如下图所示,默认值为 log:foo
和 log:bar
值。
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{env:SOMENAME:log:foo}}"/>
<to uri="{{sys:MyJvmPropertyName:log:bar}}"/>
</route>
</camelContext>
服务函数用于查找使用操作系统环境变量定义的服务,使用服务命名习惯用语 hostname : port
来引用服务位置。
-
服务主机名称
-
服务端口名称
换句话说,服务使用 _SERVICE_HOST
和 _SERVICE_PORT
作为前缀。因此,如果服务名为 FOO,那么操作系统环境变量应设置为
export $FOO_SERVICE_HOST=myserver
export $FOO_SERVICE_PORT=8888
例如,如果 FOO 服务是一个远程 HTTP 服务,那么我们可以在 Camel 端点 uri 中引用该服务,并使用 HTTP 组件进行 HTTP 调用:
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="http://{{service:FOO}}/myapp"/>
</route>
</camelContext>
如果服务尚未定义,我们可以使用默认值,例如调用 localhost 上的服务,也许是为了进行单元测试。
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="http://{{service:FOO:localhost:8080}}/myapp"/>
</route>
</camelContext>
bean 函数(需要在类路径上有 camel-bean
JAR)用于从 bean 方法的返回值中查找属性。
假设我们注册了一个名为 "foo "的 bean,它有一个名为 "bar "的方法,可以返回一个目录名,那么我们就可以在 camel 端点 url 中引用 bean 的方法,并使用文件组件来轮询一个目录:
<camelContext>
<route>
<from uri="file:{{bean:foo.bar}}"/>
<to uri="direct:result"/>
</route>
</camelContext>
该方法必须是无参数公共方法(即无参数),并返回一个值,如字符串、布尔值或 int 值。 |
使用 kubernetes 属性占位符函数
camel-kubernetes
组件包括以下功能:
-
configmap
- 用于从 Kubernetes 配置表中查找属性的函数。 -
secret
- 从 Kubernetes Secrets 查找属性的函数。
这两个函数的语法都是
configmap:name/key[:defaultValue]
默认值是可选的,例如,以下代码将查找 myKey
,如果没有这样的 configmap,则查找失败。
configmap:mymap/mykey
在本例中,由于提供了默认值,因此不会失败:
configmap:mymap/mykey:123
在使用 Kubernetes 属性占位符函数之前,需要用以下两种方法之一(或两种)对其进行配置
-
path - 必须挂载到运行 pod 的挂载路径,以便从本地磁盘加载配置映射或秘密。 -
一个用于连接 Kubernetes API 服务器的io.fabric8.kubernetes.client.KubernetesClient
实例。
Camel 将首先使用挂载路径(如果已配置)进行查找,然后回退到使用 KubernetesClient
。.
为配置映射和机密配置挂载路径
挂载路径的配置按给定顺序使用:
-
读取键值为camel.kubernetes-config.mount-path-configmaps
和camel.kubernetes-config.mount-path-secrets
的配置属性 .. -
使用关键字camel.k.mount-path.configmaps
和camel.k.mount-path.secrets
的 JVM 系统属性(与 Camel K 兼容)。(与 Camel K 兼容)。 -
使用 OS ENV 变量,键值为CAMEL_K_MOUNT_PATH_CONFIGMAPS
和CAMEL_K_MOUNT_PATH_SECRETS
(与 Camel K 兼容)。(与 Camel K 兼容)。
例如,若要使用 /etc/camel/resources/
作为挂载路径,可在 application.properties
:
camel.kubernetes-config.mount-path-configmaps = /etc/camel/myconfig/
camel.kubernetes-config.mount-path-secrets = /etc/camel/mysecrets/
配置 kubernetes 客户端
如果运行中的应用程序中存在一个客户端实例(通过注册表查找),Camel 将自动连接 KubernetesClient
。否则,将创建一个新的 KubernetesClient
。客户端可以通过以下两种方式进行配置
-
使用camel.kubernetes-config.client.
属性(示例见下文) -
通过操作系统环境变量、读取~./kube/config
配置和服务账户令牌文件的组合,尝试自动配置。更多详情,请参阅 https://github.com/fabric8io/kubernetes-client 文档。
只有在从本地计算机连接到远程 Kubernetes 集群时,才可能需要显式配置 KubernetesClient
,此时可以指定各种选项,如 masterUrl 和 oauthToken,如图所示:
camel.kubernetes-config.client.masterUrl = https://127.0.0.1:50179/
camel.kubernetes-config.client.oauthToken = eyJhbGciOiJSUzI1NiIsImtpZCI...
KubernetesClient
有许多选项,请参阅 https://github.com/fabric8io/kubernetes-client 文档。
如果只使用挂载路径,那么禁用 KubernetesClient
是个不错的做法,可以将 enabled 设置为 false,如图所示:
camel.kubernetes-config.client-enabled = false
在现有的 Kubernetes 集群中运行 Camel 应用程序时,通常不需要对 KubernetesClient
进行显式配置,可以使用默认设置。
如果使用骆驼 Quarkus,建议使用其 https://quarkus.io/guides/kubernetes-config,该网站会自动预配置 KubernetesClient ,然后骆驼会重复使用。 |
在 Kubernetes 中使用 configmap
给定 Kubernetes 中名为 myconfig
的配置映射有两个条目:
drink = beer
first = Carlsberg
然后就可以在 Camel 路由中使用这些值,例如
<camelContext>
<route>
<from uri="direct:start"/>
<log message="What {{configmap:myconfig/drink}} do you want?"/>
<log message="I want {{configmap:myconfig/first}}"/>
</route>
</camelContext>
您还可以提供一个默认值,以防某个键不存在:
<log message="I want {{configmap:myconfig/second:Heineken}}"/>
在 Kubernetes 中使用秘密
Camel 从 Kubernetes API 服务器读取配置映射。在集群上启用 RBAC 时,用于运行应用程序的 ServiceAccount 需要有适当的访问权限。
名为 mydb
的秘密可能包含连接数据库的用户名和密码,例如
myhost = killroy
myport = 5555
myuser = scott
mypass = tiger
这可以在 Camel 中与 Postrgres Sink Kamelet 等一起使用:
<camelContext>
<route>
<from uri="direct:rome"/>
<setBody>
<constant>{ "username":"oscerd", "city":"Rome"}</constant>
</setBody>
<to uri="kamelet:postgresql-sink?serverName={{secret:mydb/myhost}}
&serverPort={{secret:mydb/myport}}
&username={{secret:mydb/myuser}}
&password={{secret:mydb/mypass}}
&databaseName=cities
&query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/>
</route>
</camelContext>
postgres-sink Kamelet 也可以在 application.properties
中配置,从而减少上述路由中的配置:
camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
这就把路线缩短为
<camelContext>
<route>
<from uri="direct:rome"/>
<setBody>
<constant>{ "username":"oscerd", "city":"Rome"}</constant>
</setBody>
<to uri="kamelet:postgresql-sink?databaseName=cities
&query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/>
</route>
</camelContext>
在本地模式下使用 configmap 或秘钥
在开发过程中,你可能希望在本地模式下运行,在这种模式下,你不需要访问 Kubernetes 集群就能查找配置图。在本地模式下,Camel 会从本地属性中查找配置映射键,例如
例如上面使用 postgresql kamelet 的例子,就是使用秘密配置的:
camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
现在,假设我们想使用本地 Postrgres 数据库,那么我们可以打开本地模式,并在同一属性文件中指定凭据:
camel.kubernetes-config.local-mode = true
mydb/myhost=localhost
mydb/myport=1234
mydb/myuser=scott
mydb/mypass=tiger
注意密钥的前缀是秘密名称和斜线,例如 name/key 。.这样就可以轻松地从 configmap/secret 的实际使用中复制/粘贴到 application.properties 文件中。 |
使用自定义属性占位符函数
属性组件允许插入第三方函数,这些函数可在解析属性占位符时使用。这些函数可以执行自定义逻辑来解析占位符,例如在数据库中查找、进行自定义计算等。函数名称将成为占位符中使用的前缀。
下面的路由示例最能说明这一点,我们使用 beer
作为前缀:
<route>
<from uri="direct:start"/>
<to uri="{{beer:FOO}}"/>
<to uri="{{beer:BAR}}"/>
</route>
如下图所示,该函数的实现只有两个方法:
@org.apache.camel.spi.annotations.PropertiesFunction("beer")
public class MyBeerFunction implements PropertiesFunction {
@Override
public String getName() {
return "beer";
}
@Override
public String apply(String remainder) {
return "mock:" + remainder.toLowerCase();
}
}
函数必须实现 org.apache.camel.spi.PropertiesFunction
接口。方法 getName
是函数(啤酒)的名称。而 apply
方法是我们实现自定义逻辑的地方。由于示例代码来自单元测试,因此它只是返回一个值,用于引用模拟端点。
您还需要将 camel-component-maven-plugin
作为构建组件的一部分,这样才能确保该自定义属性函数生成了必要的源代码,使 Camel 能够自动发现该函数。
如果自定义属性函数需要启动和关闭的逻辑,则可扩展 ServiceSupport 并在 doStart 和 doStop 方法中加入该逻辑。 |
有关示例,请参阅 camel-base64 组件。 |
使用第三方财产来源
属性组件允许插入第三方资源,通过来自 camel-api 的 PropertySource
API 加载和查找属性。API 从 camel-api 加载和查询属性。
常规 PropertySource
将按需查找属性,例如从数据库或 HashiCorp Vault 等后台来源查找值。
PropertySource
可以定义支持一次性从源代码(例如文件系统)加载所有属性(通过实现 LoadablePropertiesSource
)。这样,Camel 属性组件就可以在启动时一次性加载这些属性。
例如, camel-microprofile-config
组件就是通过这种方式实现的。当 Camel 启动时,第三方 PropertySource
可以自动从 classpath 中被发现。具体方法是在文件 META-INF/services/org/apache/camel/property-source-factory
中加入 PropertySource
实现的全限定类名。
请参阅 MicroProfile Config 组件的示例。
您还可以通过 Java API 注册第三方属性源:
PropertiesComponent pc = context.getPropertiesComponent();
pc.addPropertiesSource(myPropertySource);