第 11 章 功能开发参考指南

11.1. 开发 Quarkus 功能

创建 Quarkus 功能项目后,您可以修改提供的模板文件,以将业务逻辑添加到您的功能中。这包括配置功能调用和返回的标头和状态代码。

11.1.1. 先决条件

11.1.2. Quarkus 功能模板结构

使用 Knative (kn) CLI 创建 Quarkus 功能时,项目目录类似于典型的 Maven 项目。另外,项目还包含用于配置功能的 func.yaml 文件。

httpevent 触发器功能具有相同的模板结构:

模板结构

.
├── func.yaml 1
├── mvnw
├── mvnw.cmd
├── pom.xml 2
├── README.md
└── src
    ├── main
    │   ├── java
    │   │   └── functions
    │   │       ├── Function.java 3
    │   │       ├── Input.java
    │   │       └── Output.java
    │   └── resources
    │       └── application.properties
    └── test
        └── java
            └── functions 4
                ├── FunctionTest.java
                └── NativeFunctionIT.java

1
用于确定镜像名称和 registry。
2
项目对象模型(POM)文件包含项目配置,如依赖项的相关信息。您可以通过修改此文件来添加额外的依赖项。

其他依赖项示例

...
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.8.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
...

依赖项在第一次编译时下载。

3
功能项目必须包含标有 @Funq 的 Java 方法。您可以将此方法放置在 Function.java 类中。
4
包含可用于在本地测试功能的简单测试案例。

11.1.3. 关于调用 Quarkus 功能

您可以创建一个 Quarkus 项目来响应云事件,或创建响应简单 HTTP 请求的 Quarkus 项目。Knative 中的云事件作为 POST 请求通过 HTTP 传输,因此任一功能类型都可以侦听和响应传入的 HTTP 请求。

收到传入请求时,通过允许类型的实例调用 Quarkus 函数。

表 11.1. 功能调用选项

调用方法实例中包含的数据类型数据示例

HTTP POST 请求

请求正文中的 JSON 对象

{ "customerId": "0123456", "productId": "6543210" }

HTTP GET 请求

查询字符串中的数据

?customerId=0123456&productId=6543210

CloudEvent

data 属性中的 JSON 对象

{ "customerId": "0123456", "productId": "6543210" }

以下示例显示了接收并处理上表中列出的 customerIdproductId 购买数据的函数:

Quarkus 功能示例

public class Functions {
    @Funq
    public void processPurchase(Purchase purchase) {
        // process the purchase
    }
}

包含购买数据的对应 Purchase JavaBean 类如下:

类示例

public class Purchase {
    private long customerId;
    private long productId;
    // getters and setters
}

11.1.3.1. 调用示例

以下示例代码定义了名为 withBeanswithCloudEventwithBinary 的三个功能;

示例

import io.quarkus.funqy.Funq;
import io.quarkus.funqy.knative.events.CloudEvent;

public class Input {
    private String message;

    // getters and setters
}

public class Output {
    private String message;

    // getters and setters
}

public class Functions {
    @Funq
    public Output withBeans(Input in) {
        // function body
    }

    @Funq
    public CloudEvent<Output> withCloudEvent(CloudEvent<Input> in) {
        // function body
    }

    @Funq
    public void withBinary(byte[] in) {
        // function body
    }
}

Functions 类的 withBeans 功能可以通过以下方法调用:

  • 带有 JSON 正文的 HTTP POST 请求:

    $ curl "http://localhost:8080/withBeans" -X POST \
        -H "Content-Type: application/json" \
        -d '{"message": "Hello there."}'
  • 带有查询参数的 HTTP GET 请求:

    $ curl "http://localhost:8080/withBeans?message=Hello%20there." -X GET
  • 二进制编码中的 CloudEvent 对象:

    $ curl "http://localhost:8080/" -X POST \
      -H "Content-Type: application/json" \
      -H "Ce-SpecVersion: 1.0" \
      -H "Ce-Type: withBeans" \
      -H "Ce-Source: cURL" \
      -H "Ce-Id: 42" \
      -d '{"message": "Hello there."}'
  • 结构化编码中的 CloudEvent 对象:

    $ curl http://localhost:8080/ \
        -H "Content-Type: application/cloudevents+json" \
        -d '{ "data": {"message":"Hello there."},
              "datacontenttype": "application/json",
              "id": "42",
              "source": "curl",
              "type": "withBeans",
              "specversion": "1.0"}'

withBeans 函数类似,可以利用 CloudEvent 对象来调用 Functions 类的 withCloudEvent 功能。但是,与 Beans 不同,CloudEvent 无法通过普通 HTTP 请求来调用。

Functions 类的 withBinary 功能可通过以下方式调用:

  • 二进制编码中的 CloudEvent 对象:

    $ curl "http://localhost:8080/" -X POST \
      -H "Content-Type: application/octet-stream" \
      -H "Ce-SpecVersion: 1.0"\
      -H "Ce-Type: withBinary" \
      -H "Ce-Source: cURL" \
      -H "Ce-Id: 42" \
      --data-binary '@img.jpg'
  • 结构化编码中的 CloudEvent 对象:

    $ curl http://localhost:8080/ \
      -H "Content-Type: application/cloudevents+json" \
      -d "{ \"data_base64\": \"$(base64 --wrap=0 img.jpg)\",
            \"datacontenttype\": \"application/octet-stream\",
            \"id\": \"42\",
            \"source\": \"curl\",
            \"type\": \"withBinary\",
            \"specversion\": \"1.0\"}"

11.1.4. CloudEvent 属性

如果您需要读取或写入 CloudEvent 的属性,如 typesubject,您可以使用 CloudEvent<T> 通用接口和 CloudEventBuilder 构建器。<T> 类型参数必须是允许的类型之一。

在以下示例中,CloudEventBuilder 用于返回处理订购的成功或失败:

public class Functions {

    private boolean _processPurchase(Purchase purchase) {
        // do stuff
    }

    public CloudEvent<Void> processPurchase(CloudEvent<Purchase> purchaseEvent) {
        System.out.println("subject is: " + purchaseEvent.subject());

        if (!_processPurchase(purchaseEvent.data())) {
            return CloudEventBuilder.create()
                    .type("purchase.error")
                    .build();
        }
        return CloudEventBuilder.create()
                .type("purchase.success")
                .build();
    }
}

11.1.5. Quarkus 功能返回值

功能可以从允许类型列表中返回任何类型的实例。另外,他们可以返回 Uni<T> 类型,其中 <T> 类型参数可以是允许的类型的任何类型。

如果函数调用异步 API,因为返回的对象以与接收对象相同的格式序列化,Uni<T> 类型很有用。例如:

  • 如果函数收到 HTTP 请求,则返回的对象将在 HTTP 响应的正文中发送。
  • 如果函数通过二进制编码收到 CloudEvent 对象,则返回的对象将在二进制编码的 CloudEvent 对象的 data 属性中发送。

以下示例显示了获取购买列表的功能:

示例命令

public class Functions {
    @Funq
    public List<Purchase> getPurchasesByName(String name) {
      // logic to retrieve purchases
    }
}

  • 通过 HTTP 请求调用此功能将生成 HTTP 响应,其中包含响应正文中的订购列表。
  • 通过传入的 CloudEvent 对象调用此功能可生成 CloudEvent 响应,并在 data 属性中包括一个订购列表。

11.1.5.1. 允许的类型

功能的输入和输出可以是 voidString、或 byte[] 类型。此外,它们也可以是原语类型及其打包程序,例如 intInteger。它们也可以是以下复杂的对象:Javabeans、映射、列表、数组和特殊的 CloudEvents<T> 类型。

映射、列出、数组、CloudEvents<T> 类型的 <T> 类型参数以及 Javabeans 的属性只能是此处列出的类型。

示例

public class Functions {
    public List<Integer> getIds();
    public Purchase[] getPurchasesByName(String name);
    public String getNameById(int id);
    public Map<String,Integer> getNameIdMapping();
    public void processImage(byte[] img);
}

11.1.6. 测试 Quarkus 功能

Quarkus 功能可以在您的计算机上进行本地测试。在使用 kn func create 创建功能时创建的默认项目中,有 src/test/ 目录,其中包含基本的 Maven 测试。这些测试可以根据需要扩展。

先决条件

  • 您已创建了 Quarkus 功能。
  • 已安装 Knative (kn) CLI。

流程

  1. 导航到您的功能的项目文件夹。
  2. 运行 Maven 测试:

    $ ./mvnw test

11.1.7. 覆盖存活度和就绪度探测值

您可以覆盖 Quarkus 功能的 存活度和就绪度探测 值。这可让您配置在功能上执行的健康检查。

先决条件

  • 在集群中安装了 OpenShift Serverless Operator 和 Knative Serving。
  • 已安装 Knative (kn) CLI。
  • 已使用 kn func create 创建功能。

流程

  1. 使用您自己的值覆盖 /health/liveness/health/readiness 路径。您可以通过在 func.yaml 文件中设置 QUARKUS_SMALLRYE_HEALTH_LIVENESS_PATHQUARKUS_SMALLRYE_HEALTH_READINESS_PATH 环境变量来完成此操作。

    1. 要使用功能源覆盖路径,更新 src/main/resources/application.properties 文件中的路径属性:

      quarkus.smallrye-health.root-path=/health 1
      quarkus.smallrye-health.liveness-path=alive 2
      quarkus.smallrye-health.readiness-path=ready 3
      1
      根路径,其自动添加到 存活度就绪度 路径的前面。
      2
      存活度路径,设置为此处的 /health/alive
      3
      就绪度路径,设置为 /health/ready
    2. 要使用环境变量覆盖路径,请在 func.yaml 文件的构建块中定义路径变量:

      build:
        builder: s2i
        buildEnvs:
        - name: QUARKUS_SMALLRYE_HEALTH_LIVENESS_PATH
          value: alive 1
        - name: QUARKUS_SMALLRYE_HEALTH_READINESS_PATH
          value: ready 2
      1
      存活度路径,设置为此处的 /health/alive
      2
      就绪度路径,设置为 /health/ready
  2. 将新端点添加到 func.yaml 文件中,以便它们正确绑定到 Knative 服务的容器:

    deploy:
      healthEndpoints:
        liveness: /health/alive
        readiness: /health/ready

11.1.8. 后续步骤