使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

Cloud Functions 函数执行环境

Cloud Functions 函数在全代管式无服务器环境中运行。在此环境中,Google 会为您处理所有基础架构、操作系统和运行时环境问题。每个函数都在其专有的孤立安全执行环境中运行,可自动调节,并且其生命周期不受其他函数的干扰。

运行时

Cloud Functions 函数支持多语言运行时。如果您要通过命令行Terraform 部署函数,则需要运行时 ID 值。

运行时 基础映像 运行时 ID
Node.js 16(推荐) Ubuntu 18.04 nodejs16
Node.js 14 Ubuntu 18.04 nodejs14
Node.js 12 Ubuntu 18.04 nodejs12
Node.js 10 Ubuntu 18.04 nodejs10
Node.js 8(已弃用) Ubuntu 18.04 nodejs8
Node.js 6(已停用) Debian 8 nodejs6
Python 3.10(推荐) Ubuntu 22.04 python310
Python 3.9 Ubuntu 18.04 python39
Python 3.8 Ubuntu 18.04 python38
Python 3.7 Ubuntu 18.04 python37
Go 1.16(推荐) Ubuntu 18.04 go116
Go 1.13 Ubuntu 18.04 go113
Go 1.11 Ubuntu 18.04 go111
Java 17(推荐) Ubuntu 22.04 java17
Java 11 Ubuntu 18.04 java11
.NET Core 6.0(预览版) Ubuntu 18.04 dotnet6
.NET Core 3.1(推荐) Ubuntu 18.04 dotnet3
Ruby 3.0(推荐) Ubuntu 18.04 ruby30
Ruby 2.7 Ubuntu 18.04 ruby27
Ruby 2.6 Ubuntu 18.04 ruby26
PHP 8.1(推荐) Ubuntu 18.04 php81
PHP 7.4 Ubuntu 18.04 php74

除非另有说明,否则在部署函数时,更新和安全补丁会应用于运行时及其依赖项。这包括由语言社区提供的更新和补丁,这些更新和补丁在经过一段时间的稳定性测试后可以使用。同样,Cloud Functions 可能会将更新应用于执行环境的其他方面,例如操作系统或包含的软件包。这些更新有助于确保您的函数的安全。

自动扩缩行为

Cloud Functions 实现了无服务器范式,让您只需运行代码,而不必担心底层基础架构(如服务器或虚拟机)。部署后,您的函数会自动进行管理和扩缩。

Cloud Functions 通过将传入请求分配给函数实例来处理这些请求。Cloud Functions 可以将请求分配给现有实例,也可以创建新的实例,具体取决于请求和现有函数实例的数量。

如果入站请求数量超过现有实例的数量,Cloud Functions 可以启动多个新实例来处理请求。 通过这种自动扩容行为,Cloud Functions 可以分别使用不同的函数实例并行处理多个请求。

在某些情况下,无限地扩容可能不可取。为了解决此问题,Cloud Functions 允许您配置特定函数在任何给定时间可以共存的实例数上限

无状态

如需启用函数的自动管理和扩缩,函数必须是无状态的 - 一次函数调用不得依赖于先前调用设置的内存中状态。调用可能由不共享全局变量、内存、文件系统或其他状态的不同函数实例处理。

如果您需要在多次函数调用之间共享状态,则您的函数应使用 MemorystoreDatastoreFirestoreCloud Storage 等服务来保留数据。如需详细了解 Google Cloud 提供的数据库和存储选项,请参阅 Google Cloud 数据库Google Cloud 存储产品

并发

每个函数实例一次只处理一个并发请求。 这意味着,当您的代码处理一个请求时,不可能有第二个请求被路由到同一个实例。因此,原始请求可充分使用您分配的资源(内存和 CPU)。

由于并发请求由不同的函数实例处理,因此这些请求不会共享变量或本地内存。如需了解详情,请参阅无状态函数实例有效期

冷启动

在以下两种情况下,系统会启动新的函数实例:

  • 当您部署函数时。

  • 当系统自动创建新函数实例来扩展函数计算能力以处理相应负载时,或偶尔替换现有实例时。

启动新的函数实例涉及加载运行时和您的代码。如果请求会导致启动新的函数实例(称为冷启动),则其处理速度可能会慢于那些发往现有函数实例的请求。但是,如果您的函数收到的负载量较为稳定,那么冷启动的次数通常可以忽略不计,除非您的函数因崩溃而需要频繁重启函数环境。

如果您的函数代码抛出未捕获的异常或使当前进程崩溃,该函数实例可能会重启。这可能会导致多次冷启动,从而出现较长的延时,因此我们建议捕获异常并避免终止当前进程。请参阅报告错误,了解如何处理和报告 Cloud Functions 函数中的错误。

如果您的函数对延迟时间较为敏感,请考虑设置实例数下限,以避免冷启动。

函数实例有效期

函数实例通常具有弹性,会在随后的函数调用中重复使用,除非实例数量因缺少流量或函数崩溃而缩减。也就是说,当一个函数执行结束时,另一次函数调用可由同一个函数实例处理。

函数范围与全局范围

单次函数调用会导致系统只执行被声明为入口点的函数代码。函数源代码的全局范围仅在冷启动时执行,不在已初始化的实例上执行。

Node.js

// Global (instance-wide) scope
// This computation runs at instance cold-start
const instanceVar = heavyComputation();

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
exports.scopeDemo = (req, res) => {
  // Per-function scope
  // This computation runs every time this function is called
  const functionVar = lightComputation();

  res.send(`Per instance: ${instanceVar}, per function: ${functionVar}`);
};

Python

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()

def scope_demo(request):
    """
    HTTP Cloud Function that declares a variable.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Per-function scope
    # This computation runs every time this function is called
    function_var = light_computation()
    return 'Instance: {}; function: {}'.format(instance_var, function_var)

Go


// h is in the global (instance-wide) scope.
var h string

// init runs during package initialization. So, this will only run during an
// an instance's cold start.
func init() {
	h = heavyComputation()
}

// ScopeDemo is an example of using globally and locally
// scoped variables in a function.
func ScopeDemo(w http.ResponseWriter, r *http.Request) {
	l := lightComputation()
	fmt.Fprintf(w, "Global: %q, Local: %q", h, l)
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;

public class Scopes implements HttpFunction {
  // Global (instance-wide) scope
  // This computation runs at instance cold-start.
  // Warning: Class variables used in functions code must be thread-safe.
  private static final int INSTANCE_VAR = heavyComputation();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Per-function scope
    // This computation runs every time this function is called
    int functionVar = lightComputation();

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Instance: %s; function: %s", INSTANCE_VAR, functionVar);
  }

  private static int lightComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).sum();
  }

  private static int heavyComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).reduce((t, x) -> t * x).getAsInt();
  }
}

Ruby

# Global (instance-wide) scope.
# This block runs on cold start, before any function is invoked.
#
# Note: It is usually best to run global initialization in an on_startup block
# instead at the top level of the Ruby file. This is because top-level code
# could be executed to verify the function during deployment, whereas an
# on_startup block is run only when an actual function instance is starting up.
FunctionsFramework.on_startup do
  instance_data = perform_heavy_computation

  # To pass data into function invocations, the best practice is to set a
  # key-value pair using the Ruby Function Framework's built-in "set_global"
  # method. Functions can call the "global" method to retrieve the data by key.
  # (You can also use Ruby global variables or "toplevel" local variables, but
  # they can make it difficult to isolate global data for testing.)
  set_global :my_instance_data, instance_data
end

FunctionsFramework.http "tips_scopes" do |_request|
  # Per-function scope.
  # This method is called every time this function is called.
  invocation_data = perform_light_computation

  # Retrieve the data computed by the on_startup block.
  instance_data = global :my_instance_data

  "instance: #{instance_data}; function: #{invocation_data}"
end

您可以使用全局变量作为性能优化方法,但不得依赖于先前函数调用在全局范围内设置的状态 - 如需了解详情,请参阅无状态

您可以假设在调用每个函数代码之前,每个函数实例都正好执行了一次全局范围。但是,您不能依赖于全局范围执行的总次数或时间,因为这些因素可能会因自动扩缩活动而异。

函数执行时间轴

函数只能在其执行期间使用所分配的资源(内存和 CPU)。如果在函数执行期之外运行代码,这些代码不一定会执行,并且可能随时会停止。因此,您应始终正确地发出信号来表明函数执行已结束,并避免在函数执行期之外运行任何代码。如需获取指导,请参阅 HTTP 函数后台函数CloudEvent 函数

函数执行还取决于函数的超时时长。如需了解详情,请参阅函数超时

在初始化应用时,请考虑执行时间轴。在初始化期间,不应在全局范围内创建后台任务,因为这些任务会在请求的持续时间以外执行。

执行保证

针对每个传入事件,您的函数通常会调用一次。但是,由于各种错误场景的差异,Cloud Functions 函数并不保证在所有情况下都调用一次且只调用一次。

您的函数在单个事件中可被调用的次数上限或下限取决于函数的类型:

  • HTTP 函数最多会被调用一次。这是由于 HTTP 调用的同步性质所致,这意味着在函数调用期间发生的任何错误都将返回,而不会重试。HTTP 函数的调用者应负责处理错误,并在必要时重试。

  • 事件驱动函数至少被调用一次。这是由于事件的异步性质所致,在此情况下没有调用者等待响应。在极少数情况下,系统可能会多次调用事件驱动函数,以确保事件的传递。如果事件驱动函数调用因错误而失败,那么除非为该函数启用了失败时重试,否则该函数不会被再次调用。

为了确保函数在重新尝试执行时可正常运行,您应该使其具有幂等性,方法是恰当实现函数,让事件即使被传递多次,产生的结果(和副作用)也符合预期。对于 HTTP 函数,这还意味着即使调用者尝试重新调用 HTTP 函数端点,HTTP 函数也应该能返回所期望的值。如需详细了解如何使函数具有幂等性,请参阅重试事件驱动的函数

内存和文件系统

每个函数分配有一定的内存量供其使用。您可以在部署时配置内存量 - 如需了解详情,请参阅内存限制

函数执行环境包括内存文件系统,其中包含与您的函数一起部署的源代码文件和目录(请参阅构建源代码)。包含源文件的目录是只读的,但文件系统的其余部分是可写的(操作系统使用的文件除外)。使用文件系统会计入函数的内存用量。

您的函数可以使用每种编程语言的标准方法与文件系统进行交互。

网络

您的函数可以使用每种编程语言的标准方法访问公共互联网,无论是通过运行时提供的内置库,还是您作为依赖项添加的第三方库。

请尝试在多次函数调用中重复使用网络连接,如优化网络中所述。但是,请注意,如果某个连接未被使用的时间达到 10 分钟,则可能会被系统关闭。若继续尝试使用已关闭的连接,将导致“连接重置”错误。您的代码应该使用可以很好地处理已关闭连接的库,或者明确处理已关闭的连接(如果使用的是低层级网络结构)。

函数隔离

每个部署的函数都与其他所有函数(甚至是从同一个源文件部署的函数)隔离。具体来说,这些函数不会共享内存、全局变量、文件系统或其他状态。

如需在部署的函数之间共享数据,您可以使用诸如以下服务:MemorystoreDatastoreFirestoreCloud Storage。或者,您也可以从一个函数调用另一个函数(通过使用它们的相应触发器并传递必要的数据)。例如,向 HTTP 函数的端点发出 HTTP 请求,或者向 Pub/Sub 主题发布消息以触发 Pub/Sub 函数。