Cloud Functions 実行環境

Cloud Functions は、Google がユーザーの代わりにインフラストラクチャ、オペレーティング システム、およびランタイム環境を運用する、完全に管理されたサーバーレス環境で動作します。各 Cloud 関数は、互いに切り離された独自の安全な実行コンテキストで動作し、自動的にスケーリングします。また、そのライフサイクルは他の関数とは独立しています。

ランタイム

Cloud Functions では、複数の言語のランタイムをサポートしています。

ランタイム ベースイメージ
Node.js 6(非推奨) Debian 8
Node.js 8(非推奨) Ubuntu 18.04
Node.js 10 Ubuntu 18.04
Node.js 12 Ubuntu 18.04
Python 3.7 Ubuntu 18.04
Python 3.8 Ubuntu 18.04
Go 1.11 Ubuntu 18.04
Go 1.13 Ubuntu 18.04
Java 11 Ubuntu 18.04
.NET Core 3.1 Ubuntu 18.04

特に断りのない限り、ランタイムの更新は通常自動的に行われます。言語コミュニティで利用可能になると、すべてのランタイムが言語バージョンに対する更新を自動的に受信します。同様に、Cloud Functions はオペレーティング システムや含まれているパッケージなど、実行環境の他の部分にも更新を適用します。これらの更新により、関数の保護を継続的に行うことができます。

ステートレス関数

Cloud Functions はサーバーレス パラダイムを実装しています。このパラダイムでは、サーバーや仮想マシンなどの基礎となるインフラストラクチャを意識せずにコードを実行できます。Google に関数の自動管理とスケーリングを許可するには、それらがステートレスである必要があります。つまり、1 つの関数呼び出しが、前の呼び出しで設定されたメモリ内の状態に依存しないようにする必要があります。ただし、多くの場合、既存の状態をパフォーマンスの最適化に再利用できます。詳細については、ヒントとアドバイスの推奨事項をご覧ください。

たとえば、呼び出しは、グローバル変数、メモリ、ファイル システムなどの状態を共有していない複数の関数インスタンスによって処理される可能性があるため、後続の関数によって返されるカウンタ値は総関数呼び出し回数と一致しません。

Node.js

// Global variable, but only shared within function instance.
let count = 0;

/**
 * HTTP Cloud Function that counts how many times
 * it is executed within a specific instance.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.executionCount = (req, res) => {
  count++;

  // Note: the total function invocation count across
  // all instances may not be equal to this value!
  res.send(`Instance execution count: ${count}`);
};

Python

# Global variable, modified within the function by using the global keyword.
count = 0

def statelessness(request):
    """
    HTTP Cloud Function that counts how many times it is executed
    within a specific instance.
    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 count
    count += 1

    # Note: the total function invocation count across
    # all instances may not be equal to this value!
    return 'Instance execution count: {}'.format(count)

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
)

// count is a global variable, but only shared within a function instance.
var count = 0

// ExecutionCount is an HTTP Cloud Function that counts how many times it
// is executed within a specific instance.
func ExecutionCount(w http.ResponseWriter, r *http.Request) {
	count++

	// Note: the total function invocation count across
	// all instances may not be equal to this value!
	fmt.Fprintf(w, "Instance execution count: %d", count)
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutionCount implements HttpFunction {

  private final AtomicInteger count = new AtomicInteger(0);

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    count.getAndIncrement();

    // Note: the total function invocation count across
    // all instances may not be equal to this value!
    BufferedWriter writer = response.getWriter();
    writer.write("Instance execution count: " + count);
  }
}

C#

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

namespace ExecutionCount
{
    public class Function : IHttpFunction
    {
        // Note that this variable must be static, because a new instance is
        // created for each request. An alternative approach would be to use
        // dependency injection with a singleton resource injected into the function
        // constructor.
        private static int _count;

        public async Task HandleAsync(HttpContext context)
        {
            // Note: the total function invocation count across
            // all servers may not be equal to this value!
            int currentCount = Interlocked.Increment(ref _count);
            await context.Response.WriteAsync($"Server execution count: {currentCount}");
        }
    }
}

関数呼び出し間で状態を共有する必要がある場合は、関数で DatastoreFirestoreCloud Storage などのサービスを使用してデータを保持する必要があります。利用可能なストレージ オプションについては、ストレージ オプションの選択をご覧ください。

自動スケーリングと同時実行

Cloud Functions は、受信したリクエストを関数のインスタンスに割り当てて処理します。リクエストの量と既存の関数インスタンスの数に応じて、Cloud Functions は既存のインスタンスに要求を割り当てるか、新しいインスタンスを作成します。

関数の各インスタンスは一度に 1 つのリクエストを処理します。コードが 1 つのリクエストを処理している間、次のリクエストが同じインスタンスにルーティングされることはありません。元のリクエストは、リクエストしたすべてのリソース(CPU とメモリ)を使用できます。

受信リクエストの量が既存のインスタンス数を超えた場合、Cloud Functions は複数の新しいインスタンスを開始し、リクエストを処理します。この自動スケーリングにより、Cloud Functions は関数の異なるインスタンスを使用して、複数のリクエストを同時に処理できるようになります。

同時リクエストは別々の関数インスタンスによって処理されるため、変数またはローカルメモリが共有されません。詳細については、このドキュメントの後半で説明します。

自動スケーリングの制御

Cloud Functions では、同時に存在可能な関数インスタンスの数の上限を設定できます。ただし、無制限のスケーリングが好ましくな場合もあります。たとえば、関数が Cloud Functions と同程度にスケーリングできないリソース(データベースなど)に依存している場合があります。リクエスト量が急増すると、Cloud Functions がデータベースの許容範囲を超えて関数インスタンスを作成する可能性があります。

コールド スタート

新しい関数インスタンスは、次の 2 つのケースで開始されます。

  • 関数がデプロイされた時点

  • 負荷をスケールアップするため、または、場合によっては既存のインスタンスを置き換えるために、新しい関数インスタンスが自動的に作成された時点

新しい関数インスタンスを開始すると、ランタイムとコードが読み込まれます。関数インスタンスのスタートアップ(コールド スタート)を含むリクエストは、既存の関数インスタンスに送られたリクエストより遅くなる可能性があります。ただし、関数にかかる負荷が安定している場合は、関数が頻繁にクラッシュして、関数環境の再起動が必要にならない限り、コールド スタートの回数は無視できます。エラーを正しく処理して、コールド スタートを回避する方法については、エラーをご覧ください。

関数インスタンスの寿命

一般的に、関数インスタンスを実行する環境は、インスタンス数の減少(持続トラフィックの不足による)や関数のクラッシュが発生しない限り、回復力があり、後続の関数呼び出しで再利用されます。つまり、ある関数の実行が終了すると、別の関数呼び出しが同じ関数インスタンスによって処理されます。そのため、可能であれば、グローバル スコープで呼び出し間の状態をキャッシュに保存することをおすすめします。次の呼び出しが同じ関数インスタンスに送られる保証はないため、このキャッシュが使用できなくても機能するように関数を準備する必要があります(ステートレス関数を参照)。

関数スコープとグローバル スコープ

単一の関数呼び出しでは、エントリ ポイントとして宣言された関数の本体だけが実行されます。関数定義が含まれている必要がある関数ファイル内のグローバル スコープは、コールド スタートごとに実行されますが、インスタンスがすでに初期化されている場合は実行されません。

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

グローバル スコープは、関数コードが新しい関数インスタンスで(およびその後の新しい関数インスタンスの作成ごとに)呼び出される前に、一度だけ実行されているとみなすことができます。ただし、グローバル スコープの総実行回数やタイミングは Google が管理する自動スケーリングによって異なるため、それらに依存しないようにしてください。

関数実行タイムライン

関数は、その実行期間にのみ、要求したリソース(CPU とメモリ)にアクセスできます。実行期間外に実行されるコードは必ず実行されるとは限りません。これらのコードはいつでも停止できます。そのため、必ず、関数実行の終了を正確に通知して、それを超えたコードの実行を回避する必要があります。詳細については、HTTP 関数バックグラウンド関数をご覧ください。

たとえば、HTTP レスポンスの送信後に実行されたコードは、いつでも中断される可能性があります。

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to early HTTP response
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterResponse = (req, res) => {
  res.end();

  // This statement may not execute
  console.log('Function complete!');
};

アプリケーションを初期化する場合は、実行タイムラインを考慮してください。バックグラウンド タスクは初期化時にグローバル スコープで作成しないでください。作成すると、リクエストがリクエストの処理時間外に実行されます。

実行保証

通常、関数は、送られてきたイベントごとに 1 回ずつ呼び出されます。ただし、Cloud Functions は、エラーシナリオに違いがあるため、すべてのケースで単一の呼び出しを保証するわけではありません。

1 つのイベントで関数が呼び出される最大回数または最小回数は、関数の種類によって異なります。

  • HTTP 関数は、多くても 1 回しか呼び出されません。これは、HTTP 呼び出しの同期性によるもので、関数呼び出しの処理時に発生したエラーは再試行されずに返されることを意味します。HTTP 関数の呼び出し側は、エラーを処理し、必要に応じて再試行する必要があります。

  • バックグラウンド関数は、少なくとも 1 回呼び出されます。イベントの処理は基本的に非同期で行われます。つまり、呼び出し側がレスポンスを待つ必要はありません。非常にまれですが、イベントを確実に処理するため、バックグラウンド関数が 2 回以上呼び出されることもあります。バックグラウンド関数の呼び出しでエラーが発生した場合、失敗時に再試行が有効になっていない限り、その関数が再び呼び出されることはありません。

関数が再実行時に正しく動作することを保証するには、イベントが複数回発生した場合でも望ましい結果(および副作用)をもたらすように関数を実装することで、関数をべき等にする必要があります。HTTP 関数の場合は、呼び出し側が HTTP 関数エンドポイントに対する呼び出しを再試行した場合でも、関数が望ましい値を返すことも意味します。関数をべき等にする方法の詳細については、バックグラウンド関数の再試行をご覧ください。

エラー

関数でエラーを通知するための推奨されている方法は、関数の種類によって異なります。

  • HTTP 関数は、エラーを示す適切な HTTP ステータス コードを返す必要があります。例については、HTTP 関数をご覧ください。

  • バックグラウンド関数は、エラー メッセージをログに記録して返します。例については、バックグラウンド関数をご覧ください。

推奨されている方法でエラーが返された場合、エラーを返した関数インスタンスは正常に動作するものとしてラベル付けされ、必要に応じて以降のリクエストに対応できます。

自分のコードまたは自分が呼び出したコードが、捕捉されなかった例外をスローしたか、現在のプロセスをクラッシュさせた場合は、次の呼び出しを処理する前に関数インスタンスが再起動されることがあります。この方法は、コールド スタートが増えて、レイテンシが長くなる可能性があるため、推奨されていません。

Cloud Functions でエラーを報告する方法の詳細については、エラーの報告をご覧ください。

タイムアウト

関数の実行時間は、関数のデプロイ時に指定されたタイムアウト時間によって制限されます。デフォルトでは、関数は 1 分後にタイムアウトしますが、この期間を最大 9 分まで延長できます。

関数の実行がタイムアウトするとすぐに、呼び出し側にエラー ステータスが返されます。タイムアウトした関数インスタンスが使用していた CPU リソースは調整されるため、リクエストの処理が一時停止する可能性があります。一時停止した作業は後続のリクエストで処理される場合もあれば、処理されない場合もあります。このため、予期しない問題が発生する可能性があります。

以下のスニペットのコードは、関数の実行開始から 2 分後に実行されるように設定されています。タイムアウトが 1 分に設定された場合は、このコードが実行されることはありません。

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to function execution timeout
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterTimeout = (req, res) => {
  setTimeout(() => {
    // May not execute if function's timeout is <2 minutes
    console.log('Function running...');
    res.end();
  }, 120000); // 2 minute delay
};

Python

def timeout(request):
    print('Function running...')
    time.sleep(120)

    # May not execute if function's timeout is <2 minutes
    print('Function completed!')
    return 'Function completed!'

Go


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

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

// Timeout sleeps for 2 minutes and may time out before finishing.
func Timeout(w http.ResponseWriter, r *http.Request) {
	log.Println("Function execution started...")
	time.Sleep(2 * time.Minute)
	log.Println("Function completed!")
	fmt.Fprintln(w, "Function completed!")
}

Java


package functions;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

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

  // Simple function to return "Hello World"
  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException, InterruptedException {
    logger.info("Function running...");
    TimeUnit.MINUTES.sleep(2);

    // May not execute if function's timeout is <2 minutes
    logger.info("Function completed!");
    BufferedWriter writer = response.getWriter();
    writer.write("Function completed!");
  }
}

上記のコードが正常に実行されても、予期しない結果になることがあります。関数がタイムアウトした場合について考えてみましょう。リクエストを処理しているインスタンスは一時停止します(CPU の調整により)。保留中の作業は一時停止になります。後続のリクエストが同じインスタンスにルーティングされると、作業が再開し、Function running... ログに出力されます。

この動作では、結果として 1 つのリクエストの作業とログが後続のリクエストに「リーク」されています。一時停止した作業が再開するかどうかはわからないため、この動作には依存しないでください。代わりに、次の方法を組み合わせて関数のタイムアウトを回避してください。

  1. 予想される関数の実行時間より長いタイムアウトを設定する。
  2. 実行中に残り時間を追跡し、クリーンアップまたは終了を早めに実行する。

gcloud コマンドライン ツールで関数の最大実行時間を設定するには、デプロイ時に --timeout フラグを使用します。

gcloud functions deploy FUNCTION_NAME --timeout=TIMEOUT FLAGS...

上記のコマンドで、FLAGS... は、関数のデプロイ時に渡す他のオプションを表します。deploy コマンドの詳細については、gcloud functions deploy をご覧ください。

次のように、関数の作成中に Cloud Console でタイムアウトを設定することもできます。

  1. Cloud Console の Cloud Functions の概要ページに移動します。

  2. [関数を作成] をクリックします。

  3. 関数の必須フィールドを入力します。

  4. [詳細] をクリックして、詳細設定を表示します。

  5. [タイムアウト] フィールドに値を入力します。

ファイル システム

関数の実行環境には、実行可能な関数ファイルに加えて、ローカル依存関係などの、デプロイされた関数パッケージに含まれているファイルやディレクトリも存在します。これらのファイルは、読み取り専用ディレクトリで使用できます。このディレクトリは、関数ファイルの場所に基づいて決定できます。関数のディレクトリは、現在の作業ディレクトリと異なる場合があるので注意してください。

次の例は、関数ディレクトリに配置されたファイルを一覧表示します。

Node.js

const fs = require('fs');

/**
 * HTTP Cloud Function that lists files in the function directory
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.listFiles = (req, res) => {
  fs.readdir(__dirname, (err, files) => {
    if (err) {
      console.error(err);
      res.sendStatus(500);
    } else {
      console.log('Files', files);
      res.sendStatus(200);
    }
  });
};

Python

def list_files(request):
    import os
    from os import path

    root = path.dirname(path.abspath(__file__))
    children = os.listdir(root)
    files = [c for c in children if path.isfile(path.join(root, c))]
    return 'Files: {}'.format(files)

Go


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

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

// ListFiles lists the files in the current directory.
// Uses directory "serverless_function_source_code" as defined in the Go
// Functions Framework Buildpack.
// See https://github.com/GoogleCloudPlatform/buildpacks/blob/56eaad4dfe6c7bd0ecc4a175de030d2cfab9ae1c/cmd/go/functions_framework/main.go#L38.
func ListFiles(w http.ResponseWriter, r *http.Request) {
	files, err := ioutil.ReadDir("./serverless_function_source_code")
	if err != nil {
		http.Error(w, "Unable to read files", http.StatusInternalServerError)
		log.Printf("ioutil.ListFiles: %v", err)
		return
	}
	fmt.Fprintln(w, "Files:")
	for _, f := range files {
		fmt.Fprintf(w, "\t%v\n", f.Name())
	}
}

Java


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

public class FileSystem implements HttpFunction {

  // Lists the files in the current directory.
  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    File currentDirectory = new File(".");
    File[] files = currentDirectory.listFiles();
    PrintWriter writer = new PrintWriter(response.getWriter());
    writer.println("Files:");
    for (File f : files) {
      writer.printf("\t%s%n", f.getName());
    }
  }
}

C#

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

namespace FileSystem
{
    public class Function : IHttpFunction
    {
        public async Task HandleAsync(HttpContext context)
        {
            string[] files = Directory.GetFiles(".");

            await context.Response.WriteAsync("Files:\n");
            foreach (string file in files)
            {
                await context.Response.WriteAsync($"\t{file}\n");
            }
        }
    }
}

この関数を使用して、デプロイされた他のファイルからコードを読み込むこともできます。

ファイル システムで書き込み可能な部分は /tmp ディレクトリだけです。このディレクトリは、関数インスタンスの一時ファイルの保存先として使用できます。このディレクトリは、ボリュームに書き込まれたデータがメモリに格納される「tmpfs」ボリュームと呼ばれるローカル ディスクのマウント ポイントです。これにより、関数用にプロビジョニングされたメモリリソースが消費されるので注意してください。

ファイル システムの残りの部分は読み取り専用で、関数からアクセスできます。

ネットワーク

関数は、ランタイム プロバイダまたはサードパーティ プロバイダから提供された標準ライブラリを使用して、パブリック インターネットにアクセスできます。たとえば、次のように HTTP エンドポイントを呼び出すことができます。

Node.js

const fetch = require('node-fetch');

/**
 * HTTP Cloud Function that makes an HTTP request
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.makeRequest = async (req, res) => {
  const url = 'https://example.com'; // URL to send the request to
  const externalRes = await fetch(url);
  res.sendStatus(externalRes.ok ? 200 : 500);
};

Python

def make_request(request):
    """
    HTTP Cloud Function that makes another HTTP request.
    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>.
    """
    import requests

    # The URL to send the request to
    url = 'http://example.com'

    # Process the request
    response = requests.get(url)
    response.raise_for_status()
    return 'Success!'

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
	"time"
)

var urlString = "https://example.com"

// client is used to make HTTP requests with a 10 second timeout.
// http.Clients should be reused instead of created as needed.
var client = &http.Client{
	Timeout: 10 * time.Second,
}

// MakeRequest is an example of making an HTTP request. MakeRequest uses a
// single http.Client for all requests to take advantage of connection
// pooling and caching. See https://godoc.org/net/http#Client.
func MakeRequest(w http.ResponseWriter, r *http.Request) {
	resp, err := client.Get(urlString)
	if err != nil {
		http.Error(w, "Error making request", http.StatusInternalServerError)
		return
	}
	if resp.StatusCode != http.StatusOK {
		msg := fmt.Sprintf("Bad StatusCode: %d", resp.StatusCode)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "ok")
}

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.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;

public class SendHttpRequest implements HttpFunction {

  // Create a client with some reasonable defaults. This client can be reused for multiple requests.
  // (java.net.httpClient also pools connections automatically by default.)
  private static HttpClient client =
      HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException, InterruptedException {
    // Create a GET sendHttpRequest to "http://example.com"
    String url = "http://example.com";
    var getRequest = java.net.http.HttpRequest.newBuilder().uri(URI.create(url)).GET().build();

    // Send the sendHttpRequest using the client
    var getResponse = client.send(getRequest, BodyHandlers.ofString());

    // Write the results to the output:
    var writer = new PrintWriter(response.getWriter());
    writer.printf("Received code '%s' from url '%s'.", getResponse.statusCode(), url);
  }
}

C#

using Google.Cloud.Functions.Framework;
using Google.Cloud.Functions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using System.Threading.Tasks;

namespace SendHttpRequest
{
    // Dependency injection configuration, executed during server startup.
    public class Startup : FunctionsStartup
    {
        public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services)
        {
            // Make an HttpClient available to our function via dependency injection.
            // There are many options here; see
            // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
            // for more details.
            services.AddHttpClient<IHttpFunction, Function>();
        }
    }

    // Function, decorated with the FunctionsStartup attribute to specify the startup class
    // for dependency injection.
    [FunctionsStartup(typeof(Startup))]
    public class Function : IHttpFunction
    {
        private readonly HttpClient _httpClient;

        public Function(HttpClient httpClient) =>
            _httpClient = httpClient;

        public async Task HandleAsync(HttpContext context)
        {
            string url = "http://example.com";
            using (HttpResponseMessage clientResponse = await _httpClient.GetAsync(url))
            {
                await context.Response.WriteAsync($"Received code '{(int) clientResponse.StatusCode}' from URL '{url}'.");
            }
        }
    }
}

ネットワークの最適化で説明されているように、関数呼び出し間でネットワーク接続を再利用してみてください。ただし、システムが 2 分以上未使用状態の接続を閉じた後、切断された接続を再利用しようとすると、「接続のリセット」エラーが発生することがあります。コードは、閉じられた接続を適切に処理するライブラリを使用するか、低レベルのネットワーキング構造を使用している場合は、閉じられた接続を明示的に処理する必要があります。

複数の関数

デプロイされた関数は他のすべての関数から分離されます。これには、同じソースファイルからデプロイされたものも含まれます。特に、メモリ、グローバル変数、ファイル システムなどの状態は共有されません。

デプロイされた関数間でデータを共有するには、DatastoreFirestore、または Cloud Storage などのストレージ サービスを使用します。また、適切なトリガーを使用して、別の関数を呼び出すこともできます。たとえば、HTTP 関数のエンドポイントに HTTP リクエストを送信します。また、Pub/Sub トピックにメッセージをパブリッシュして Pub/Sub 関数をトリガーします。