تحسين الشبكات

تتيح لك بساطة Cloud Functions تطوير الرموز البرمجية بسرعة وتشغيلها في بيئة لا تستخدِم الخادم. على نطاق معتدل، تكون تكلفة تشغيل الدوالّ منخفضة، وقد لا يبدو تحسين الرمز البرمجي من الأولويات العالية. ومع زيادة عمليات النشر ، يصبح تحسين الرمز البرمجي أكثر أهمية.

يصف هذا المستند كيفية تحسين الاتصال بالشبكة لوظائفك. في ما يلي بعض مزايا تحسين الشبكة:

  • يمكنك تقليل وقت وحدة المعالجة المركزية الذي تقضيه في إنشاء اتصالات خارجية جديدة عند كل طلب للوظيفة.
  • الحد من احتمالية نفاذ الحصص الخاصة بالاتصال أو نظام أسماء النطاقات

الحفاظ على اتصالات دائمة

يقدّم هذا القسم أمثلة على كيفية الحفاظ على اتصالات دائمة في دالّة. وقد يؤدي عدم إجراء ذلك إلى استنفاد حصص الاتصال بسرعة.

يتناول هذا القسم السيناريوهات التالية:

  • 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.

الوصول إلى Google APIs

يستخدِم المثال أدناه Cloud Pub/Sub، ولكن يمكن أيضًا استخدام هذا النهج مع مكتبات العملاء الأخرى، مثل Cloud Natural Language أو Cloud Spanner. يُرجى العِلم أنّ تحسينات الأداء قد تعتمد على التنفيذ الحالي لمكتبات العميل المحدّدة.

يؤدّي إنشاء عنصر عميل Pub/Sub إلى إجراء اتصال واحد وطلبَي بحث في نظام أسماء النطاقات لكلّ طلب. لتجنُّب عمليات الاتصال غير الضرورية وطلبات البحث في نظام أسماء النطاقات، أنشئ ملف تعريف العميل Pub/Sub على مستوى عام كما هو موضّح في المثال أدناه:

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. في أوقات التشغيل اللاحقة، احرص على تحديده عند نشر الدالة. راجِع مقالة ضبط متغيّرات البيئة.

عمليات الربط الصادرة

مهلات الطلبات الصادرة

يتم ضبط مهلة بعد 10 دقائق من وقت التوقف عن العمل للطلبات الواردة من وظيفتك إلى شبكة VPC. بالنسبة إلى الطلبات الواردة من دالتك إلى الإنترنت، يتم ضبط مهلة بعد 20 دقيقة من وقت الخمول.

عمليات إعادة ضبط الاتصال الصادر

يمكن أن يتم أحيانًا إنهاء عمليات نقل البيانات من الدالة إلى كلّ من شبكة VPC والإنترنت واستبدالها عند إعادة تشغيل البنية الأساسية أو تعديلها. إذا كان تطبيقك يعيد استخدام اتصالات دائمة، ننصحك بضبط تطبيقك لإعادة إنشاء اتصالات لتجنُّب إعادة استخدام اتصال غير نشط.

اختبار التحميل لوظيفتك

لقياس عدد عمليات الربط التي تُجريها الدالة في المتوسّط، ما عليك سوى نشر هذه الدالة كدالّة HTTP واستخدام إطار عمل لاختبار الأداء لتشغيلها عند عدد معيّن من طلبات البيانات في الثانية. أحد الخيارات المحتملة هو Artillery، والذي يمكنك استدعاؤه بسطر واحد:

$ artillery quick -d 300 -r 30 URL

يُستخدَم هذا الأمر لجلب عنوان URL المحدّد بمعدّل 30 طلب في الثانية لمدة 300 ثانية.

بعد إجراء الاختبار، تحقَّق من استخدام حصة الاتّصال في صفحة حصة Cloud Functions API في Cloud Console. إذا كان الاستخدام ثابتًا عند 30 عملية تقريبًا (أو مضاعفاتها)، يعني ذلك أنّه يتم إنشاء اتصال واحد (أو عدة اتصالات) في كل عملية استدعاء. بعد تحسين الرمز، من المفترض أن تلاحظ حدوث بضع عمليات اتصال (من 10 إلى 30) فقط في بداية الاختبار.

يمكنك أيضًا مقارنة تكلفة وحدة المعالجة المركزية قبل التحسين وبعده في الرسم البياني لحصة وحدة المعالجة المركزية في الصفحة نفسها.