Tips & Trik

Dokumen ini berisi penjelasan mengenai praktik terbaik untuk merancang, mengimplementasikan, menguji, dan men-deploy Cloud Functions.

Ketepatan

Bagian ini menjelaskan praktik terbaik yang umum untuk merancang dan mengimplementasikan Cloud Functions.

Menulis fungsi idempoten

Fungsi Anda harus memberikan hasil yang sama sekalipun dipanggil berkali-kali. Dengan demikian, Anda dapat mencoba ulang pemanggilan jika pemanggilan sebelumnya gagal di tengah jalan akibat masalah pada kode Anda. Untuk mengetahui informasi selengkapnya, lihat mencoba ulang fungsi berdasarkan peristiwa.

Memastikan fungsi HTTP mengirimkan respons HTTP

Jika fungsi Anda dipicu HTTP, jangan lupa untuk mengirim respons HTTP, seperti yang ditunjukkan di bawah ini. Jika tidak dilakukan, fungsi dapat dieksekusi hingga waktu tunggu habis. Jika hal ini terjadi, Anda akan dikenakan biaya untuk seluruh waktu tunggu. Waktu tunggu juga dapat menyebabkan perilaku yang tidak dapat diprediksi atau cold start pada pemanggilan berikutnya, yang mengakibatkan perilaku yang tidak dapat diprediksi atau latensi tambahan.

Node.js

const functions = require('@google-cloud/functions-framework');
const escapeHtml = require('escape-html');

/**
 * Responds to an HTTP request using data from the request body parsed according
 * to the "content-type" header.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
functions.http('helloHttp', (req, res) => {
  res.send(`Hello ${escapeHtml(req.query.name || req.body.name || 'World')}!`);
});

Python


import functions_framework


from markupsafe import escape

@functions_framework.http
def hello_http(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <https://flask.palletsprojects.com/en/1.1.x/api/#incoming-request-data>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response>.
    """
    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and "name" in request_json:
        name = request_json["name"]
    elif request_args and "name" in request_args:
        name = request_args["name"]
    else:
        name = "World"
    return f"Hello {escape(name)}!"

Go


// Package helloworld provides a set of Cloud Functions samples.
package helloworld

import (
	"encoding/json"
	"fmt"
	"html"
	"net/http"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
)

func init() {
	functions.HTTP("HelloHTTP", HelloHTTP)
}

// HelloHTTP is an HTTP Cloud Function with a request parameter.
func HelloHTTP(w http.ResponseWriter, r *http.Request) {
	var d struct {
		Name string `json:"name"`
	}
	if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	if d.Name == "" {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	fmt.Fprintf(w, "Hello, %s!", html.EscapeString(d.Name))
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Logger;

public class HelloHttp implements HttpFunction {
  private static final Logger logger = Logger.getLogger(HelloHttp.class.getName());

  private static final Gson gson = new Gson();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Check URL parameters for "name" field
    // "world" is the default value
    String name = request.getFirstQueryParameter("name").orElse("world");

    // Parse JSON request and check for "name" field
    try {
      JsonElement requestParsed = gson.fromJson(request.getReader(), JsonElement.class);
      JsonObject requestJson = null;

      if (requestParsed != null && requestParsed.isJsonObject()) {
        requestJson = requestParsed.getAsJsonObject();
      }

      if (requestJson != null && requestJson.has("name")) {
        name = requestJson.get("name").getAsString();
      }
    } catch (JsonParseException e) {
      logger.severe("Error parsing JSON: " + e.getMessage());
    }

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Hello %s!", name);
  }
}

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;

namespace HelloHttp;

public class Function : IHttpFunction
{
    private readonly ILogger _logger;

    public Function(ILogger<Function> logger) =>
        _logger = logger;

    public async Task HandleAsync(HttpContext context)
    {
        HttpRequest request = context.Request;
        // Check URL parameters for "name" field
        // "world" is the default value
        string name = ((string) request.Query["name"]) ?? "world";

        // If there's a body, parse it as JSON and check for "name" field.
        using TextReader reader = new StreamReader(request.Body);
        string text = await reader.ReadToEndAsync();
        if (text.Length > 0)
        {
            try
            {
                JsonElement json = JsonSerializer.Deserialize<JsonElement>(text);
                if (json.TryGetProperty("name", out JsonElement nameElement) &&
                    nameElement.ValueKind == JsonValueKind.String)
                {
                    name = nameElement.GetString();
                }
            }
            catch (JsonException parseException)
            {
                _logger.LogError(parseException, "Error parsing JSON request");
            }
        }

        await context.Response.WriteAsync($"Hello {name}!");
    }
}

Ruby

require "functions_framework"
require "cgi"
require "json"

FunctionsFramework.http "hello_http" do |request|
  # The request parameter is a Rack::Request object.
  # See https://www.rubydoc.info/gems/rack/Rack/Request
  name = request.params["name"] ||
         (request.body.rewind && JSON.parse(request.body.read)["name"] rescue nil) ||
         "World"
  # Return the response body as a string.
  # You can also return a Rack::Response object, a Rack response array, or
  # a hash which will be JSON-encoded into a response.
  "Hello #{CGI.escape_html name}!"
end

PHP

<?php

use Google\CloudFunctions\FunctionsFramework;
use Psr\Http\Message\ServerRequestInterface;

// Register the function with Functions Framework.
// This enables omitting the `FUNCTIONS_SIGNATURE_TYPE=http` environment
// variable when deploying. The `FUNCTION_TARGET` environment variable should
// match the first parameter.
FunctionsFramework::http('helloHttp', 'helloHttp');

function helloHttp(ServerRequestInterface $request): string
{
    $name = 'World';
    $body = $request->getBody()->getContents();
    if (!empty($body)) {
        $json = json_decode($body, true);
        if (json_last_error() != JSON_ERROR_NONE) {
            throw new RuntimeException(sprintf(
                'Could not parse body: %s',
                json_last_error_msg()
            ));
        }
        $name = $json['name'] ?? $name;
    }
    $queryString = $request->getQueryParams();
    $name = $queryString['name'] ?? $name;

    return sprintf('Hello, %s!', htmlspecialchars($name));
}

Jangan memulai aktivitas latar belakang

Aktivitas latar belakang meliputi segala sesuatu yang terjadi setelah fungsi Anda dihentikan. Pemanggilan fungsi dianggap selesai setelah fungsi tersebut menampilkan atau menandakan penyelesaian, misalnya dengan memanggil argumen callback di fungsi berdasarkan peristiwa Node.js. Setiap kode yang dijalankan setelah penghentian normal tidak dapat mengakses CPU dan tidak akan diproses.

Selain itu, ketika pemanggilan berikutnya dijalankan di lingkungan yang sama, aktivitas latar belakang Anda akan dilanjutkan, dan itu akan mengganggu pemanggilan baru. Hal ini dapat menyebabkan perilaku yang tidak terduga dan error yang sulit untuk didiagnosis. Mengakses jaringan setelah suatu fungsi berakhir biasanya menyebabkan koneksi disetel ulang (kode error ECONNRESET).

Aktivitas latar belakang sering kali dapat dideteksi dalam log dari pemanggilan individu, dengan menemukan hal apa pun yang dicatat dalam log setelah baris yang menyatakan bahwa pemanggilan selesai. Aktivitas latar belakang terkadang dapat diletakkan lebih dalam di kode, terutama ketika terdapat operasi asinkron seperti callback atau timer. Tinjau kode Anda untuk memastikan semua operasi asinkron selesai sebelum fungsi dihentikan.

Selalu hapus file sementara

Penyimpanan disk lokal di direktori sementara adalah sistem file dalam memori. File yang Anda tulis menggunakan memori yang disediakan untuk fungsi Anda, dan terkadang tetap dipertahankan di antara pemanggilan. Jika file ini tidak dihapus secara eksplisit, dapat terjadi error kehabisan memori yang disusul dengan cold start.

Anda dapat melihat memori yang digunakan oleh setiap fungsi dengan memilihnya pada daftar fungsi di Konsol Google Cloud dan memilih plot Memory usage.

Usahakan tidak menulis di luar direktori sementara, dan pastikan untuk menggunakan metode yang tidak bergantung pada platform/OS untuk membuat jalur file.

Anda dapat mengurangi persyaratan memori saat memproses file yang lebih besar menggunakan pipeline. Misalnya, Anda dapat memproses file di Cloud Storage dengan membuat aliran data baca, meneruskannya melalui proses berbasis aliran data, dan menuliskan aliran data output langsung ke Cloud Storage.

Functions Framework

Saat Anda men-deploy suatu fungsi, Functions Framework akan otomatis ditambahkan sebagai dependensi menggunakan versi saat ini. Untuk memastikan dependensi yang sama diinstal secara konsisten di berbagai lingkungan, sebaiknya sematkan fungsi Anda ke versi Functions Framework tertentu.

Untuk melakukannya, sertakan versi pilihan Anda dalam file kunci yang relevan (misalnya, package-lock.json untuk Node.js, atau requirements.txt untuk Python).

Alat

Bagian ini berisi panduan cara menggunakan fitur untuk mengimplementasikan, menguji, dan berinteraksi dengan Cloud Functions.

Pengembangan lokal

Deployment fungsi memerlukan waktu cukup lama. Jadi, sebaiknya lakukan pengujian kode fungsi secara lokal karena durasinya bisa lebih cepat.

Pelaporan error

Dalam bahasa yang menggunakan penanganan pengecualian, jangan menampilkan pengecualian yang tidak tertangkap, karena pengecualian tersebut memaksa cold start pada pemanggilan di berikutnya. Lihat panduan Pelaporan Error untuk mendapatkan informasi tentang cara melaporkan error dengan benar.

Jangan keluar secara manual

Keluar secara manual dapat menyebabkan perilaku yang tidak terduga. Sebagai gantinya, gunakan idiom spesifik bahasa berikut:

Node.js

Jangan gunakan process.exit(). Fungsi HTTP harus mengirimkan respons dengan res.status(200).send(message), dan fungsi berbasis peristiwa akan keluar setelah fungsi tersebut ditampilkan (baik secara implisit maupun eksplisit).

Python

Jangan gunakan sys.exit(). Fungsi HTTP harus secara eksplisit menampilkan respons sebagai string, dan fungsi yang dipicu peristiwa akan keluar setelah fungsi tersebut menampilkan nilai (baik secara implisit maupun eksplisit).

Go

Jangan gunakan os.Exit(). Fungsi HTTP harus secara eksplisit menampilkan respons sebagai string, dan fungsi yang dipicu peristiwa akan keluar setelah fungsi tersebut menampilkan nilai (baik secara implisit maupun eksplisit).

Java

Jangan gunakan System.exit(). Fungsi HTTP harus mengirimkan respons dengan response.getWriter().write(message), dan fungsi berbasis peristiwa akan keluar setelah fungsi tersebut ditampilkan (baik secara implisit maupun eksplisit).

C#

Jangan gunakan System.Environment.Exit(). Fungsi HTTP harus mengirimkan respons dengan context.Response.WriteAsync(message), dan fungsi berbasis peristiwa akan keluar setelah fungsi tersebut ditampilkan (baik secara implisit maupun eksplisit).

Ruby

Jangan gunakan exit() atau abort(). Fungsi HTTP harus secara eksplisit menampilkan respons sebagai string, dan fungsi yang dipicu peristiwa akan keluar setelah fungsi tersebut menampilkan nilai (baik secara implisit maupun eksplisit).

PHP

Jangan gunakan exit() atau die(). Fungsi HTTP harus secara eksplisit menampilkan respons sebagai string, dan fungsi yang dipicu peristiwa akan keluar setelah fungsi tersebut menampilkan nilai (baik secara implisit maupun eksplisit).

Menggunakan Sendgrid untuk mengirim email

Cloud Functions tidak mengizinkan koneksi keluar pada port 25, sehingga Anda tidak dapat membuat koneksi tidak aman ke server SMTP. Cara yang direkomendasikan untuk mengirim email adalah dengan menggunakan SendGrid. Anda dapat menemukan opsi lain untuk mengirim email dalam tutorial Mengirim Email dari Instance untuk Google Compute Engine.

Performa

Bagian ini menjelaskan praktik terbaik untuk mengoptimalkan performa.

Menggunakan dependensi dengan bijak

Karena fungsi bersifat stateless, lingkungan eksekusi biasanya diinisialisasi sejak awal (selama periode yang disebut cold start). Saat terjadi cold start, konteks global fungsi akan dievaluasi.

Jika fungsi Anda mengimpor modul, waktu pemuatan untuk modul tersebut dapat meningkatkan latensi pemanggilan selama cold start. Anda dapat mengurangi latensi ini dan waktu yang diperlukan untuk men-deploy fungsi. Caranya, dengan memuat dependensi secara benar dan tidak memuat dependensi yang tidak digunakan fungsi Anda.

Menggunakan variabel global untuk menggunakan kembali objek dalam pemanggilan selanjutnya

Tidak ada jaminan bahwa status Cloud Functions akan dipertahankan untuk pemanggilan selanjutnya. Namun, Cloud Functions sering mendaur ulang lingkungan eksekusi dari pemanggilan sebelumnya. Jika Anda mendeklarasikan sebuah variabel dalam cakupan global, nilainya dapat digunakan kembali dalam pemanggilan selanjutnya tanpa harus dikomputasi ulang.

Dengan begitu, Anda dapat melakukan caching untuk objek yang mungkin memakan biaya tinggi jika harus dibuat ulang di setiap pemanggilan fungsi. Pemindahan objek semacam ini dari isi fungsi ke cakupan global dapat menghasilkan peningkatan performa secara signifikan. Contoh berikut membuat objek berat hanya satu kali per instance fungsi, dan membagikannya ke semua pemanggilan fungsi yang mencapai instance tersebut:

Node.js

const functions = require('@google-cloud/functions-framework');

// TODO(developer): Define your own computations
const {lightComputation, heavyComputation} = require('./computations');

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

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
functions.http('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

import time

import functions_framework


# Placeholder
def heavy_computation():
    return time.time()


# Placeholder
def light_computation():
    return time.time()


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


@functions_framework.http
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 f"Instance: {instance_var}; function: {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()
	functions.HTTP("ScopeDemo", ScopeDemo)
}

// 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();
  }
}

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Linq;
using System.Threading.Tasks;

namespace Scopes;

public class Function : IHttpFunction
{
    // Global (server-wide) scope.
    // This computation runs at server cold-start.
    // Warning: Class variables used in functions code must be thread-safe.
    private static readonly int GlobalVariable = HeavyComputation();

    // Note that one instance of this class (Function) is created per invocation,
    // so calling HeavyComputation in the constructor would not have the same
    // benefit.

    public async Task HandleAsync(HttpContext context)
    {
        // Per-function-invocation scope.
        // This computation runs every time this function is called.
        int functionVariable = LightComputation();

        await context.Response.WriteAsync($"Global: {GlobalVariable}; function: {functionVariable}");
    }

    private static int LightComputation()
    {
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        return numbers.Sum();
    }

    private static int HeavyComputation()
    {
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        return numbers.Aggregate((current, next) => current * next);
    }
}

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

PHP


use Psr\Http\Message\ServerRequestInterface;

function scopeDemo(ServerRequestInterface $request): string
{
    // Heavy computations should be cached between invocations.
    // The PHP runtime does NOT preserve variables between invocations, so we
    // must write their values to a file or otherwise cache them.
    // (All writable directories in Cloud Functions are in-memory, so
    // file-based caching operations are typically fast.)
    // You can also use PSR-6 caching libraries for this task:
    // https://packagist.org/providers/psr/cache-implementation
    $cachePath = sys_get_temp_dir() . '/cached_value.txt';

    $response = '';
    if (file_exists($cachePath)) {
        // Read cached value from file, using file locking to prevent race
        // conditions between function executions.
        $response .= 'Reading cached value.' . PHP_EOL;
        $fh = fopen($cachePath, 'r');
        flock($fh, LOCK_EX);
        $instanceVar = stream_get_contents($fh);
        flock($fh, LOCK_UN);
    } else {
        // Compute cached value + write to file, using file locking to prevent
        // race conditions between function executions.
        $response .= 'Cache empty, computing value.' . PHP_EOL;
        $instanceVar = _heavyComputation();
        file_put_contents($cachePath, $instanceVar, LOCK_EX);
    }

    // Lighter computations can re-run on each function invocation.
    $functionVar = _lightComputation();

    $response .= 'Per instance: ' . $instanceVar . PHP_EOL;
    $response .= 'Per function: ' . $functionVar . PHP_EOL;

    return $response;
}

Sangatlah penting untuk menyimpan koneksi jaringan, referensi library, dan objek klien API ke dalam cache pada cakupan global. Untuk mendapatkan contoh, baca bagian Mengoptimalkan Jaringan.

Menginisialisasi variabel global berdasarkan permintaan

Jika Anda menginisialisasi variabel dalam cakupan global, kode inisialisasi akan selalu dijalankan melalui pemanggilan cold start, sehingga latensi fungsi Anda meningkat. Dalam kasus tertentu, hal ini menyebabkan waktu tunggu yang terputus-putus pada layanan yang sedang dipanggil jika variabel tersebut tidak ditangani dengan benar dalam blok try/catch. Jika beberapa objek tidak digunakan di semua jalur kode, sebaiknya lakukan inisialisasi berdasarkan permintaan:

Node.js

const functions = require('@google-cloud/functions-framework');

// Always initialized (at cold-start)
const nonLazyGlobal = fileWideComputation();

// Declared at cold-start, but only initialized if/when the function executes
let lazyGlobal;

/**
 * HTTP function that uses lazy-initialized globals
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
functions.http('lazyGlobals', (req, res) => {
  // This value is initialized only if (and when) the function is called
  lazyGlobal = lazyGlobal || functionSpecificComputation();

  res.send(`Lazy global: ${lazyGlobal}, non-lazy global: ${nonLazyGlobal}`);
});

Python

import functions_framework

# Always initialized (at cold-start)
non_lazy_global = file_wide_computation()

# Declared at cold-start, but only initialized if/when the function executes
lazy_global = None


@functions_framework.http
def lazy_globals(request):
    """
    HTTP Cloud Function that uses lazily-initialized globals.
    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>.
    """
    global lazy_global, non_lazy_global

    # This value is initialized only if (and when) the function is called
    if not lazy_global:
        lazy_global = function_specific_computation()

    return f"Lazy: {lazy_global}, non-lazy: {non_lazy_global}."

Go


// Package tips contains tips for writing Cloud Functions in Go.
package tips

import (
	"context"
	"log"
	"net/http"
	"sync"

	"cloud.google.com/go/storage"
	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
)

// client is lazily initialized by LazyGlobal.
var client *storage.Client
var clientOnce sync.Once

func init() {
	functions.HTTP("LazyGlobal", LazyGlobal)
}

// LazyGlobal is an example of lazily initializing a Google Cloud Storage client.
func LazyGlobal(w http.ResponseWriter, r *http.Request) {
	// You may wish to add different checks to see if the client is needed for
	// this request.
	clientOnce.Do(func() {
		// Pre-declare an err variable to avoid shadowing client.
		var err error
		client, err = storage.NewClient(context.Background())
		if err != nil {
			http.Error(w, "Internal error", http.StatusInternalServerError)
			log.Printf("storage.NewClient: %v", err)
			return
		}
	})
	// Use client.
}

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 LazyFields implements HttpFunction {
  // Always initialized (at cold-start)
  // Warning: Class variables used in Servlet classes must be thread-safe,
  // or else might introduce race conditions in your code.
  private static final int NON_LAZY_GLOBAL = fileWideComputation();

  // Declared at cold-start, but only initialized if/when the function executes
  // Uses the "initialization-on-demand holder" idiom
  // More information: https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
  private static class LazyGlobalHolder {
    // Making the default constructor private prohibits instantiation of this class
    private LazyGlobalHolder() {}

    // This value is initialized only if (and when) the getLazyGlobal() function below is called
    private static final Integer INSTANCE = functionSpecificComputation();

    private static Integer getInstance() {
      return LazyGlobalHolder.INSTANCE;
    }
  }

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    Integer lazyGlobal = LazyGlobalHolder.getInstance();

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Lazy global: %s; non-lazy global: %s%n", lazyGlobal, NON_LAZY_GLOBAL);
  }

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

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

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace LazyFields;

public class Function : IHttpFunction
{
    // This computation runs at server cold-start.
    // Warning: Class variables used in functions code must be thread-safe.
    private static readonly int NonLazyGlobal = FileWideComputation();

    // This variable is initialized at server cold-start, but the
    // computation is only performed when the function needs the result.
    private static readonly Lazy<int> LazyGlobal = new Lazy<int>(
        FunctionSpecificComputation,
        LazyThreadSafetyMode.ExecutionAndPublication);

    public async Task HandleAsync(HttpContext context)
    {
        // In a more complex function, there might be some paths that use LazyGlobal.Value,
        // and others that don't. The computation is only performed when necessary, and
        // only once per server.
        await context.Response.WriteAsync(
            $"Lazy global: {LazyGlobal.Value}; non-lazy global: {NonLazyGlobal}");
    }

    private static int FunctionSpecificComputation()
    {
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        return numbers.Sum();
    }

    private static int FileWideComputation()
    {
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        return numbers.Aggregate((current, next) => current * next);
    }
}

Ruby

FunctionsFramework.on_startup do
  # This method is called when the function is initialized, not on each
  # invocation.

  # Declare and set non_lazy_global
  set_global :non_lazy_global, file_wide_computation

  # Declare, but do not set, lazy_global
  set_global :lazy_global do
    function_specific_computation
  end
end

FunctionsFramework.http "tips_lazy" do |_request|
  # This method is called every time this function is called.

  "Lazy: #{global :lazy_global}; non_lazy: #{global :non_lazy_global}"
end

PHP

Fungsi PHP tidak dapat mempertahankan variabel di antara permintaan. Contoh cakupan di atas menggunakan pemuatan lambat untuk meng-cache nilai variabel global dalam file.

Hal ini sangat penting jika Anda menetapkan beberapa fungsi dalam satu file, dan fungsi yang berbeda menggunakan variabel yang berbeda. Jika tidak menggunakan inisialisasi berdasarkan permintaan, Anda dapat menyia-nyiakan resource pada variabel yang telah diinisialisasi tapi tidak pernah digunakan.

Mengurangi cold start dengan menetapkan jumlah minimum instance

Secara default, Cloud Functions menskalakan jumlah instance berdasarkan jumlah permintaan yang masuk. Anda dapat mengubah perilaku default ini dengan menetapkan jumlah minimum instance yang harus selalu disiapkan oleh Cloud Functions untuk menyalurkan permintaan. Menetapkan jumlah minimum instance akan mengurangi cold start pada aplikasi Anda. Sebaiknya tetapkan jumlah minimum instance jika aplikasi Anda sensitif terhadap latensi.

Untuk mempelajari cara menetapkan jumlah instance minimum, baca Menggunakan instance minimum.

Referensi lain

Cari tahu selengkapnya tentang pengoptimalan performa di video "Google Cloud Performance Atlas" Cloud Functions Cold Boot Time.