提示与技巧
本文档介绍了设计、实现、测试和部署 Cloud Run functions 函数的最佳做法。
正确做法
本部分介绍 Cloud Run functions 函数设计和实现方面的常规最佳做法。
编写幂等函数
即使您的函数被多次调用,也应产生相同的结果。这样,如果前面的代码调用中途失败,您可以重新调用。如需了解详情,请参阅重试事件驱动型函数。
确保 HTTP 函数发送 HTTP 响应
如果您的函数是由 HTTP 触发,请记得发送 HTTP 响应,如下所示。否则,函数的执行过程可能会发生超时。如果发生这种情况,您将需要为整段超时时间付费。超时还可能使后续调用出现不可预测的行为或冷启动,从而导致意外行为或延时增加。
Node.js
Python
Go
Java
C#
Ruby
PHP
请勿启动后台活动
后台活动是指在函数终止后发生的任何活动。一旦函数返回或以其他方式发出完成信号(例如通过调用 Node.js 事件驱动型函数中的 callback
参数),函数调用就会完成。在正常终止后运行的任何代码都无法访问 CPU,因而无法继续进行。
另外,当在同一环境中执行后续调用时,您的后台活动将继续进行,从而干扰新的调用。这可能会导致难以诊断的意外行为和错误。在函数终止后访问网络通常会导致连接重置(错误代码为 ECONNRESET
)。
通常可以在各调用产生的日志中检测到后台活动,相关信息记录在指示调用已完成的行的后面。后台活动有时可能会深藏在代码中,尤其是在存在回调函数或定时器等异步操作的情况下。 请检查您的代码,以确保所有异步操作都会在函数终止之前完成。
务必删除临时文件
临时目录中的本地磁盘存储是内存中的文件系统。您写入的文件会占用函数可以使用的内存,并且有时会在多次调用过程中持续存在。如果不明确删除这些文件,最终可能会导致内存不足错误,并且随后需要进行冷启动。
如需查看单个函数所使用的内存,您可以访问 Google Cloud 控制台,在函数列表中选择相应的函数,然后选择“内存用量”图。
请勿试图在临时目录之外执行写入操作,并务必使用独立于平台/操作系统的方法构建文件路径。
您可以在使用流水线处理大型文件时减少内存要求。例如,要在 Cloud Storage 上处理文件,您可以创建读取流,通过基于流的进程传递读取流,然后将输出流直接写入 Cloud Storage。
Cloud Functions 框架
部署函数时,Functions 框架会使用其当前版本自动添加为依赖项。为了确保在不同的环境中以一致的方式安装相同的依赖项,我们建议您将函数固定到特定版本的 Functions 框架。
为此,请在相关锁定文件中添加您的首选版本(例如,Node.js 版为 package-lock.json
,Python 版为 requirements.txt
)。
工具
本部分指导您如何使用工具来实现和测试 Cloud Run functions 函数并与之互动。
本地开发
函数部署需要一些时间,因此在本地测试函数的代码通常会更快。
错误报告
在使用异常处理的语言中,不要抛出未捕获的异常,因为这些异常会导致在未来调用函数时强制执行冷启动。如需了解如何适当地报告错误,请参阅报告错误指南。
请勿手动退出
手动退出可能会导致出现意外行为。请改用以下特定语言的惯用语:
Node.js
请勿使用 process.exit()
。HTTP 函数应使用 res.status(200).send(message)
发送响应,事件驱动函数在返回(隐式或显式)后即退出。
Python
请勿使用 sys.exit()
。HTTP 函数应明确返回字符串形式的响应,事件驱动函数返回值(隐式或显式)后即退出。
Go
请勿使用 os.Exit()
。HTTP 函数应明确返回字符串形式的响应,事件驱动函数返回值(隐式或显式)后即退出。
Java
请勿使用 System.exit()
。HTTP 函数应使用 response.getWriter().write(message)
发送响应,事件驱动函数在返回(隐式或显式)后即退出。
C#
请勿使用 System.Environment.Exit()
。HTTP 函数应使用 context.Response.WriteAsync(message)
发送响应,事件驱动函数在返回(隐式或显式)后即退出。
Ruby
请勿使用 exit()
或 abort()
。HTTP 函数应明确返回字符串形式的响应,事件驱动函数返回值(隐式或显式)后即退出。
PHP
请勿使用 exit()
或 die()
。HTTP 函数应明确返回字符串形式的响应,事件驱动函数返回值(隐式或显式)后即退出。
使用 Sendgrid 发送电子邮件
Cloud Run functions 函数不允许在端口 25 上建立出站连接,因此您无法建立到 SMTP 服务器的非安全连接。推荐使用 SendGrid 发送电子邮件。如需了解发送电子邮件的其他方式,请参阅适用于 Google Compute Engine 的从实例发送电子邮件教程。
性能
本部分介绍性能优化方面的最佳实践。
谨慎使用依赖项
由于函数是无状态的,执行环境通常是从头开始初始化(称为“冷启动”)。当发生冷启动时,系统会对函数的全局环境进行评估。
如果您的函数导入了模块,那么在冷启动期间,这些模块的加载时间会造成调用延迟加重。正确加载依赖项而不加载函数不使用的依赖项,即可缩短此延迟时间以及函数部署时间。
使用全局变量,以便在日后的调用中重复使用对象
系统无法保证能保留 Cloud Run 函数的状态,以用于将来的调用。不过,Cloud Run 函数经常会回收利用先前调用的执行环境。如果您在全局范围内声明一个变量,就可以在后续的调用中再次使用该变量的值,而不必重新计算。
通过这种方式,您可以缓存在每次调用函数时重建的成本较高的对象。将此类对象从函数体移到全局范围可能会显著提升性能。以下示例会为每个函数实例创建一个重量级对象(每个实例仅限一次),并提供给连接指定实例的所有函数调用共用:
Node.js
Python
Go
Java
C#
Ruby
PHP
尤为重要的是,您应在全局范围内缓存网络连接、库引用和 API 客户端对象。如需查看相关示例,请参阅优化网络连接方式。
对全局变量进行延迟初始化
如果您在全局范围内初始化变量,系统始终会通过冷启动调用执行初始化代码,而这会增加函数的延迟时间。在某些情况下,如果被调用的服务在 try
/catch
块中未得到正确处理,则会导致这些服务间歇性超时。如果某些对象并非在所有代码路径中都会用到,可以考虑按需对它们进行延迟初始化:
如果您在单个文件中定义多个函数,并且不同的函数使用不同的变量,这种做法尤其有用。如果不使用延迟初始化,您可能会因为初始化后永远不会再用到的变量而浪费资源。
通过设置实例数下限减少冷启动次数
默认情况下,Cloud Run functions 函数会根据传入请求的数量扩缩实例数量。您可以更改这种默认行为,只需设置 Cloud Run functions 函数必须保持就绪状态以处理请求的实例数下限即可。设置实例数下限可以减少应用的冷启动次数。如果您的应用对延迟时间较为敏感,我们建议您设置实例数下限。
如需了解如何设置实例数下限,请参阅使用最小实例数。
其他资源
如需详细了解如何优化性能,请观看“Google Cloud 性能指南”视频 Cloud Run 函数冷启动时间。