Serverless 的定义和理解在不同的角度和场景会有不同的解读,AWS 将 Serverless(在 AWS 云上) 定义为 “是一种用于描述服务、实践和策略的方式,使您能够构建更敏捷的应用程序,从而能够更快地创新和响应变化”的一种服务。 而红帽认为 Serverless 是 “可使开发人员专注构建和运行应用,而无需管理服务器” 的一种开发模型,并进一步将 Serverless 的产品分为两类:BaaS(后端即服务,让开发人员访问各种各样的第三方服务和应用) 与 FaaS(功能即服务,开发人员编写逻辑,部署到完全由平台管理的容器中,然后按需执行) 两种形态。 而 Serverless Framework 则认为 Serverless 是“一场由开发人员和企业推动,让单个开发人员可以完成高流量的应用开发,同时只将精力集中在产生价值的方面”的运动,
不管哪个方面,哪种角度,Serverless 都具有以下共同特点:
而目前都是基于各个云厂商的 FaaS 服务来实现,如: 腾讯云的 SCF, AWS 的 Lambda, Azure 云的 Azure Funcitons 等。
随着计算能力的加强,系统复杂度的增加,用户规模的增长,软件问题(如下, 也称为软件危机)也会发生指数型的增长。
而 Serverless 则可以通过以下方式提出了对于软件危机问题的解决方案:
同时在现在普遍倡导敏捷工作方式的现代工作环境中,Serverless 也为快速验证想法、迭代功能提供了开发方式的最佳实践,同时而不需要担心代码改动会影响系统的其他功能,也无需考虑部署前的服务器配置以及部署后的维护工作。
Serverless Framework 是业界非常受欢迎的无服务器应用框架,通过与众多一流云供应商如腾讯云,AWS 等的紧密合作,为广大开发者提供无需关心底层基础设施,即可编写和部署代码的无服务开发体验。
Serverless Framework 同时提供资源管理、自动伸缩、统计分析等能力,让广大开发者可以节省运维成本,真正做到“按量付费”的同时,也无需花费精力处理日志收集、异常统计等任务。
Serverless Framework 通过 CLI 工具与腾讯云紧密合作,为中国用户提供了基于组件(serverless components)的完整解决方案。覆盖了无服务应用编码、测试、部署等全生命周期,同时切合中国用户的使用场景和习惯。
通过 Serverless Framework 的短短几行配置文件和 CLI 工具,开发者就可以额外获得:
本示例使用 Serverless Framework 的多函数组件(multi-scf)和 PostgreSQL 组件(postgresql),实现以下 3 个 API 接口。
GET /todos/
获取所有的 todo 事项POST /todos/
创建新的 todo 事项POST /todos/{id}/actions/complete
完成 todo 事项并使用 Serverless Framework 提供的 invoke 和 logs 功能进行调试以及查看生产环境实时日志。
本示例相关代码可以在Git 仓库中获取。
执行以下命令安装 Serverless Framework
$ npm install serverless -g
如果之前您已经安装过 Serverless Framework,可以通过下列命令升级到最新版:
$ npm update serverless -g
此命令会安装最新的 Serverless Framework 到你的计算机,安装成功后可以通过 serverless
或者 sls
开始使用 Serverless Framework
$ sls init multi-scf-nodejs --name sls-demo-msn-todo
此命令会使用应用模板 multi-scf-nodejs
初始化名为 my-multi-scf-demo
的应用目录。初始化成功后该目录结构为
.
├── README.md
├── index.js
└── serverless.yml
这里的文件用途如下:
因为 Serverless 是无状态的(运行后就会销毁), 所以这里需要链接数据库用来持久化 todo 信息。添加数据库需要先借助 VPC 网络连接。
创建子目录 vpc
并在子目录中添加新的 serverless.yml
文件如下:
component: vpc # [必选]要使用组件,更多组件请查看 https://github.com/serverless-components
name: sls-demo-msn-vpc # [必选]组件实例名称
inputs:
region: ap-guangzhou # 实例所属地区
zone: ap-guangzhou-2 # 实例所属地区区域
vpcName: ${name} # 实例名称,这里复用字段 name 作为名称。
subnetName: sls-demo-msn-subnet # 子网的名称
更多 VPC 的配置内容,查看 VPC 私有网络 获取更多详情信息。
在子组件的配置文件中,app 名称会自动继承父目录的 serverless.yml 中的配置。 同时同一个应用的 app 名称需要保持一致。
创建子目录 db
并在子目录中添加新的 serverless.yml
文件如下:
component: postgresql #(必填) 引用 component 的名称,当前用到的是 postgresql 组件
name: sls-demo-msn-DB # (必填) 该 postgresql 组件创建的实例名称
inputs:
region: ap-guangzhou # 实例所属地区
zone: ap-guangzhou-2 # 实例所属地区区域
dBInstanceName: ${name}-${stage} # 数据库实例名称唯一,且同一个数据库只能存在同一个vpc内。
extranetAccess: true # 是否开启实例外网访问
vpcConfig: # vpc网络配置
vpcId: ${output:${stage}:${app}:sls-demo-msn-vpc.vpcId} # 私有网络Id
subnetId: ${output:${stage}:${app}:sls-demo-msn-vpc.subnetId} # 子网Id
在数据库配置中添加数据库到 vpc 网络,这里使用输出变量(output)来动态获取 vpc 的 id 信息。
更多变量的配置内容,查看 Serverless 变量 获取更多详情信息。
更多 PostgreSQL 的配置内容,查看 PostgreSQL 数据库 获取更多详情信息。
在组件部署完成后,可以在组件目录内,使用
sls info
查看组件的输出变量,或者可以到腾讯云的应用控制台查看相关信息。
src
并将创建生成的 index.js
(重命名为todos.js
) 和 serverless.yml
移动到目录中。src
目录中执行npm init
初始化 Node.js 项目。src
目录中执行npm i pg --save
安装数据库链接依赖包pg
。serverless.yml
,文件如下:app: sls-demo-msn-todo-3e5a2134 # 应用唯一识别标识,同账号下需要保持唯一。
stage: dev # 应用部署环境名称,这里使用环境变量 STAGE 的值。
根目录的配置文件信息会被子组件继承,不需要在子组件中重复定义。(仅限于,app 与 stage)。
最终完成的项目目录结构如下:
.
├── README.md
├── db # 数据库
│ └── serverless.yml # 数据库配置文件
├── serverless.yml
├── src # 多函数应用
│ ├── node_modules
│ ├── package-lock.json
│ ├── package.json # Node.js依赖文件
│ ├── serverless.yml # 多函数应用配置文件
│ └── todos.js # todo 应用主文件
└── vpc # vpc
└── serverless.yml # vpc配置文件
在多函数目录src
内修改配置文件如下:
component: multi-scf
name: sls-demo-msn
inputs:
src:
src: ./
exclude:
- .env
- "node_modules/**" # 部署时忽略node_modules目录中所有文件,以加快部署速度
environments: # 应用环境变量信息
- key: PG_CONNECT_STRING
value: ${output:${stage}:${app}:sls-demo-msn-DB.private.connectionString}
region: ap-guangzhou
runtime: Nodejs12.16
memorySize: 128
vpc: # vpc网络配置
vpcId: ${output:${stage}:${app}:sls-demo-msn-vpc.vpcId} # 私有网络Id
subnetId: ${output:${stage}:${app}:sls-demo-msn-vpc.subnetId} # 子网Id
installDependency: true # 是否在线安装依赖
timeout: 6 # 默认超时时间(秒)
functions: # 多函数定义
allTodo: # 函数别名
handler: todos.all # 处理函数
memorySize: 256 # 自定义次函数的内存空间
addTodo:
handler: todos.add
timeout: 9 # 自定义此函数的超时时间(秒)
completeTodo:
handler: todos.comp
timeout: 9
triggers: # 触发器配置
- type: apigw
parameters:
name: todosAPIGW
protocols:
- https
- http
apis: # API配置
- path: /todos/ # 路由路径
method: GET # 路由方法
function: allTodo # 路由处理函数别名
- path: /todos/
method: POST
function: addTodo
- path: /todos/{id}/actions/complete
method: POST
function: completeTodo
param: # 动态路由参数配置
- name: id
position: PATH
required: true
type: number
desc: Todo ID
这里修改主要内容有:
installDependency
开启部署后依赖自动安装并忽略node_module
目录下的全部文件(无需上传 node_modules,加快部署)vpc
添加 vpc 网络并链接到项目同一个 vpc 网络中。environments
添加项目环境变量,并使用输出变量(output)来动态生成数据库连接字符串。functions
来声明项目中的函数及其别名。triggers
声明函数的触发器,并在触发器的apis
中配置各个函数对应的路径,以及参数信息。更多 函数开发 的说明内容,查看 函数开发 获取更多详情信息。
修改todos.js
并完成相关功能的开发,最终该文件代码如下:
"use strict";
const { Client } = require("pg");
const client = new Client({
connectionString: process.env.PG_CONNECT_STRING,
});
/**
* 初始化数据库和表结构
*/
const initDB = async () => {
const isConnected = client && client._connected;
if (!isConnected) {
await client.connect();
await client.query(`
CREATE TABLE IF NOT EXISTS todo (
ID SERIAL NOT NULL,
TITLE VARCHAR NOT NULL,
NOTE TEXT,
IS_COMPLETE BOOLEAN DEFAULT FALSE
);`);
}
};
/**
* 获取所有Todo事项
*/
exports.all = async (event, context) => {
// async 需要关闭事件循环等待,以避免日志记录超时或函数无返回的问题。
context.callbackWaitsForEmptyEventLoop = false;
await initDB();
const { rows } = await client.query({ text: "SELECT * FROM todo" });
return {
message: "Tencent SCF execute successful!",
data: rows,
};
};
/**
* 添加新的Todo事项
*/
exports.add = async (event, context) => {
// async 需要关闭事件循环等待,以避免日志记录超时或函数无返回的问题。
context.callbackWaitsForEmptyEventLoop = false;
const { title, note } = JSON.parse(event.body);
if (!title) {
return {
statusCode: 400,
message: "Missing Todo Title",
};
}
await initDB();
const { rowCount } = await client.query({
text: "INSERT INTO todo (title, note) VALUES($1, $2)",
values: [title, note],
});
return rowCount === 1
? {
statusCode: 201,
message: "Todo added success.",
}
: {
statusCode: 400,
message: "Todo added failed.",
};
};
/**
* 完成指定Todo事项
*/
exports.comp = async (event, context) => {
// async 需要关闭事件循环等待,以避免日志记录超时或函数无返回的问题。
context.callbackWaitsForEmptyEventLoop = false;
const todoId = event.pathParameters.id;
if (!todoId && !isNaN(todoId)) {
return {
statusCode: 400,
message: "Missing Todo Id",
};
}
await initDB();
const { rowCount } = await client.query({
text: "UPDATE todo SET is_complete = true WHERE id=$1",
values: [todoId],
});
return rowCount === 1
? {
statusCode: 200,
message: "Todo Complete success.",
}
: {
statusCode: 400,
message: "Todo Complete failed.",
};
};
要调试代码除了使用第三方开发工具通过配置的 API 网关 url 调试,还可以使用 Serverless Framework 的 Invoke 功能 或 远程调试 功能. 这里使用 invoke 功能演示如何调试函数功能。
invoke 和 远程调试功能 需要在组件的目录内执行。
在 src 目录下执行
$ serverless invoke -f allTodo
执行后可以得到结果
使用授权信息 default 授权中,如果需要使用临时密钥,请使用 --login 重新登陆
billDuration: 36
duration: 36
errMsg:
functionRequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
invokeResult: 0
log:
"""
START RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
Event RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
END RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3
Report RequestId: fe6d302d-f6db-42ad-9c7b-8d0c61ead9b3 Duration:36ms Memory:256MB MemUsage:11.3984MB
"""
memUsage: 11952128
---------------------------------------------
Serverless: 调用成功
{
message: 'Tencent SCF execute successful!',
data: []
}
在 invoke 返回的结果中,会包含函数执行后的 meta 信息,如运行时间,错误,RequestId,执行的日志 和函数返回的结果。
在 src 目录下执行
$ serverless invoke -f addTodo --data "{\"body\":\"{\\\"title\\\":\\\"Create multi-scf project demo\\\",\\\"note\\\":\\\"Todo App with postgreSQL\\\"}\"}"
执行后可以得到结果
使用授权信息 default 授权中,如果需要使用临时密钥,请使用 --login 重新登陆
billDuration: 35
duration: 35
errMsg:
functionRequestId: 93f50016-064f-468d-9e98-31645fc254fd
invokeResult: 0
log:
"""
START RequestId: 93f50016-064f-468d-9e98-31645fc254fd
Event RequestId: 93f50016-064f-468d-9e98-31645fc254fd
END RequestId: 93f50016-064f-468d-9e98-31645fc254fd
Report RequestId: 93f50016-064f-468d-9e98-31645fc254fd Duration:35ms Memory:128MB MemUsage:11.293MB
"""
memUsage: 11841536
---------------------------------------------
Serverless: 调用成功
{
statusCode: 201,
message: 'Todo added success.'
}
使用下面命令可以快速部署项目到生产环境(这里命名生产环境为prod
)
$ serverless deploy --stage prod
在项目目录src
中执行以下命令可以查看项目的即时日志信息
$ sls logs --tail -f allTodo --stage prod
以下是返回结果:
使用授权信息 default 授权中,如果需要使用临时密钥,请使用 --login 重新登陆
serverless ⚡components
Action: "logs" - Stage: "prod" - App: "sls-demo-msn-todo-3e5a2134" - Name: "sls-demo-msn"
START RequestId:6f31857109130f092c547337c073ea91
Response RequestId:dbb3a8ed63a32be8e6b7a2dd8a32bbe2 RetMsg:{"message":"Tencent SCF execute successful!","data":[{"id":1,"title":"Create multi-scf project demo","note":"Todo App with postgreSQL","is_complete":false}]}
END RequestId:dbb3a8ed63a32be8e6b7a2dd8a32bbe2
Report RequestId:dbb3a8ed63a32be8e6b7a2dd8a32bbe2 Duration:4ms Memory:256MB MemUsage:12.113281MB
Response RequestId:485a87cfc6ad385b7e9c84343962391b RetMsg:{"message":"Tencent SCF execute successful!","data":[{"id":1,"title":"Create multi-scf project demo","note":"Todo App with postgreSQL","is_complete":false}]}
END RequestId:485a87cfc6ad385b7e9c84343962391b
Report RequestId:485a87cfc6ad385b7e9c84343962391b Duration:4ms Memory:256MB MemUsage:11.886719MB
START RequestId:0ede6d26dca55362a701c10ff51c9021
Serverless › 监听中 ...
感谢长久以来对 Serverless Framework 支持的广大开发者,未来我们也会继续迭代产品,推出新功能,改进产品使用体验,最终我们会为中国的开发者打造一套符合中国开发者习惯的无服务器开发的完整解决方案。
也欢迎大家到 Serverless 中文社区分享经验和想法以及反馈问题和 BUG。
Serverless 中文社区:https://github.com/serverless/serverless-tencent/discussions
最后希望大家可以参与我们的有奖调查问卷:https://www.surveymonkey.com/r/blog-msntodo