優化網絡

Cloud Functions 的簡單性使您可以快速開發程式碼並在無伺服器環境中運行它。在中等規模下,運行函數的成本較低,優化程式碼可能看起來不是一個高優先級。然而,隨著部署規模的擴大,優化程式碼變得越來越重要。

本文檔描述如何優化您的功能的網路。優化網路的一些好處如下:

  • 減少每次函數呼叫時建立新連線所花費的 CPU 時間。
  • 降低連線或 DNS配額耗盡的可能性。

維護持久連接

本節提供如何在函數中維護持久連接的範例。否則可能會導致連接配額快速耗盡。

本節涵蓋以下場景:

  • HTTP/S
  • GoogleAPI

HTTP/S 請求

下面的最佳化程式碼片段展示如何維護持久連接,而不是在每次函數呼叫時建立新連接:

Node.js

const http = require('http');
const functions = require('firebase-functions');

// Setting the `keepAlive` option to `true` keeps
// connections open between function invocations
const agent = new http.Agent({keepAlive: true});

exports.function = functions.https.onRequest((request, response) => {
    req = http.request({
        host: '',
        port: 80,
        path: '',
        method: 'GET',
        agent: agent, // Holds the connection open after the first invocation
    }, res => {
        let rawData = '';
        res.setEncoding('utf8');
        res.on('data', chunk => { rawData += chunk; });
        res.on('end', () => {
            response.status(200).send(`Data: ${rawData}`);
        });
    });
    req.on('error', e => {
        response.status(500).send(`Error: ${e.message}`);
    });
    req.end();
});

Python

from firebase_functions import https_fn
import requests

# Create a global HTTP session (which provides connection pooling)
session = requests.Session()

@https_fn.on_request()
def connection_pooling(request):

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

    # Process the request
    response = session.get(url)
    response.raise_for_status()
    return https_fn.Response("Success!")
    

此 HTTP 函數使用連線池來發出 HTTP 請求。它接受一個請求物件( flask.Request )並傳回回應文本,或者可以使用make_response轉換為Response物件的任何值集。

存取 Google API

下面的範例使用Cloud Pub/Sub ,但此方法也適用於其他客戶端程式庫,例如Cloud Natural LanguageCloud Spanner 。請注意,效能改進可能取決於特定客戶端庫的當前實作。

建立 Pub/Sub 用戶端物件會導致每次呼叫產生一個連線和兩個 DNS 查詢。為了避免不必要的連線和 DNS 查詢,請在全域範圍內建立 Pub/Sub 用戶端對象,如下例所示:

節點.js

const PubSub = require('@google-cloud/pubsub');
const functions = require('firebase-functions');
const pubsub = PubSub();

exports.function = functions.https.onRequest((req, res) => {
    const topic = pubsub.topic('');

    topic.publish('Test message', err => {
        if (err) {
            res.status(500).send(`Error publishing the message: ${err}`);
        } else {
            res.status(200).send('1 message published');
        }
    });
});

Python

import os

from firebase_functions import https_fn
from google.cloud import pubsub_v1

# from firebase_functions import https_fn
# Create a global Pub/Sub client to avoid unneeded network activity
pubsub = pubsub_v1.PublisherClient()

@https_fn.on_request()
def gcp_api_call(request):

    project = os.getenv("GCP_PROJECT")
    request_json = request.get_json()

    topic_name = request_json["topic"]
    topic_path = pubsub.topic_path(project, topic_name)

    # Process the request
    data = b"Test message"
    pubsub.publish(topic_path, data=data)

    return https_fn.Response("1 message published")
    

此 HTTP 函數使用快取的用戶端庫實例來減少每次函數呼叫所需的連線數。它接受一個請求物件( flask.Request )並傳回回應文本,或者可以使用make_response轉換為Response物件的任何值集。

GCP_PROJECT環境變數在 Python 3.7 運行時中自動設定。在以後的運行時,請確保在函數部署時指定它。請參閱配置環境變數

對您的函數進行負載測試

要測量您的函數平均執行的連接數,只需將其部署為 HTTP 函數並使用效能測試框架以特定 QPS 呼叫它即可。一個可能的選擇是Artillery ,您可以使用一行來呼叫它:

$ artillery quick -d 300 -r 30 URL

此命令以 30 QPS 的速度獲取給定 URL,持續 300 秒。

測試完成後,您可以在 Cloud Console 的Cloud Functions API 配額頁面查看連線配額的使用情況。如果使用率始終在 30(或其倍數)左右,則您將在每次呼叫中建立一個(或多個)連線。優化程式碼後,您應該會看到僅在測試開始時出現一些 (10-30) 個連線。

您也可以在同一頁的CPU配額圖上比較最佳化前後的CPU成本。