全部产品
云市场

Node.js 运行环境

更新时间:2020-03-26 10:09:27

函数计算目前支持 Node.js 6.10(runtime = nodejs6)、Node.js 8.9.0(runtime = nodejs8)和 Node.js 10.15.3(runtime = nodejs10)运行环境。本文介绍了 Node.js 运行环境的以下内容:

打印日志

您的函数通过 console.log 打印的内容会被收集到创建 service 时指定的 Logstore 中。函数日志的更多信息,请参见函数日志

  1. exports.handler = function (event, context, callback) {
  2. console.info(null, 'hello world');
  3. callback(null, 'hello world');
  4. };

运行上面的代码输出的日志内容如下所示。

  1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world

使用 console.warnconsole.error 可以分别打印 WARN、ERROR 级别的日志。

注意:您可以通过接口 setLogLevel 来改变日志级别,其中日志级别从高到低如下所示。

日志对应接口
errorconsole.error
warnconsole.warn
infoconsole.info
verboseconsole.log
debugconsole.debug
  1. 'use strict';
  2. exports.handler = function(evt, ctx, cb) {
  3. console.setLogLevel("error");
  4. console.error("console error 1");
  5. console.info("console info 1");
  6. console.warn("console warn 1");
  7. console.log("console log 1");
  8. console.setLogLevel("warn");
  9. console.error("console error 2");
  10. console.info("console info 2");
  11. console.warn("console warn 2");
  12. console.log("console log 2");
  13. console.setLogLevel("info");
  14. cb(null, evt);
  15. };

运行上面的代码输出的日志内容如下所示。

  1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [ERROR] console error 1
  2. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [ERROR] console error 2
  3. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [WARN] console warn 2

函数返回

Node.js 采用异步编程的模型,您的函数需要使用 callback 入参返回信息。callback 的语法如下所示。

  1. callback(Error error, Object result);

其中:

error:可选参数,在函数执行内部失败时使用此参数返回错误内容,成功情况下设置为 null。

result:可选参数,使用此参数返回函数成功的执行结果。result 可以是任意类型,函数计算会将其序列化成字节流,放到响应体中返回给调用方。

如果在代码中未调用 callback,函数计算无法确定函数是否执行完成,将会运行到超时时间并退出。提示函数运行超时的错误信息。

根据调用函数时的调用类型不同,返回值会有不同的处理方式。同步调用的返回值将会序列化字节流返回给调用方,异步调用的返回值将会被抛弃,需要您将重要信息记录到日志中。

callback 被调用后则函数结束

callback 被调用后函数就会停止运行,重复调用 callback 只接受第一次调用的结果。需要确保所有任务在 callback 调用之前完成,否则有些任务可能不会被运行。例如调用下面的函数,将返回 hello world ,并且 message 不会被打印。

  1. exports.handler = function(event, context, callback) {
  2. callback(null, 'hello world');
  3. callback(null, 'done');
  4. setTimeout(function() {
  5. console.log('message');
  6. }, 1000);
  7. };

callback 未被调用则函数超时

如果在函数中没有调用 callback,则系统将认为函数没有结束,会等待函数结果直到超时。例如调用下面的函数时将收到超时错误。

  1. exports.handler = function(event, context, callback) {
  2. console.log('hello world');
  3. };

调用结果如下所示。

  1. {"errorMessage":"Function timed out after 3 seconds"}

错误处理

  • 对于 Node.js 运行环境的函数,用户可能收到两种错误,错误类型记录在返回的 HTTP Header 字段中 X-Fc-Error-Type

    • HandledInvocationError:通过 callback 的第一个参数返回的错误。

    • UnhandledInvocationError:其他错误,包括未捕获异常、超时错误和 OOM(Out of memory)错误等。

  • 返回 HandledInvocationError 示例
  1. exports.handler = function(event, context, callback) {
  2. callback(new Error('oops'));
  3. };

调用时收到的响应如下所示。

  1. {
  2. "errorMessage": "oops",
  3. "errorType": "Error",
  4. "stackTrace": [
  5. "at exports.handler (/code/index.js:2:12)"
  6. ]
  7. }
  • 返回 UnhandledInvocationError 示例

当用户逻辑未捕获到错误时,函数计算系统会尽可能捕获错误,并返回具体的信息。当遇到系统无法捕获的错误时,例如用户函数在运行过程中突然崩溃退出,系统会返回一个通用的错误信息。

  1. exports.handler = function(event, context, callback) {
  2. throw new Error('oops');
  3. };

调用时收到的响应如下所示。

  1. {"errorMessage":"Process exited unexpectedly before completing request"}

错误类型的更多信息,请参见错误类型

使用内置模块

除了 Node.js 的标准模块,函数计算的 Node.js 运行环境中还包含了一些常用模块,您可以直接引用这些常用模块,目前函数计算包含的常见模块如下所示。

模块名称 版本 模块介绍 相关链接
co 4.6.0 控制流 https://github.com/tj/co
gm 1.23.0 图片处理库 https://github.com/aheckmann/gm
ali-oss 4.10.1 OSS SDK https://github.com/ali-sdk/ali-oss
ali-mns 2.6.5 MNS SDK https://github.com/InCar/ali-mns
tablestore 4.2.0 OTS SDK https://github.com/aliyun/aliyun-tablestore-nodejs-sdk
aliyun-sdk 1.10.12 Aliyun SDK https://github.com/aliyun-UED/aliyun-sdk-js
@alicloud/fc2 2.1.0 函数计算SDK https://github.com/aliyun/fc-nodejs-sdk
opencv 6.0.0 视觉算法库 https://github.com/peterbraden/node-opencv
body 5.1.0 http body 解析库 https://github.com/Raynos/body
raw-body 2.3.2 http body 解析库 https://github.com/stream-utils/raw-body

访问 OSS 的示例代码如下所示。

  1. var gm = require('gm').subClass({ imageMagick: true });
  2. exports.handler = function (event, context, callback) {
  3. gm(event)
  4. .flip()
  5. .toBuffer('PNG', function (err, buffer) {
  6. if (err) return callback(err);
  7. callback(null, buffer);
  8. });
  9. };

注意:上面的函数直接使用 event 作为图片的二进制数据,并且直接把生成的图片作为二进制数据返回。

使用自定义模块

如果您需要使用自定义模块,则需要将您的自定义模块与代码一起打包上传。您可以通过 npm 包管理器进行依赖管理,如果您使用 Fun 部署应用,可以使用 fun install 命令来安装依赖。

使用 npm 包管理器进行依赖管理

使用方法:

  • package.json 中配置依赖信息。
  • 通过 npm install 命令安装依赖库至 node_modules 文件夹。
  • 上传代码库时将依赖库一同打包上传。

下文以安装 Mysql 数据库为例进行详细介绍。

1.建立一个目录,用于存放代码和依赖模块。

  1. mkdir /tmp/code

2.在 /tmp/code 目录下安装依赖。

  1. cd /tmp/code
  2. npm install mysql

3.新建代码文件,例如 /tmp/code/index.js,在代码中使用 mysql

  1. var mysql = require('mysql');
  2. var connection;
  3. // exports.initializer: initializer function
  4. exports.initializer = function (context, callback) {
  5. connection = mysql.createConnection({
  6. host: 'localhost',
  7. user: 'me',
  8. password: 'secret',
  9. database: 'my_db'
  10. });
  11. connection.connect();
  12. }
  13. exports.handler = function (event, context, callback) {
  14. connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  15. if (error) return callback(error);
  16. console.log('The solution is: ', results[0].solution);
  17. callback(null, results[0].solution);
  18. });
  19. connection.end();
  20. };

4.打包上传

打包时,需要针对文件进行打包,而不是针对代码整体目录进行打包。打包完成后,入口函数文件需要位于包内的根目录。

  • 在 Windows 下打包时,可以进入函数代码目录,全选所有文件以后,单击鼠标右键,选择压缩为 zip 包,生成代码包。

  • 在 Linux 下打包时,通过调用 zip 命令时,将源文件指定为代码目录下的所有文件,实现生成部署代码包,例如 zip code.zip /home/code/*

打包后,您通过函数计算控制台代码执行页面,可以选择 OSS 上传或者代码包上传方式上传代码包。

使用 fun install 安装依赖

如果您使用 Fun 部署应用,可以使用 fun install 命令来安装依赖。关于 fun install 的详细信息请参考 fun install 介绍
下面以安装 mysql 库为例进行详细介绍。

  1. 在函数根目录下初始化 Funfile 文件。
  1. $ fun install init
  2. ? Select a runtime
  3. nodejs6
  4. nodejs8
  5. nodejs10
  6. python2.7
  7. python3
  8. java8
  9. php7.2
  10. (Move up and down to reveal more choices)

2.选择 nodejs8 后,会在当前目录生成一个名为 Funfile 的文件,编辑文件内容。

  1. RUNTIME nodejs8
  2. RUN npm install mysql

3.新建 template.yml 文件,例如 /tmp/code/template.yml,内容如下。

  1. ROSTemplateFormatVersion: '2015-09-01'
  2. Transform: 'Aliyun::Serverless-2018-04-03'
  3. Resources:
  4. FunDemo:
  5. Type: 'Aliyun::Serverless::Service'
  6. nodejsdemo:
  7. Type: 'Aliyun::Serverless::Function'
  8. Properties:
  9. Handler: index.handler
  10. Runtime: nodejs8
  11. Initializer: index.initializer
  12. CodeUri: './'

这个 template.yml 的含义如下:声明名为 FunDemo 的服务,在这个服务下,声明一个名为 nodejsdemo 的函数,配置函数入口为 index.handler,Initializer 为 index.initializer 函数的 runtime 为 nodejs8。并指定 CodeUri 为当前目录。在部署时,Fun 会将 CodeUri 指定的目录打包上传。更多的配置规则,请参见 Serverless Application Model

4.在函数根目录下执行 fun install 命令安装依赖。

  1. $ fun install
  2. using template: template.yml
  3. start installing function dependencies without docker
  4. building FunDemo/nodejsdemo
  5. Funfile exist, Fun will use container to build forcely
  6. Step 1/2 : FROM registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-nodejs8:build-1.9.4
  7. ---> 8c6c75614064
  8. Step 2/2 : RUN npm install mysql
  9. ---> Using cache
  10. ---> 715f4427faf9
  11. sha256:715f4427faf9b66238328e44736078f2ac4bbc0d403e441a6c460b6cc2f405ae
  12. Successfully built 715f4427faf9
  13. Successfully tagged fun-cache-58fa61db-dd07-416b-aa47-a6ee752ec6d7:latest
  14. copying function artifact to /Users/txd123/Desktop/express
  15. Install Success
  16. Tips for next step
  17. ======================
  18. * Invoke Event Function: fun local invoke
  19. * Invoke Http Function: fun local start
  20. * Build Http Function: fun build
  21. * Deploy Resources: fun deploy

5.使用 Fun 部署。

  1. fun deploy

执行成功时,会看到相关日志。

  1. using region: cn-hangzhou
  2. using accountId: ***********3557
  3. using accessKeyId: ***********r3Ra
  4. using timeout: 300
  5. Waiting for service FunDemo to be deployed...
  6. Waiting for function nodejsdemo to be deployed...
  7. Waiting for packaging function nodejs code...
  8. package function nodejs code done
  9. function nodejsdemo deploy success
  10. service FunDemo deploy success

登录函数计算控制台,即可看到相关的服务、函数被创建成功,且触发执行可以返回正确的结果。

调用外部命令

您的函数可能会用到一些工具,而这些工具并不是用 Node.js 写的(例如 Shell 、C++ 或者 Go 编译出来的可执行文件)。您仍然可以将这些工具与代码一起打包,然后在函数中通过运行外部命令的方法来使用它们。下面的代码演示了如何运行一个 Shell 脚本。

注意:使用 C 、 C++ 、Go 编译出来的可执行文件,需要与函数计算的运行环境兼容。函数计算的 Node.js 运行环境如下所示。

  • Linux 内核版本:Linux 4.4.24-2.al7.x86_64。
  • Docker 基础镜像:docker pull node:6.10。
  1. var exec = require('child_process');
  2. exports.handler = function(event, context, callback) {
  3. var scriptPath = process.env['FC_FUNC_CODE_PATH'] + '/script.sh';
  4. exec.exec('bash '+scriptPath, {}, function(err, stdout, stderr) {
  5. if (err) return callback(err);
  6. console.log(stdout, stderr);
  7. callback(null, stdout);
  8. });
  9. };