.
├── func.yaml (1)
├── package.json (2)
├── package-lock.json
├── README.md
├── src
│ └── index.ts (3)
├── test (4)
│ ├── integration.ts
│ └── unit.ts
└── tsconfig.json
在您创建了 TypeScript 函数项目之后,您可以修改提供的模板文件以向您的函数添加业务逻辑。这包括配置函数调用以及返回的头信息和状态码。
在开发函数之前,您必须完成配置 OpenShift Serverless 函数中的步骤。
使用 Knative (kn) CLI 创建 TypeScript 函数时,项目目录看起来像一个典型的 TypeScript 项目。唯一的例外是额外的 func.yaml 文件,该文件用于配置函数。
http 和 event 触发函数具有相同的模板结构。
.
├── func.yaml (1)
├── package.json (2)
├── package-lock.json
├── README.md
├── src
│ └── index.ts (3)
├── test (4)
│ ├── integration.ts
│ └── unit.ts
└── tsconfig.json
| 1 | func.yaml 配置文件用于确定镜像名称和注册表。 |
| 2 | 您不限于模板 package.json 文件中提供的依赖项。您可以像在任何其他 TypeScript 项目中一样添加其他依赖项。添加 npm 依赖项的示例
构建项目以进行部署时,这些依赖项将包含在创建的运行时容器镜像中。 |
| 3 | 您的项目必须包含一个导出名为 handle 的函数的 src/index.js 文件。 |
| 4 | 集成和单元测试脚本作为函数模板的一部分提供。 |
使用 Knative (kn) CLI 创建函数项目时,您可以生成一个响应 CloudEvents 的项目,或一个响应简单 HTTP 请求的项目。Knative 中的 CloudEvents 通过 HTTP 作为 POST 请求传输,因此两种类型的函数都侦听并响应传入的 HTTP 事件。
TypeScript 函数可以使用简单的 HTTP 请求调用。收到传入请求时,将使用 context 对象作为第一个参数调用函数。
要调用函数,请提供 context 对象作为第一个参数。访问 context 对象的属性可以提供有关传入 HTTP 请求的信息。
function handle(context:Context): string
此信息包括 HTTP 请求方法、随请求发送的任何查询字符串或标头、HTTP 版本以及请求正文。包含CloudEvent的传入请求会将传入的 CloudEvent 实例附加到上下文对象,以便可以使用context.cloudevent访问它。
context 对象有一个方法cloudEventResponse(),它接受一个数据值并返回一个 CloudEvent。
在 Knative 系统中,如果作为服务部署的函数由发送 CloudEvent 的事件代理调用,则代理会检查响应。如果响应是 CloudEvent,则此事件将由代理处理。
// Expects to receive a CloudEvent with customer data
export function handle(context: Context, cloudevent?: CloudEvent): CloudEvent {
// process the customer
const customer = cloudevent.data;
const processed = processCustomer(customer);
return context.cloudEventResponse(customer)
.source('/customer/process')
.type('customer.processed')
.response();
}
TypeScript 类型定义文件导出以下类型供您的函数使用。
// Invokable is the expeted Function signature for user functions
export interface Invokable {
(context: Context, cloudevent?: CloudEvent): any
}
// Logger can be used for structural logging to the console
export interface Logger {
debug: (msg: any) => void,
info: (msg: any) => void,
warn: (msg: any) => void,
error: (msg: any) => void,
fatal: (msg: any) => void,
trace: (msg: any) => void,
}
// Context represents the function invocation context, and provides
// access to the event itself as well as raw HTTP objects.
export interface Context {
log: Logger;
req: IncomingMessage;
query?: Record<string, any>;
body?: Record<string, any>|string;
method: string;
headers: IncomingHttpHeaders;
httpVersion: string;
httpVersionMajor: number;
httpVersionMinor: number;
cloudevent: CloudEvent;
cloudEventResponse(data: string|object): CloudEventResponse;
}
// CloudEventResponse is a convenience class used to create
// CloudEvents on function returns
export interface CloudEventResponse {
id(id: string): CloudEventResponse;
source(source: string): CloudEventResponse;
type(type: string): CloudEventResponse;
version(version: string): CloudEventResponse;
response(): CloudEvent;
}
如果传入请求是 CloudEvent,则会从事件中提取与 CloudEvent 关联的任何数据,并作为第二个参数提供。例如,如果收到一个 CloudEvent,其 data 属性中包含类似于以下内容的 JSON 字符串
{
"customerId": "0123456",
"productId": "6543210"
}
调用时,函数的第二个参数(在context对象之后)将是一个具有customerId和productId属性的 JavaScript 对象。
function handle(context: Context, cloudevent?: CloudEvent): CloudEvent
在此示例中,cloudevent参数是一个包含customerId和productId属性的 JavaScript 对象。
函数可以返回任何有效的 JavaScript 类型,也可以没有返回值。当函数没有指定返回值且没有指示失败时,调用方会收到204 No Content响应。
函数还可以返回 CloudEvent 或Message对象,以便将事件推送到 Knative Eventing 系统。在这种情况下,开发人员无需理解或实现 CloudEvent 消息传递规范。将提取返回值中的标头和其他相关信息,并随响应一起发送。
export const handle: Invokable = function (
context: Context,
cloudevent?: CloudEvent
): Message {
// process customer and return a new CloudEvent
const customer = cloudevent.data;
return HTTP.binary(
new CloudEvent({
source: 'customer.processor',
type: 'customer.processed'
})
);
};
您可以通过向return对象添加headers属性来设置响应标头。这些标头将被提取并随响应一起发送给调用方。
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, any> {
// process customer and return custom headers
const customer = cloudevent.data as Record<string, any>;
return { headers: { 'customer-id': customer.id } };
}
您可以通过向return对象添加statusCode属性来设置返回给调用方的状态代码。
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, any> {
// process customer
const customer = cloudevent.data as Record<string, any>;
if (customer.restricted) {
return {
statusCode: 451
}
}
// business logic, then
return {
statusCode: 240
}
}
也可以为函数创建和抛出的错误设置状态代码。
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, string> {
// process customer
const customer = cloudevent.data as Record<string, any>;
if (customer.restricted) {
const err = new Error(‘Unavailable for legal reasons’);
err.statusCode = 451;
throw err;
}
}
可以在您的计算机上本地测试 TypeScript 函数。使用kn func create创建函数时创建的默认项目中,有一个包含一些简单的单元测试和集成测试的test文件夹。
集群上已安装 OpenShift Serverless Operator 和 Knative Serving。
您已安装 Knative (kn) CLI。
您已使用kn func create创建了一个函数。
如果您以前没有运行过测试,请先安装依赖项。
$ npm install
导航到函数的test文件夹。
运行测试
$ npm test
您可以覆盖 TypeScript 函数的liveness和readiness探针值。这允许您配置对函数执行的运行状况检查。
集群上已安装 OpenShift Serverless Operator 和 Knative Serving。
您已安装 Knative (kn) CLI。
您已使用kn func create创建了一个函数。
在您的函数代码中,创建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.path和Function.readiness.path的替代方法,您可以使用LIVENESS_URL和READINESS_URL环境变量指定自定义端点。
run:
envs:
- name: LIVENESS_URL
value: /alive (1)
- name: READINESS_URL
value: /ready (2)
| 1 | 存活性路径,此处设置为/alive。 |
| 2 | 就绪路径,此处设置为/ready。 |
将新的端点添加到func.yaml文件中,以便它们正确绑定到 Knative 服务的容器。
deploy:
healthEndpoints:
liveness: /alive
readiness: /ready
context对象有一些函数开发人员可以访问的属性。访问这些属性可以提供有关传入 HTTP 请求的信息并将输出写入集群日志。
提供一个日志记录对象,可用于将输出写入集群日志。该日志符合Pino 日志 API。
export function handle(context: Context): string {
// log the incoming request body's 'hello' parameter
if (context.body) {
context.log.info((context.body as Record<string, string>).hello);
} else {
context.log.info('No data received');
}
return 'OK';
}
您可以使用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"}
您可以将日志级别更改为fatal、error、warn、info、debug、trace或silent之一。为此,请通过使用config命令将这些值之一分配给环境变量FUNC_LOG_LEVEL来更改logLevel的值。
返回请求的查询字符串(如果有),作为键值对。这些属性也位于上下文对象本身中。
export function handle(context: Context): string {
// log the 'name' query parameter
if (context.query) {
context.log.info((context.query as Record<string, string>).name);
} else {
context.log.info('No data received');
}
return 'OK';
}
您可以使用kn func invoke命令访问该函数。
$ kn func invoke --target 'http://example.function.com' --data '{"name": "tiger"}'
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
返回请求正文(如果有)。如果请求正文包含 JSON 代码,则会对其进行解析,以便可以直接使用属性。
export function handle(context: Context): string {
// log the incoming request body's 'hello' parameter
if (context.body) {
context.log.info((context.body as Record<string, string>).hello);
} else {
context.log.info('No data received');
}
return 'OK';
}
您可以使用kn func invoke命令访问该函数。
$ kn func invoke --target 'http://example.function.com' --data '{"hello": "world"}'
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"world"}
将 HTTP 请求标头作为对象返回。
export function handle(context: Context): string {
// log the incoming request body's 'hello' parameter
if (context.body) {
context.log.info((context.headers as Record<string, string>)['custom-header']);
} else {
context.log.info('No data received');
}
return 'OK';
}
您可以使用curl命令调用该函数来访问它。
$ curl -H'x-custom-header: some-value’' http://example.function.com
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"some-value"}
有关使用函数进行日志记录的更多信息,请参阅Pino API 文档。