×

先决条件

Node.js 函数模板结构

使用 Knative (kn) CLI 创建 Node.js 函数时,项目目录看起来像一个典型的 Node.js 项目。唯一的例外是附加的 func.yaml 文件,该文件用于配置函数。

httpevent 触发器函数具有相同的模板结构

模板结构
.
├── func.yaml (1)
├── index.js (2)
├── package.json (3)
├── README.md
└── test (4)
    ├── integration.js
    └── unit.js
1 func.yaml 配置文件用于确定镜像名称和注册表。
2 您的项目必须包含一个导出单个函数的 index.js 文件。
3 您不限于模板 package.json 文件中提供的依赖项。您可以像在任何其他 Node.js 项目中一样添加其他依赖项。
添加 npm 依赖项的示例
npm install --save opossum

为部署构建项目时,这些依赖项将包含在创建的运行时容器镜像中。

4 集成和单元测试脚本作为函数模板的一部分提供。

关于调用 Node.js 函数

使用 Knative (kn) CLI 创建函数项目时,您可以生成一个响应 CloudEvents 的项目,或者一个响应简单 HTTP 请求的项目。Knative 中的 CloudEvents 作为 POST 请求通过 HTTP 传输,因此两种函数类型都侦听并响应传入的 HTTP 事件。

Node.js 函数可以使用简单的 HTTP 请求调用。收到传入请求时,将使用 context 对象作为第一个参数调用函数。

Node.js 上下文对象

通过提供 context 对象作为第一个参数来调用函数。此对象提供对传入 HTTP 请求信息的访问。

此信息包括 HTTP 请求方法、与请求一起发送的任何查询字符串或头信息、HTTP 版本和请求主体。包含 CloudEvent 的传入请求会将传入的 CloudEvent 实例附加到上下文对象,以便可以通过使用 context.cloudevent 来访问它。

上下文对象方法

context 对象有一个单一方法 cloudEventResponse(),它接受数据值并返回 CloudEvent。

在 Knative 系统中,如果作为服务部署的函数由发送 CloudEvent 的事件代理调用,则代理会检查响应。如果响应是 CloudEvent,则此事件将由代理处理。

示例上下文对象方法
// Expects to receive a CloudEvent with customer data
function handle(context, customer) {
  // process the customer
  const processed = handle(customer);
  return context.cloudEventResponse(customer)
    .source('/handle')
    .type('fn.process.customer')
    .response();
}

CloudEvent 数据

如果传入请求是 CloudEvent,则会从事件中提取与 CloudEvent 关联的任何数据,并作为第二个参数提供。例如,如果收到一个 CloudEvent,其 data 属性中包含类似于以下内容的 JSON 字符串

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

调用时,函数的第二个参数(在 context 对象之后)将是一个 JavaScript 对象,它具有 customerIdproductId 属性。

示例签名
function handle(context, data)

此示例中的 data 参数是一个 JavaScript 对象,包含 customerIdproductId 属性。

任意数据

函数可以接收任何数据,而不仅仅是 CloudEvents。例如,您可能希望使用 POST 方法并使用主体中的任意对象来调用函数

{
  "id": "12345",
  "contact": {
    "title": "Mr.",
    "firstname": "John",
    "lastname": "Smith"
  }
}

在这种情况下,您可以按如下方式定义函数:

function handle(context, customer) {
  return "Hello " + customer.contact.title + " " + customer.contact.lastname;
}

向函数提供 contact 对象将返回以下输出:

Hello Mr. Smith

支持的数据类型

CloudEvents 可以包含各种数据类型,包括 JSON、XML、纯文本和二进制数据。这些数据类型将以各自的格式提供给函数。

  • JSON 数据:作为 JavaScript 对象提供。

  • XML 数据:作为 XML 文档提供。

  • 纯文本:作为字符串提供。

  • 二进制数据:作为 Buffer 对象提供。

函数中的多种数据类型

通过检查 Content-Type 标头并相应地解析数据,确保您的函数可以处理不同的数据类型。例如:

function handle(context, data) {
  if (context.headers['content-type'] === 'application/json') {
    // handle JSON data
  } else if (context.headers['content-type'] === 'application/xml') {
    // handle XML data
  } else {
    // handle other data types
  }
}

Node.js 函数返回值

函数可以返回任何有效的 JavaScript 类型,也可以没有返回值。当函数没有指定返回值并且没有指示错误时,调用者将收到 204 No Content 响应。

函数也可以返回 CloudEvent 或 Message 对象,以便将事件推送到 Knative Eventing 系统。在这种情况下,开发人员不需要理解或实现 CloudEvent 消息传递规范。从返回值中提取标头和其他相关信息,并随响应一起发送。

示例
function handle(context, customer) {
  // process customer and return a new CloudEvent
  return new CloudEvent({
    source: 'customer.processor',
    type: 'customer.processed'
  })
}

返回基本类型

函数可以返回任何有效的 JavaScript 类型,包括字符串、数字和布尔值等基本类型。

返回字符串的示例函数:
function handle(context) {
  return "This function Works!"
}

调用此函数将返回以下字符串:

$ curl https://myfunction.example.com
This function Works!
返回数字的示例函数:
function handle(context) {
  let somenumber = 100
  return { body: somenumber }
}

调用此函数将返回以下数字:

$ curl https://myfunction.example.com
100
返回布尔值的示例函数:
function handle(context) {
  let someboolean = false
  return { body: someboolean }
}

调用此函数将返回以下布尔值:

$ curl https://myfunction.example.com
false

直接返回基本类型而不用对象包装它们会导致返回 204 No Content 状态码以及空主体。

直接返回基本类型的示例函数:
function handle(context) {
  let someboolean = false
  return someboolean
}

调用此函数将返回以下内容:

$ http :8080
HTTP/1.1 204 No Content
Connection: keep-alive
...

返回标头

您可以通过向 return 对象添加 headers 属性来设置响应标头。这些标头将被提取并与响应一起发送给调用者。

示例响应标头:
function handle(context, customer) {
  // process customer and return custom headers
  // the response will be '204 No content'
  return { headers: { customerid: customer.id } };
}

返回状态码

您可以通过向 return 对象添加 statusCode 属性来设置返回给调用者的状态码。

示例状态码:
function handle(context, customer) {
  // process customer
  if (customer.restricted) {
    return { statusCode: 451 }
  }
}

也可以为函数创建和抛出的错误设置状态码。

示例错误状态码:
function handle(context, customer) {
  // process customer
  if (customer.restricted) {
    const err = new Error(Unavailable for legal reasons);
    err.statusCode = 451;
    throw err;
  }
}

测试 Node.js 函数

可以在您的计算机上本地测试 Node.js 函数。使用 kn func create 创建函数时创建的默认项目中,有一个包含一些简单的单元和集成测试的 **test** 文件夹。

先决条件
  • 集群上已安装 OpenShift Serverless Operator 和 Knative Serving。

  • 您已安装 Knative (kn) CLI。

  • 您已使用 kn func create 创建了一个函数。

步骤
  1. 导航到函数的 **test** 文件夹。

  2. 运行测试:

    $ npm test

覆盖存活性探针和就绪性探针的值

您可以覆盖 Node.js 函数的 livenessreadiness 探针值。这允许您配置对函数执行的健康检查。

先决条件
  • 集群上已安装 OpenShift Serverless Operator 和 Knative Serving。

  • 您已安装 Knative (kn) CLI。

  • 您已使用 kn func create 创建了一个函数。

步骤
  1. 在您的函数代码中,创建 Function 对象,该对象实现以下接口:

    export interface Function {
      init?: () => any; (1)
    
      shutdown?: () => any; (2)
    
      liveness?: HealthCheck; (3)
    
      readiness?: HealthCheck; (4)
    
      logLevel?: LogLevel;
    
      handle: CloudEventFunction | HTTPFunction; (5)
    }
    1 初始化函数,在服务器启动之前调用。此函数是可选的,应为同步函数。
    2 关闭函数,在服务器停止后调用。此函数是可选的,应为同步函数。
    3 存活性函数,用于检查服务器是否存活。此函数是可选的,如果服务器存活,则应返回 200/OK。
    4 就绪性函数,用于检查服务器是否已准备好接受请求。此函数是可选的,如果服务器已准备好,则应返回 200/OK。
    5 用于处理 HTTP 请求的函数。

    例如,将以下代码添加到 index.js 文件:

    const Function = {
    
      handle: (context, body) => {
        // The function logic goes here
        return 'function called'
      },
    
      liveness: () => {
        process.stdout.write('In liveness\n');
        return 'ok, alive';
      }, (1)
    
      readiness: () => {
        process.stdout.write('In readiness\n');
        return 'ok, ready';
      } (2)
    };
    
    Function.liveness.path = '/alive'; (3)
    Function.readiness.path = '/ready'; (4)
    
    module.exports = Function;
    1 自定义 liveness 函数。
    2 自定义 readiness 函数。
    3 自定义 liveness 端点。
    4 自定义 readiness 端点。

    作为 Function.liveness.pathFunction.readiness.path 的替代方法,您可以使用 LIVENESS_URLREADINESS_URL 环境变量指定自定义端点。

    run:
      envs:
      - name: LIVENESS_URL
        value: /alive (1)
      - name: READINESS_URL
        value: /ready (2)
    1 存活性路径,此处设置为 /alive
    2 就绪性路径,此处设置为 /ready
  2. 将新的端点添加到 func.yaml 文件中,以便它们正确绑定到 Knative 服务的容器。

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

Node.js 上下文对象参考

context 对象具有函数开发人员可以访问的多个属性。访问这些属性可以提供有关 HTTP 请求的信息并将输出写入集群日志。

log

提供一个日志记录对象,可用于将输出写入集群日志。该日志符合 Pino 日志记录 API

示例日志:
function handle(context) {
  context.log.info(Processing customer);
}

您可以使用 kn func invoke 命令访问该函数。

示例命令:
$ kn func invoke --target 'http://example.function.com'
示例输出:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"Processing customer"}

您可以将日志级别更改为 fatalerrorwarninfodebugtracesilent 之一。为此,请通过将这些值之一分配给使用 config 命令的环境变量 FUNC_LOG_LEVEL 来更改 logLevel 的值。

query

返回请求的查询字符串(如果有),作为键值对。这些属性也位于上下文对象本身。

示例查询:
function handle(context) {
  // Log the 'name' query parameter
  context.log.info(context.query.name);
  // Query parameters are also attached to the context
  context.log.info(context.name);
}

您可以使用 kn func invoke 命令访问该函数。

示例命令:
$ kn func invoke --target 'http://example.com?name=tiger'
示例输出:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}

body

返回请求主体(如果有)。如果请求主体包含 JSON 代码,则将对其进行解析,以便可以直接使用这些属性。

示例主体:
function handle(context) {
  // log the incoming request body's 'hello' parameter
  context.log.info(context.body.hello);
}

您可以使用 curl 命令调用该函数来访问它。

示例命令:
$ kn func invoke -d '{"Hello": "world"}'
示例输出:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"world"}

headers

返回 HTTP 请求头作为对象。

示例头部
function handle(context) {
  context.log.info(context.headers["custom-header"]);
}

您可以使用 kn func invoke 命令访问该函数。

示例命令:
$ kn func invoke --target 'http://example.function.com'
示例输出:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"some-value"}

HTTP 请求

方法

返回 HTTP 请求方法,以字符串形式表示。

HTTP 版本

返回 HTTP 版本,以字符串形式表示。

HTTP 主版本号

返回 HTTP 主版本号,以字符串形式表示。

HTTP 次版本号

返回 HTTP 次版本号,以字符串形式表示。

后续步骤