.
├── 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 文档。