函数计算,是最近流行的一种全托管的计算服务。
作为编程语言的基本单元,函数对大多数开发人员来说,本身是非常简单的。例如,我们编写一个计算绝对值的JavaScript函数:
function abs(x) {
if (typeof (x) !== 'number') {
throw 'not a number';
}
return x < 0 ? -x : x;
}
这个函数编写非常容易,但是,如果作为服务,要把它部署到网络上,则需要大量的工作。
第一个问题是选择什么协议调用函数。可以选择HTTP协议或者gRPC协议,考虑到通用型,选择HTTP协议,并采用REST方式调用相对更加容易。
紧接着,就需要支持REST的服务器与对应的框架。例如,以JavaScript函数为例,就需要安装Node,以及选择一个Web框架,例如Express。
最后,还需要自己编写路由、解析输入、调用函数并返回输出,只有完成了这一系列工作后,用户才能通过一个URL调用这个函数并获得返回值:
curl http://example.com/functions/abs?input=12345
无论函数多么简单,一旦涉及到上述开发部署流程,就非常让人头大。因此,越来越多的云服务商开始提供FaaS(Function as a Service)服务,即用户只定义函数,剩下的网络请求、负载均衡、虚拟机环境等全部由FaaS提供,能大大简化函数计算的开发。
本文的目标是从零开始部署一个头像服务,输入一个名字,例如bob
,返回一个动态生成的SVG图像。以下是一个示例函数调用:
https://avatar.liaoxuefeng.com/.netlify/functions/avatar?name=bob
它返回的图像如下:
这个服务用JavaScript编写,并部署在Netlify上。后面我们会讨论为什么选择Netlify。
首先,我们在GitHub创建一个新的Git库netlify-avatar,它具有如下目录结构:
<repo>
├── netlify
│ └── functions
│ └── avatar.js
├── netlify.toml
├── package.json
└── public
└── index.html
Netlify默认在netlify/functions
目录下查找所有函数。我们放置了一个avatar.js
,对应的函数名就是avatar
,调用该函数的URL路径则是/.netlify/functions/avatar
。接下来,我们在根目录编写一个netlify.toml
描述文件:
[build]
publish = "public/"
command = "npm install"
[functions]
directory = "netlify/functions/"
node_bundler = "esbuild"
这个文件的详细说明可以参考Netlify文档,其中:
publish = "public/"
指示静态文件存储的目录,这并不是必须的,但我们放一个index.html
可以作为函数的描述页面;
command = "npm install"
指示部署时运行的命令,此处是安装相关依赖包;
node_bundler = "esbuild"
指示使用ES的import
语法,因为Netlify不再支持require()
语句。
接下来,我们把用到的依赖包用npm
安装并保存到package.json
中:
$ npm install --save minidenticons
最后,编写核心函数avatar.js
:
// 导入模块:
import { identicon } from 'minidenticons';
exports.handler = async function (event, context) {
// 获取name输入:
let name = event.queryStringParameters.name || 'unnamed';
// 计算:
let svg = identicon(name);
// 返回结果:
return {
// HTTP 200:
statusCode: 200,
// 响应类型:
headers: {
'content-type': 'image/svg+xml'
},
// 响应文本:
body: svg
};
};
可见,核心函数是一个async function (event, context)
,event
封装了所有输入,context
则是函数的运行时环境,要求返回一个JavaScript Object,就可以完成函数的功能。
部署
要把上述函数部署到Netlify,首先使用GitHub账号登录到Netlify,创建一个Site,选择“Import an existing project from a Git repository”而不是“Start from a template”,因为我们并不需要部署复杂的Next.js
。从GitHub选择对应的repo后,Netlify自动读取netlify.toml
,点击部署,稍等一分钟左右,就可以通过Netlify提供的URL访问函数。最后,再绑定一个域名,勾选HTTPS支持,就完成了整个部署流程,非常简单。
后续如果修改了代码,则推送到GitHub后,会触发自动部署。
一般来说,无状态的服务非常适合作为函数服务运行,但很多时候,仍需要一些运行时配置,例如,防盗链设置、缓存时长等,这些可以通过环境变量实现,即在Netlify后台设置好环境变量,在函数中读取即可。
如果对比AWS的Lambda服务或者阿里云的FC服务,Netlify的部署无疑是最简单且最直接的,并且默认实现了代码推送即部署,无额外手动步骤。
本文源码可从GitHub下载。