دوال HTTP السحابية في بايثون

1. مقدمة

b158ce75c3cccd6d.png

Python هي لغة برمجة مفتوحة المصدر وشائعة الاستخدام من قِبل علماء البيانات ومطوّري تطبيقات الويب ومشرفي الأنظمة وغيرهم.

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

هناك نوعان من Cloud Functions:

  • تستجيب دوال HTTP لطلبات HTTP. ستنشئ بعضًا منها في هذا الدرس التطبيقي حول الترميز.
  • يتم تشغيل وظائف الخلفية من خلال أحداث، مثل نشر رسالة على Cloud Pub/Sub أو تحميل ملف إلى Cloud Storage. لن نتناول هذا الموضوع في هذا التمرين، ولكن يمكنك الاطّلاع على مزيد من المعلومات في المستندات.

efb3268e3b74ed4f.png

سيرشدك هذا الدرس التطبيقي حول الترميز خلال إنشاء دوال Cloud Functions الخاصة بك في Python.

ما ستقوم بإنشائه

في هذا الدرس التطبيقي حول الترميز، ستنشر دالة Cloud Function تعرض شعار "Python Powered" عند استدعائها عبر بروتوكول HTTP:

a7aaf656b78050fd.png

أهداف الدورة التعليمية

  • كيفية كتابة دالة Cloud Function عبر بروتوكول HTTP
  • كيفية كتابة دالة Cloud Function عبر بروتوكول HTTP تقبل الوسيطات
  • كيفية اختبار دالة Cloud Function عبر بروتوكول HTTP
  • كيفية تشغيل خادم HTTP محلي بلغة Python لتجربة الدالة
  • كيفية كتابة دالة HTTP Cloud تعرض صورة

2. الإعداد والمتطلبات

إعداد بيئة الاختبار الذاتي

  1. سجِّل الدخول إلى Google Cloud Console وأنشئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. إذا لم يكن لديك حساب على Gmail أو Google Workspace، عليك إنشاء حساب.

fbef9caa1602edd0.pnga99b7ace416376c4.png5e3ff691252acf41.png

  • اسم المشروع هو الاسم المعروض للمشاركين في هذا المشروع. وهي سلسلة أحرف لا تستخدمها Google APIs. ويمكنك تعديلها في أي وقت.
  • رقم تعريف المشروع فريد في جميع مشاريع Google Cloud وغير قابل للتغيير (لا يمكن تغييره بعد ضبطه). تنشئ Cloud Console تلقائيًا سلسلة فريدة، وعادةً لا يهمّك ما هي. في معظم الدروس التطبيقية حول الترميز، ستحتاج إلى الإشارة إلى رقم تعريف المشروع (يتم تحديده عادةً على أنّه PROJECT_ID). إذا لم يعجبك رقم التعريف الذي تم إنشاؤه تلقائيًا، يمكنك إنشاء رقم تعريف عشوائي آخر. يمكنك بدلاً من ذلك تجربة اسم من اختيارك والتحقّق من توفّره. ولا يمكن تغييره بعد هذه الخطوة ويظل ساريًا طوال مدة المشروع.
  • للعلم، هناك قيمة ثالثة، وهي رقم المشروع، تستخدمها بعض واجهات برمجة التطبيقات. يمكنك الاطّلاع على مزيد من المعلومات عن هذه القيم الثلاث في المستندات.
  1. بعد ذلك، عليك تفعيل الفوترة في Cloud Console لاستخدام موارد/واجهات برمجة تطبيقات Cloud. لن يتطلّب تنفيذ هذا الدرس التطبيقي حول الترميز تكلفة كبيرة، هذا إن تطلّب أي تكلفة على الإطلاق. لإيقاف الموارد لتجنُّب تحمُّل تكاليف الفوترة بعد هذا البرنامج التعليمي، يمكنك حذف الموارد التي أنشأتها أو حذف المشروع. يمكن لمستخدمي Google Cloud الجدد الاستفادة من برنامج الفترة التجريبية المجانية بقيمة 300 دولار أمريكي.

بدء Cloud Shell

على الرغم من إمكانية تشغيل Google Cloud عن بُعد من الكمبيوتر المحمول، ستستخدم في هذا الدرس التطبيقي حول الترميز Cloud Shell، وهي بيئة سطر أوامر تعمل في السحابة الإلكترونية.

تفعيل Cloud Shell

  1. من Cloud Console، انقر على تفعيل Cloud Shell 853e55310c205094.png.

3c1dabeca90e44e5.png

إذا كانت هذه هي المرة الأولى التي تبدأ فيها Cloud Shell، ستظهر لك شاشة وسيطة تصف ما هي. إذا ظهرت لك شاشة وسيطة، انقر على متابعة.

9c92662c6a846a5c.png

من المفترض أن يستغرق توفير Cloud Shell والاتصال به بضع ثوانٍ فقط.

9f0e51b578fecce5.png

يتم تحميل هذا الجهاز الافتراضي بجميع أدوات التطوير اللازمة. وتوفّر هذه الأداة دليلاً رئيسيًا بسعة 5 غيغابايت وتعمل في Google Cloud، ما يحسّن أداء الشبكة والمصادقة بشكل كبير. يمكن تنفيذ معظم العمل في هذا الدرس التطبيقي حول الترميز، إن لم يكن كله، باستخدام متصفّح.

بعد الاتصال بـ Cloud Shell، من المفترض أن تظهر لك رسالة تفيد بأنّه تمت مصادقتك وأنّ المشروع تم ضبطه على رقم تعريف مشروعك.

  1. نفِّذ الأمر التالي في Cloud Shell للتأكّد من مصادقتك:
gcloud auth list

مخرجات الأمر

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. نفِّذ الأمر التالي في Cloud Shell للتأكّد من أنّ أمر gcloud يعرف مشروعك:
gcloud config list project

مخرجات الأمر

[core]
project = <PROJECT_ID>

إذا لم يكن كذلك، يمكنك تعيينه من خلال هذا الأمر:

gcloud config set project <PROJECT_ID>

مخرجات الأمر

Updated property [core/project].

التأكّد من تفعيل Cloud Functions وCloud Build API

شغِّل الأمر التالي من Cloud Shell للتأكّد من تفعيل واجهتَي برمجة التطبيقات Cloud Functions وCloud Build:

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

ملاحظة: سيتم استدعاء Cloud Build من خلال الأمر gcloud functions deploy وسينشئ تلقائيًا الرمز البرمجي في صورة حاوية.

تنزيل الرمز المصدر

من وحدة Cloud Shell الطرفية، نفِّذ الأوامر التالية:

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

اطّلِع على محتوى دليل ملفات المصدر:

ls

يجب أن يكون لديك الملفات التالية:

main.py  python-powered.png  test_main.py  web_app.py

3- مقدّمة حول دوال Cloud Functions عبر بروتوكول HTTP

تتم كتابة دوال Cloud Functions عبر بروتوكول HTTP بلغة Python كدوال عادية في Python. يجب أن تقبل الدالة وسيطة flask.Request واحدة، والتي عادةً ما تُسمّى request.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

يمكنك فتح الملف باستخدام محرّر سطر الأوامر المفضّل لديك (nano أو vim أو emacs). يمكنك أيضًا فتحه في "محرِّر Cloud Shell" بعد ضبط دليل ملفات المصدر كمساحة عمل:

cloudshell open-workspace .

لننشر هذه الدالة كدالة HTTP Cloud Function باستخدام الأمر gcloud functions deploy:

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

ناتج الأمر:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

ملاحظات حول خيارات "gcloud functions deploy":

  • ‫--runtime: تحدّد هذه السمة بيئة تشغيل اللغة. بالنسبة إلى Python، يمكن أن تكون هذه القيمة حاليًا python37 أو python38 أو python39 أو python310 أو python312. يُرجى الاطّلاع على أوقات التشغيل.
  • ‫--trigger-http: سيتم تعيين نقطة نهاية للدالة. ستؤدي طلبات HTTP‏ (POST وPUT وGET وDELETE وOPTIONS) إلى نقطة النهاية إلى تنفيذ الدالة.
  • ‫--allow-unauthenticated: ستكون الدالة علنية، ما يسمح لجميع المتصلين بالوصول إليها بدون التحقّق من المصادقة.
  • لمزيد من المعلومات، يُرجى الاطّلاع على نشر دوال gcloud.

لاختبار الدالة، يمكنك النقر على عنوان URL الخاص بـ httpsTrigger.url المعروض في ناتج الأمر أعلاه. يمكنك أيضًا استرداد عنوان URL آليًا واستدعاء الدالة باستخدام الأوامر التالية:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

من المفترض أن تحصل على النتيجة التالية:

Hello World! 👋

4. كتابة دالة Cloud Function عبر بروتوكول HTTP تقبل وسيطات

تكون الدوال أكثر تنوعًا عندما تقبل الوسيطات. لنعرّف دالة جديدة باسم hello_name تتيح استخدام المَعلمة name:

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

لننشر هذه الدالة الجديدة:

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

ناتج الأمر:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

لاختبار الدالة، يمكنك النقر على عنوان URL الخاص بـ httpsTrigger.url المعروض في ناتج الأمر أعلاه. يمكنك أيضًا استرداد عنوان URL آليًا واستدعاء الدالة باستخدام الأوامر التالية:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

من المفترض أن تظهر لك النتيجة التلقائية:

Hello World! 🚀

تظهر لك النتيجة التلقائية لأنّه لم يتم ضبط الوسيطة name. أضِف مَعلمة إلى عنوان URL:

curl -w "\n" $URL?name=YOUR%20NAME

هذه المرة، ستحصل على ردّك المخصّص:

Hello YOUR NAME! 🚀

الخطوة التالية هي إضافة اختبارات الوحدات لضمان استمرار عمل الدوال على النحو المطلوب عند تعديل الرمز المصدر.

5- كتابة الاختبارات

يتم اختبار دوال Cloud Functions عبر بروتوكول HTTP بلغة Python باستخدام وحدة unittest من المكتبة العادية. ليست هناك حاجة إلى تشغيل محاكي أو محاكاة أخرى لاختبار الدالة، بل يكفي استخدام رمز Python عادي.

في ما يلي مثال على اختبار لدالتَي hello_world وhello_name:

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. تتم كتابة اختبارات Python بنفس طريقة كتابة ملفات Python الأخرى. تبدأ بمجموعة من عمليات الاستيراد، ثم تحدّد الفئات والدوال.
  2. يجب أن يكون بيان الاختبار على النحو التالي: class TestHello(TestCase). ويجب أن تكون فئة تكتسب من unittest.TestCase.
  3. تحتوي فئة الاختبار على طرق، يجب أن تبدأ كل منها بـ test_، والتي تمثل حالات اختبار فردية.
  4. يختبر كل إطار اختبار إحدى الدوال من خلال محاكاة المَعلمة request (أي استبدالها بعنصر مزيّف يتضمّن البيانات المحدّدة المطلوبة للاختبار).
  5. بعد استدعاء كل دالة، يتحقّق الاختبار من استجابة HTTP للتأكّد من أنّها كانت كما نتوقّع.

بما أنّ main.py يعتمد على flask، تأكَّد من تثبيت إطار عمل Flask في بيئة الاختبار:

pip install flask

يؤدي تثبيت Flask إلى نتيجة مشابهة لما يلي:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

يمكنك إجراء هذه الاختبارات محليًا:

python -m unittest

يجب أن تنجح اختبارات الوحدات الثلاث:

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

بعد ذلك، عليك إنشاء دالة جديدة تعرض شعار "Python Powered".

6. كتابة دالة Cloud Function عبر بروتوكول HTTP "تعمل بلغة Python"

لنجعل الدالة الجديدة أكثر تسلية من خلال عرض صورة "Python Powered" لكل طلب:

a7aaf656b78050fd.png

تعرض القائمة التالية الرمز البرمجي لتحقيق ذلك:

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

نشر دالة python_powered جديدة:

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

ناتج الأمر:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

لاختبار الدالة، انقر على عنوان URL الخاص بـ httpsTrigger.url المعروض في ناتج الأمر أعلاه. إذا كان كل شيء يعمل بشكل صحيح، فسترى شعار "Python Powered" في علامة تبويب متصفح جديدة!

بعد ذلك، ستنشئ تطبيقًا لتتمكّن من تشغيل الدالة وتجربتها على جهازك قبل نشرها.

7. تشغيل الدالة محليًا

يمكنك تشغيل دالة HTTP محليًا من خلال إنشاء تطبيق ويب واستدعاء الدالة في مسار. يمكنك إضافته في الدليل نفسه الذي يتضمّن الدالة. يحتوي الملف الذي يحمل الاسم web_app.py على المحتوى التالي:

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. ينشئ هذا الملف تطبيق Flask.
  2. وتسجّل مسارًا في الجزء الأساسي من عنوان URL الذي تتم معالجته باستخدام دالة باسم index().
  3. بعد ذلك، تستدعي الدالة index() الدالة python_powered، وتمرّر إليها الطلب الحالي.

تأكَّد من تثبيت إطار عمل Flask في بيئة التطوير:

pip install flask

يؤدي تثبيت Flask إلى نتيجة مشابهة لما يلي:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

لتشغيل هذا التطبيق محليًا، نفِّذ الأمر التالي:

python web_app.py

استخدِم الآن ميزة "معاينة الويب" في Cloud Shell لاختبار تطبيق الويب في متصفّحك. في Cloud Shell، انقر على الزر "معاينة الويب" واختَر "معاينة على المنفذ 8080":

6c9ff9e5c692c58e.gif

يفتح Cloud Shell عنوان URL للمعاينة على خدمة الخادم الوكيل في نافذة متصفّح جديدة. تقتصر المعاينة على الويب على الوصول عبر HTTPS إلى حساب المستخدم الخاص بك فقط. إذا كان كل شيء يعمل بشكل صحيح، فمن المفترض أن ترى شعار "Python Powered"!

8e5c3ead11cfd103.png

8. تهانينا!

b158ce75c3cccd6d.png

لقد نشرت دوال HTTP Cloud Functions باستخدام دوال اصطلاحية تعالج طلبات الويب من خلال إطار عمل Flask.

تستند أسعار Cloud Functions إلى عدد مرات استدعاء الدالة، بما في ذلك فئة مجانية للدوال التي لا يتم تشغيلها كثيرًا. بعد الانتهاء من اختبار دوال Cloud Functions، يمكنك حذفها باستخدام gcloud:

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

يمكنك أيضًا حذف الدوال من وحدة تحكّم Google Cloud.

نأمل أن تستمتع باستخدام Cloud Functions في Python.