אופטימיזציה של הרשת

הפשטות של Cloud Functions מאפשרת לכם לפתח קוד ולהריץ אותו במהירות ללא שרת (serverless). בקנה מידה בינוני, העלות של הפעלת פונקציות נמוכה, ואולי אופטימיזציה של הקוד לא נראית כעדיפות גבוהה. כפריסה שלך גבוהה יותר, אבל החשיבות של האופטימיזציה של הקוד הולכת וגדלה.

במסמך הזה נסביר איך לבצע אופטימיזציה של הרישות לפונקציות שלכם. חלק מ- אלה היתרונות של אופטימיזציה לרשתות:

  • הפחתת זמן המעבד (CPU) ליצירת חיבורים יוצאים חדשים בכל פעם בקשה להפעלת פונקציה.
  • כדאי לצמצם את הסיכוי שיתנתקו החיבור או ה-DNS מכסות.

שמירה על קשרים עקביים

בקטע הזה מפורטות דוגמאות לשמירה על חיבורים מתמידים בפונקציה. אם לא תעשו זאת, מכסות החיבור שלכם ינוצלו במהירות.

בקטע הזה מתוארים התרחישים הבאים:

  • HTTP/S
  • Google APIs

בקשות 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) ומחזירה טקסט התשובה, או כל קבוצת ערכים שאפשר להפוך אובייקט Response באמצעות make_response.

גישה לממשקי ה-API של Google

הדוגמה הבאה משתמשת ב-Cloud Pub/Sub, אבל הגישה הזאת עובדת גם בספריות לקוח אחרות, Cloud Natural Language או Cloud Spanner. שימו לב שהביצועים השיפורים עשויים להיות תלויים ביישום הנוכחי של לקוח מסוים של הספריות.

יצירת אובייקט לקוח Pub/Sub יוצרת חיבור אחד ושתי שאילתות DNS לכל קריאה. כדי להימנע מחיבורים ומשאילתות DNS מיותרים, יוצרים את אובייקט הלקוח של Pub/Sub ברמת ה-global, כפי שמוצג בדוגמה הבאה:

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

פונקציית ה-HTTP הזו משתמשת במופע של ספריית לקוח שנשמרה במטמון כדי לצמצם את מספר החיבורים הנדרשים בכל הפעלה של פונקציה. היא לוקחת אובייקט בקשה (flask.Request) ומחזירה טקסט התשובה, או כל קבוצת ערכים שאפשר להפוך אובייקט Response באמצעות make_response.

משתנה הסביבה GCP_PROJECT מוגדר באופן אוטומטי ב-Python 3.7 זמן ריצה. בסביבות זמני ריצה מאוחרים יותר, חשוב לציין פריסת פונקציה. צפייה להגדיר משתני סביבה.

החיבורים היוצאים מתאפסים

שידורי החיבור מהפונקציה אל VPC וגם לאינטרנט יכולים נסגרים מדי פעם ומוחלפים כאשר התשתית הבסיסית מופעלת מחדש או מעודכנת. אם האפליקציה שלך עושה שימוש חוזר בחיבורים לטווח ארוך, מומלץ להגדיר את האפליקציה ליצור מחדש חיבורים כדי להימנע משימוש חוזר בחיבור מת.

בדיקת הפונקציה

כדי למדוד כמה חיבורים הפונקציה שלך מבצעת בממוצע, פשוט פורסים אותו כפונקציית HTTP ולהשתמש ב-framework לבדיקת ביצועים כדי להפעיל אותו QPS מסוים. אפשרות אחת היא ארטילריה, יכול להפעיל באמצעות שורה אחת:

$ artillery quick -d 300 -r 30 URL

הפקודה הזו מאחזרת את כתובת ה-URL הנתונה ב- 30 QPS למשך 300 שניות.

לאחר ביצוע הבדיקה, בדקו את השימוש במכסת החיבורים דף מכסת API של Cloud Functions במסוף Cloud. אם השימוש הוא בערך 30 (או מכפיל של 30) באופן עקבי, אתם יוצרים חיבור אחד (או כמה חיבורים) בכל קריאה. אחרי לבצע אופטימיזציה של הקוד, אמורים להופיע כמה (10-30) חיבורים רק בתחילת הבדיקה.

אפשר גם להשוות את עלות המעבד (CPU) לפני ואחרי האופטימיזציה בתרשים המכסות של המעבד (CPU) באותו דף.