Tối ưu hoá kết nối mạng

Sự đơn giản của Cloud Functions cho phép bạn nhanh chóng phát triển mã và chạy mã đó trong môi trường không máy chủ. Ở quy mô vừa phải, chi phí chạy các hàm là thấp và việc tối ưu hoá mã có thể không phải là việc cần ưu tiên cao. Tuy nhiên, khi quá trình triển khai của bạn mở rộng quy mô, việc tối ưu hoá mã sẽ ngày càng trở nên quan trọng.

Tài liệu này mô tả cách tối ưu hoá hoạt động kết nối mạng cho các hàm của bạn. Sau đây là một số lợi ích của việc tối ưu hoá hoạt động kết nối mạng:

  • Giảm thời gian CPU dành cho việc thiết lập các kết nối đi mới tại mỗi lệnh gọi hàm.
  • Giảm khả năng hết hạn mức kết nối hoặc DNS.

Duy trì kết nối liên tục

Phần này đưa ra ví dụ về cách duy trì các kết nối liên tục trong một hàm. Nếu không làm như vậy, bạn có thể nhanh chóng hết hạn mức kết nối.

Phần này đề cập đến các trường hợp sau:

  • HTTP/S
  • Google API

Yêu cầu HTTP/S

Đoạn mã được tối ưu hoá bên dưới cho biết cách duy trì các kết nối liên tục thay vì tạo một kết nối mới khi mỗi hàm được gọi:

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

Hàm HTTP này sử dụng một nhóm kết nối để tạo các yêu cầu HTTP. Phương thức này lấy một đối tượng yêu cầu (flask.Request) và trả về văn bản phản hồi hoặc bất kỳ tập hợp giá trị nào có thể chuyển thành đối tượng Response bằng cách sử dụng make_response.

Truy cập vào các API của Google

Ví dụ bên dưới sử dụng Cloud Pub/Sub, nhưng phương pháp này cũng hoạt động với các thư viện ứng dụng khác, chẳng hạn như Cloud Natural Language hoặc Cloud Spanner. Xin lưu ý rằng hiệu suất có thể cải thiện tuỳ thuộc vào cách triển khai hiện tại của các thư viện ứng dụng cụ thể.

Việc tạo một đối tượng máy khách Pub/Sub sẽ dẫn đến một kết nối và hai truy vấn DNS cho mỗi lệnh gọi. Để tránh các kết nối và truy vấn DNS không cần thiết, hãy tạo đối tượng ứng dụng Pub/Sub trong phạm vi toàn cục như trong mẫu bên dưới:

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');
        }
    });
});
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")
    

Hàm HTTP này sử dụng một phiên bản thư viện ứng dụng được lưu vào bộ nhớ đệm để giảm số lượng kết nối cần thiết cho mỗi lệnh gọi hàm. Hàm này nhận một đối tượng yêu cầu (flask.Request) và trả về văn bản phản hồi hoặc bất kỳ tập hợp giá trị nào có thể chuyển thành đối tượng Response bằng cách sử dụng make_response.

Biến môi trường GCP_PROJECT được tự động đặt trong thời gian chạy Python 3.7. Trong các thời gian chạy sau này, hãy nhớ chỉ định thời gian chạy đó khi triển khai hàm. Xem phần Định cấu hình biến môi trường.

Kết nối đi

Hết thời gian chờ yêu cầu đi

Có một thời gian chờ sau 10 phút không hoạt động đối với các yêu cầu từ hàm của bạn đến mạng VPC. Đối với các yêu cầu từ hàm của bạn đến Internet, sẽ có thời gian chờ sau 20 phút không hoạt động.

Đã đặt lại kết nối đi

Các luồng kết nối từ hàm của bạn đến cả mạng VPC và Internet đôi khi có thể bị chấm dứt và thay thế khi cơ sở hạ tầng cơ bản được khởi động lại hoặc cập nhật. Nếu ứng dụng của bạn sử dụng lại các kết nối tồn tại lâu dài, bạn nên định cấu hình ứng dụng để thiết lập lại các kết nối nhằm tránh sử dụng lại một kết nối không hoạt động.

Kiểm thử tải cho hàm của bạn

Để đo lường số lượng kết nối mà hàm của bạn thực hiện trung bình, hãy triển khai hàm đó dưới dạng hàm HTTP và sử dụng một khung kiểm thử hiệu suất để gọi hàm đó ở một QPS nhất định. Một lựa chọn có thể là Artillery, bạn có thể gọi bằng một dòng duy nhất:

$ artillery quick -d 300 -r 30 URL

Lệnh này tìm nạp URL đã cho ở tốc độ 30 QPS trong 300 giây.

Sau khi thực hiện kiểm thử, hãy kiểm tra mức sử dụng hạn mức kết nối trên trang hạn mức API Cloud Functions trong Cloud Console. Nếu mức sử dụng luôn ở khoảng 30 (hoặc bội số của 30), thì tức là bạn đang thiết lập một (hoặc một số) kết nối trong mỗi lệnh gọi. Sau khi tối ưu hoá mã, bạn sẽ chỉ thấy một vài (10 – 30) kết nối xảy ra vào đầu quá trình kiểm thử.

Bạn cũng có thể so sánh chi phí CPU trước và sau khi tối ưu hoá trên biểu đồ hạn mức CPU trên cùng một trang.