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

Tính đơ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 có máy chủ. Ở quy mô vừa phải, chi phí chạy các hàm sẽ thấp và việc tối ưu hoá mã có vẻ không phải là ưu tiên hàng đầu. Tuy nhiên, khi quy mô triển khai của bạn tăng lên, việc tối ưu hoá mã sẽ trở nên ngày càng quan trọng.

Tài liệu này mô tả cách tối ưu hoá kết nối mạng cho các hàm của bạn. Dưới đâ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 kết nối hoặc hạn mứ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 ổn định trong một hàm. Nếu không làm như vậy, bạn có thể nhanh chóng dùng hết hạn mức kết nối.

Phần này đề cập đến các tình huống sau:

  • HTTP/S
  • Google API

Yêu cầu HTTP/S

Đoạn mã được tối ưu hoá dưới đây cho biết cách duy trì các kết nối ổn định thay vì tạo kết nối mới cho mỗi lệnh gọi hàm:

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!")
    

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ể được 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, ví dụ: Cloud Natural Language hoặc Cloud Spanner. Xin lưu ý rằng việc cải thiện hiệu suất có thể phụ 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 đối tượng ứng dụng 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:

node.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")
    

Hàm HTTP này sử dụng một thực thể 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. 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ể được 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 tự động trong môi trường 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

Hệ thống sẽ hế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 ở trạng thái rảnh.

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

Đôi khi, luồng kết nối từ hàm của bạn đến cả mạng VPC và Internet 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 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 kết nối đã ngắt.

Kiểm thử tải hàm

Để đo lường số lượng kết nối mà hàm của bạn thực hiện trung bình, bạn chỉ cần triển khai hàm đó dưới dạng hàm HTTP và sử dụng khung kiểm thử hiệu suất để gọi hàm đó ở một số QPS nhất định. Bạn có thể chọn Artillery (Pháo binh) và 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 ở mức khoảng 30 (hoặc nhiều hơn), thì bạn đang thiết lập một (hoặc một vài) kết nối trong mỗi lệnh gọi. Sau khi tối ưu hoá mã, bạn sẽ thấy một vài (10-30) kết nối chỉ xảy ra ở đầ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.