如何使用 Camel 属性占位符

属性占位符函数

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

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

属性占位符函数

返回属性占位符

Properties 组件开箱即用地包含以下函数:

  • env - 用于从操作系统环境变量中查找属性的函数
  • sys - 用于从 Java JVM 系统属性中查找属性的函数
  • bean - 用于从 Bean 方法的返回值中查找属性的函数(需要 camel-bean JAR)
  • boolean - 用于判断属性键是否满足某个条件,并返回 true 或 false 的函数
  • service - 使用服务命名惯例从操作系统环境变量中查找属性的函数
  • service.name - 使用服务命名惯例从操作系统环境变量中查找属性的函数,仅返回主机名部分
  • service.port - 使用服务命名惯例从操作系统环境变量中查找属性的函数,仅返回端口部分

这些函数旨在方便地从环境中查找值,如下面的示例所示:

  • Java
  • XML
  • YAML
from("direct:start")
  .to("{{env:SOMENAME}}")
  .to("{{sys:MyJvmPropertyName}}");
<route>
    <from uri="direct:start"/>
    <to uri="{{env:SOMENAME}}"/>
    <to uri="{{sys:MyJvmPropertyName}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "{{env:SOMENAME}}"
        - to:
            uri: "{{sys:MyJvmPropertyName}}"

你也可以使用默认值,如果属性不存在,可以按如下方式定义默认值,其中默认值为 log:foo 和 log:bar。

  • Java
  • XML
  • YAML
from("direct:start")
  .to("{{env:SOMENAME:log:foo}}")
  .to("{{sys:MyJvmPropertyName:log:bar}}");
<route>
    <from uri="direct:start"/>
    <to uri="{{env:SOMENAME:log:foo}}"/>
    <to uri="{{sys:MyJvmPropertyName:log:bar}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "{{env:SOMENAME:log:foo}}"
        - to:
            uri: "{{sys:MyJvmPropertyName:log:bar}}"

布尔函数旨在为启用或停用 EIP 选项提供更大的灵活性。

例如,假设我们有一个键为 region、值为 EMEA 的属性:

region = EMEA

然后我们可以根据该条件配置 EIP 是否被禁用,方式如下:

  • Java
  • XML
  • YAML
from("direct:start")
    .choice().disabled("{{boolean:region == 'EMEA'}}")
        .when(simple("${header.RetryAttempts} == null"))
            .setProperty("HttpMessageMethod", constant("SET"))
            .process("SetOriginalMessageProcessor")
        .end();
<route>
    <from uri="direct:start"/>
    <choice disabled="{{boolean:region == 'EMEA'}}">
        <when>
            <simple>${header.RetryAttempts} == null</simple>
            <setProperty name="HttpMessageMethod">
                <constant>SET</constant>
            </setProperty>
            <process ref="SetOriginalMessageProcessor"/>
        </when>
    </choice>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - choice:
            disabled: "{{boolean:region == 'EMEA'}}"
            when:
            - expression:
                simple:
                  expression: "${header.RetryAttempts} == null"
              steps:
              - setProperty:
                  name: "HttpMessageMethod"
                  expression:
                    constant: "SET"
              - process:
                  ref: "SetOriginalMessageProcessor"

属性占位符也可在 application.properties 文件中使用,同样支持如 ENV 之类的函数,示例如下:

# server name read from ENV and fallback to use localhost as default value
myserver = {{env:MY_SERVER_NAME:localhost}}
bool 函数使用 Simple 语言,该语言提供了许多用于构建条件的函数和运算符。

service 函数用于查找通过操作系统环境变量定义的服务,它采用服务命名惯例,通过 hostname : port 来引用服务地址:

  • NAME_SERVICE_HOST
  • NAME_SERVICE_PORT

也就是说,服务使用 _SERVICE_HOST 和 _SERVICE_PORT 作为前缀。因此,如果服务名为 FOO,则应将操作系统环境变量设置为:

export $FOO_SERVICE_HOST=myserver
export $FOO_SERVICE_PORT=8888

例如,如果 FOO 服务是一个远程 HTTP 服务,那么我们可以在 Camel 端点 URI 中引用该服务,并使用 HTTP 组件发起 HTTP 调用:

  • Java
  • XML
  • YAML
from("direct:start")
  .to("http://{{service:FOO}}/myapp");
<route>
    <from uri="direct:start"/>
    <to uri="http://{{service:FOO}}/myapp"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "http://{{service:FOO}}/myapp"

如果服务尚未定义,我们可以提供默认值,例如用于单元测试时调用 localhost 上的服务。

  • Java
  • XML
  • YAML
from("direct:start")
  .to("http://{{service:FOO:localhost:8080}}/myapp");
<route>
    <from uri="direct:start"/>
    <to uri="http://{{service:FOO:localhost:8080}}/myapp"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "http://{{service:FOO:localhost:8080}}/myapp"

bean 函数(需要在 classpath 中包含 camel-bean JAR)用于从 bean 方法的返回值中查找属性。

假设我们注册了一个名为 foo 的 bean,它有一个名为 bar 的方法,该方法返回一个目录名,那么我们可以在 Camel 端点 URL 中引用该 bean 的方法,并使用 file 组件轮询该目录:

  • Java
  • XML
  • YAML
from("file:{{bean:foo.bar}}")
  .to("direct:result");
<route>
    <from uri="file:{{bean:foo.bar}}"/>
    <to uri="direct:result"/>
</route>
- route:
    from:
      uri: file:{{bean:foo.bar}}
      steps:
        - to:
            uri: "direct:result"
该方法必须是公共的无参方法(即没有参数),并且返回一个值,例如 String、boolean、int。

使用 Kubernetes 属性占位符函数

camel-kubernetes 组件包含以下函数:

  • configmap - 用于从 Kubernetes ConfigMap 中查找字符串属性的函数。
  • configmap-binary - 用于从 Kubernetes ConfigMap 中查找二进制属性的函数。
  • secret - 用于从 Kubernetes Secret 中查找字符串属性的函数。
  • secret-binary - 用于从 Kubernetes Secret 中查找二进制属性的函数。

这两个函数的语法为:

configmap:name/key[:defaultValue]

默认值是可选的,例如下面的例子会查找 myKey,如果不存在这样的 configmap 则会失败。

configmap:mymap/mykey

因此,在这个示例中,由于提供了默认值,它不会失败:

configmap:mymap/mykey:123

如果存储在 configmap 中的值是二进制格式(即以 Binary Data 形式存储),则会将其下载为一个文件,并返回该文件的绝对路径。

configmap-binary:mymap/mybinkey

它会返回类似 /tmp/camel11787545916150467474/mybinkey 的路径

在使用 Kubernetes 属性占位符函数之前,需要先对其进行配置,可配置以下一项(或两项都配置):

  • path - 一个挂载路径,必须挂载到正在运行的 Pod 上,以便从本地磁盘加载 ConfigMap 或 Secret。
  • kubernetes client - 自动装配(Autowired) 一个 io.fabric8.kubernetes.client.KubernetesClient 实例,用于连接 Kubernetes API 服务器。

Camel 会首先使用挂载路径(如果已配置)进行查找,若未配置则回退使用 KubernetesClient。

为 ConfigMap 和 Secret 配置挂载路径

挂载路径的配置按以下优先顺序读取:

  1. 读取配置属性,键为 camel.kubernetes-config.mount-path-configmaps 和 camel.kubernetes-config.mount-path-secrets。
  2. 使用 JVM 系统属性,键为 camel.k.mount-path.configmaps 和 camel.k.mount-path.secrets(与 Camel K 兼容)。
  3. 使用操作系统环境变量,键为 CAMEL_K_MOUNT_PATH_CONFIGMAPS 和 CAMEL_K_MOUNT_PATH_SECRETS(与 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 客户端

如果运行中的应用程序里只存在一个客户端实例(通过 Registry 查找),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 拥有许多选项,更多详情请参阅 Kubernetes Client 文档。

如果你只使用挂载路径,那么最好禁用 KubernetesClient,只需将 enabled 设置为 false 即可,如下所示:

camel.kubernetes-config.client-enabled = false

当在现有的 Kubernetes 集群中运行 Camel 应用时,通常无需显式配置 KubernetesClient,可以直接依赖默认设置。

如果你使用 Camel Quarkus,建议使用其 https://quarkus.io/guides/kubernetes-config,它会自动预先配置 KubernetesClient,随后 Camel 会复用该客户端。

在 Kubernetes 中使用 configmap

假设 Kubernetes 中有一个名为 myconfig 的 configmap,其中包含两条条目:

drink = beer
first = Carlsberg

然后这些值可以在你的 Camel 路由中使用,例如:

  • Java
  • XML
  • YAML
from("direct:start")
  .log("What {{configmap:myconfig/drink}} do you want?")
  .log("I want {{configmap:myconfig/first}}");
  <route>
    <from uri="direct:start"/>
    <log message="What {{configmap:myconfig/drink}} do you want?"/>
    <log message="I want {{configmap:myconfig/first}}"/>
  </route>
- route:
    from:
      uri: direct:start
      steps:
        - log:
            message: "What {{configmap:myconfig/drink}} do you want?"
        - log:
            message: "I want {{configmap:myconfig/first}}"

你还可以为键不存在的情况提供默认值,例如将 Heiniken 作为默认值:

  • Java
  • XML
  • YAML
from("direct:start")
  .log("What {{configmap:myconfig/drink}} do you want?")
  .log("I want {{configmap:myconfig/second:Heineken}}");
  <route>
    <from uri="direct:start"/>
    <log message="What {{configmap:myconfig/drink}} do you want?"/>
    <log message="I want {{configmap:myconfig/second:Heineken}}"/>
  </route>
- route:
    from:
      uri: direct:start
      steps:
        - log:
            message: "What {{configmap:myconfig/drink}} do you want?"
        - log:
            message: "I want {{configmap:myconfig/second:Heineken}}"

在 Kubernetes 中使用 Secret

Camel 会从 Kubernetes API Server 读取 ConfigMap。当集群启用了 RBAC 时,用于运行应用程序的 ServiceAccount 必须具备相应的权限才能进行此类访问。

一个名为 mydb 的 Secret 可能包含用于连接数据库的用户名和密码,例如:

myhost = killroy
myport = 5555
myuser = scott
mypass = tiger

这可以在 Camel 中使用,例如配合 Postgres Sink Kamelet:

  • Java
  • XML
  • YAML
String sink =
"""
 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)
""";

from("direct:rome")
  .setBody(constant("{ \"username\":\"oscerd\", \"city\":\"Rome\"}"))
  .to(sink);
  <route>
    <from uri="direct:rome"/>
    <setBody>
      <constant>{ "username":"oscerd", "city":"Rome"}</constant>
    </setBody>
    <to uri="kamelet:postgresql-sink?serverName={{secret:mydb/myhost}}
             &amp;serverPort={{secret:mydb/myport}}
             &amp;username={{secret:mydb/myuser}}
             &amp;password={{secret:mydb/mypass}}
             &amp;databaseName=cities
             &amp;query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/>
  </route>
- route:
    from:
      uri: direct
      parameters:
        name: rome
      steps:
        - setBody:
            expression:
              constant:
                expression: "{ \"username\":\"oscerd\", \"city\":\"Rome\"}"
        - to:
            uri: kamelet
            parameters:
              templateId: 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)"

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}}

这会将路由简化为:

  • Java
  • XML
  • YAML
from("direct:rome")
  .setBody(constant("{ \"username\":\"oscerd\", \"city\":\"Rome\"}"))
  .to("kamelet:postgresql-sink?databaseName=cities&query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)");
  <route>
    <from uri="direct:rome"/>
    <setBody>
      <constant>{ "username":"oscerd", "city":"Rome"}</constant>
    </setBody>
    <to uri="kamelet:postgresql-sink?databaseName=cities
             &amp;query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/>
  </route>
- route:
    from:
      uri: direct:rome
      steps:
        - setBody:
            expression:
              constant:
                expression: "{ \"username\":\"oscerd\", \"city\":\"Rome\"}"
        - to:
            uri: kamelet
            parameters:
              templateId: postgresql-sink
              databaseName: cities
              query: "INSERT INTO accounts (username,city) VALUES (:#username,:#city)"

在本地模式中使用 configmap 或 secret

在开发过程中,你可能希望以本地模式运行,此时无需访问 Kubernetes 集群即可查找 configmap。在本地模式下,Camel 将从本地属性中查找 configmap 的键,例如:

例如,上面使用 Postgres kamelet 并通过 secret 进行配置的示例:

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}}

现在假设我们有一个本地 Postgres 数据库需要使用,那么我们可以开启本地模式,并在同一个属性文件中指定凭据:

camel.kubernetes-config.local-mode = true
mydb/myhost=localhost
mydb/myport=1234
mydb/myuser=scott
mydb/mypass=tiger
注意,键前面带有 Secret 名称和一个斜杠,例如 name/key。这样就便于从 configmap/secret 的实际使用处复制粘贴到 application.properties 文件中。

使用自定义属性占位符函数

Properties 组件允许接入第三方函数,这些函数可以在解析属性占位符时被使用。随后,这些函数可以执行自定义逻辑来解析占位符,例如查询数据库、进行自定义计算等。函数的名称会成为占位符中使用的前缀。

下面的示例路由最能说明这一点,我们在其中使用 beer 作为前缀:

  • Java
  • XML
  • YAML
from("direct:start")
  .to("{{beer:FOO}}")
  .to("{{beer:BAR}}");
<route>
    <from uri="direct:start"/>
    <to uri="{{beer:FOO}}"/>
    <to uri="{{beer:BAR}}"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: "{{beer:FOO}}"
        - to:
            uri: "{{beer:BAR}}"

该函数的实现仅需两个方法,如下所示:

@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 方法用于返回函数的名称(beer),而 apply 方法则用来实现自定义的处理逻辑。由于示例代码取自单元测试,它只返回一个用于引用模拟端点的值。

你还需要在构建该组件时包含 camel-component-maven-plugin Maven 插件,它将确保为这个自定义属性函数生成必要的源代码,从而使 Camel 能够自动发现该函数。

如果自定义属性函数需要在启动和停止时执行逻辑,那么该函数可以继承 ServiceSupport,并将这些逻辑放在 doStart 和 doStop 方法中。
相关示例可参见 camel-base64 组件。

评论

登录后参与评论

正在加载评论…