1. نظرة عامة
تهدف سلسلة البرامج التعليمية حول Serverless Migration Station (برامج تعليمية ذاتية السرعة وعملية) ومقاطع الفيديو ذات الصلة إلى مساعدة مطوّري الحوسبة بدون خادم على Google Cloud في تحديث تطبيقاتهم من خلال إرشادهم خلال عملية نقل واحدة أو أكثر، مع التركيز بشكل أساسي على التخلّي عن الخدمات القديمة. يؤدي ذلك إلى زيادة قابلية نقل تطبيقاتك ويمنحك المزيد من الخيارات والمرونة، ما يتيح لك الدمج مع مجموعة أكبر من منتجات Cloud والوصول إليها، كما يسهّل عليك الترقية إلى أحدث إصدارات اللغة. على الرغم من أنّ هذه السلسلة تركّز في البداية على أوائل مستخدمي Cloud، وخاصةً مطوّري App Engine (البيئة العادية)، إلا أنّها واسعة النطاق بما يكفي لتشمل منصات أخرى بلا خادم، مثل Cloud Functions وCloud Run، أو في أي مكان آخر إذا كان ذلك منطبقًا.
الغرض من هذا الدرس التطبيقي حول الترميز هو تكييف البرنامج من نموذج التطبيق من الوحدة 8 إلى الإصدار 3 من Python، بالإضافة إلى التبديل من استخدام Cloud NDB إلى مكتبة برامج Cloud Datastore الأصلية للوصول إلى مخزن البيانات (وضع Firestore في مخزن البيانات)، والترقية إلى أحدث إصدار من مكتبة برامج Cloud Tasks.
أضفنا استخدام Task Queue لمهام الدفع في الوحدة 7، ثم نقلنا هذا الاستخدام إلى Cloud Tasks في الوحدة 8. في الوحدة 9، سنواصل العمل على Python 3 وCloud Datastore. سيتم نقل المستخدمين الذين يستعينون بـ "قوائم انتظار المهام" لتنفيذ مهام السحب إلى Cloud Pub/Sub، وعليهم الرجوع إلى الوحدتين 18 و19 بدلاً من ذلك.
ستتعرَّف على كيفية إجراء ما يلي:
- نقل نموذج تطبيق الوحدة 8 إلى Python 3
- التبديل من Cloud NDB إلى مكاتب برامج Cloud Datastore للوصول إلى Datastore
- الترقية إلى أحدث إصدار من مكتبة برامج Cloud Tasks
المتطلبات
- مشروع Google Cloud Platform مع حساب فوترة نشط على Google Cloud Platform
- مهارات أساسية في لغة Python
- معرفة عملية بالأوامر الشائعة على نظام التشغيل Linux
- معرفة أساسية بشأن تطوير ونشر تطبيقات App Engine
- تطبيق App Engine للوحدة 8 يعمل: أكمِل درس تطبيقي حول الترميز للوحدة 8 (يُنصح بذلك) أو انسخ تطبيق الوحدة 8 من المستودع
استطلاع
كيف ستستخدم هذا البرنامج التعليمي؟
كيف تقيّم تجربتك مع Python؟
ما هو تقييمك لتجربة استخدام خدمات Google Cloud؟
2. الخلفية
توضّح الوحدة 7 كيفية استخدام مهام الدفع في "قائمة انتظار المهام" في App Engine في تطبيقات Python 2 Flask App Engine. في الوحدة 8، ستنقل هذا التطبيق من "قائمة انتظار المهام" إلى Cloud Tasks. في الوحدة 9، ستواصل هذه الرحلة وتنقل هذا التطبيق إلى الإصدار 3 من لغة Python، بالإضافة إلى التبديل من استخدام Cloud NDB إلى مكتبة برامج العميل الأصلية Cloud Datastore للوصول إلى Datastore.
بما أنّ Cloud NDB تعمل مع كل من Python 2 و3، فهي كافية لمستخدمي App Engine الذين ينقلون تطبيقاتهم من Python 2 إلى 3. إنّ عملية نقل مكتبات البرامج إلى Cloud Datastore هي اختيارية تمامًا، ولا يوجد سبب واحد فقط يدعو إلى التفكير فيها، وهو أنّ لديك تطبيقات غير تابعة لـ App Engine (و/أو تطبيقات Python 3 App Engine) تستخدم حاليًا مكتبة برامج Cloud Datastore وتريد دمج قاعدة الرموز البرمجية للوصول إلى Datastore باستخدام مكتبة برامج واحدة فقط. تم إنشاء Cloud NDB خصيصًا لمطوّري Python 2 App Engine كأداة لنقل البيانات إلى Python 3، لذا إذا لم يكن لديك رمز حاليًا يستخدم مكتبة عملاء Cloud Datastore، لن تحتاج إلى التفكير في عملية نقل البيانات هذه.
أخيرًا، ستتواصل عملية تطوير مكتبة برامج Cloud Tasks فقط في Python 3، لذا نحن "ننقل" إحدى آخر إصدارات Python 2 إلى إصدار Python 3 الحديث. لحسن الحظ، لا توجد تغييرات غير متوافقة مع الإصدار السابق من Python 2، ما يعني أنّه ليس عليك اتّخاذ أي إجراء آخر.
يتضمّن هذا الدليل التعليمي الخطوات التالية:
- الإعداد/العمل التحضيري
- تعديل الإعدادات
- تعديل الرمز البرمجي للتطبيق
3- الإعداد/العمل التحضيري
يوضّح هذا القسم كيفية تنفيذ ما يلي:
- إعداد مشروعك على السحابة الإلكترونية
- الحصول على نموذج تطبيق أساسي
- (إعادة) نشر تطبيق أساسي وإثبات صحته
تضمن هذه الخطوات أنّك تبدأ برمز برمجي يعمل وأنّه جاهز للترحيل إلى خدمات السحابة الإلكترونية.
1. إعداد المشروع
إذا أكملت الدرس التطبيقي حول الترميز في الوحدة 8، أعِد استخدام المشروع (والرمز) نفسه. بدلاً من ذلك، يمكنك إنشاء مشروع جديد تمامًا أو إعادة استخدام مشروع حالي آخر. تأكَّد من أنّ المشروع يتضمّن حساب فوترة نشطًا وتطبيق App Engine مفعَّلاً. ابحث عن رقم تعريف مشروعك لأنّك ستحتاج إليه أثناء هذا الدرس التطبيقي حول الترميز، واستخدِمه كلّما صادفت المتغيّر PROJECT_ID.
2. الحصول على نموذج تطبيق أساسي
أحد المتطلبات الأساسية هو توفّر تطبيق يعمل على "الوحدة 8" من App Engine: أكمل درس تطبيقي حول الترميز الخاص بـ "الوحدة 8" (يُنصح بذلك) أو انسخ تطبيق "الوحدة 8" من المستودع. سواء كنت تستخدم رمزك أو رمزنا، سنبدأ برمز الوحدة 8 ("START"). يرشدك هذا الدرس التطبيقي حول الترميز خلال عملية نقل البيانات، وينتهي برمز يشبه الرمز الموجود في مجلد مستودع الوحدة 9 ("FINISH").
- البدء: مستودع الوحدة 8
- إنهاء: مستودع الوحدة التدريبية 9
- المستودع بأكمله (نسخ أو تنزيل ملف ZIP)
بغض النظر عن تطبيق الوحدة التدريبية 7 الذي تستخدمه، من المفترض أن يبدو المجلد كما هو موضّح أدناه، وربما يتضمّن مجلد lib أيضًا:
$ ls README.md appengine_config.py requirements.txt app.yaml main.py templates
3- (إعادة) نشر تطبيق أساسي وإثبات صحته
اتّبِع الخطوات التالية لنشر تطبيق الوحدة التدريبية 8:
- احذف المجلد
libإذا كان هناك مجلد، ثم شغِّلpip install -t lib -r requirements.txtلإعادة ملءlib. قد تحتاج إلى استخدامpip2بدلاً من ذلك إذا كان لديك كل من Python 2 و3 مثبّتَين على جهاز التطوير. - تأكَّد من أنّك ثبَّت وهيّأت أداة سطر الأوامر
gcloudوراجعت طريقة استخدامها. - (اختياري) اضبط مشروعك على السحابة الإلكترونية باستخدام
gcloud config set projectPROJECT_IDإذا كنت لا تريد إدخالPROJECT_IDمع كل أمرgcloudتصدره. - نشر نموذج التطبيق باستخدام
gcloud app deploy - تأكَّد من أنّ التطبيق يعمل على النحو المتوقّع بدون أي مشاكل. إذا أكملت الدرس التطبيقي حول الترميز 8، سيعرض التطبيق أهم الزوّار بالإضافة إلى أحدث الزيارات (كما هو موضّح أدناه). في أسفل الصفحة، يظهر إشارة إلى المهام الأقدم التي سيتم حذفها.

4. تعديل الإعدادات
requirements.txt
إنّ requirements.txt الجديد هو نفسه تقريبًا requirements.txt الخاص بالوحدة التدريبية 8، مع تغيير واحد كبير فقط: استبدِل google-cloud-ndb بـ google-cloud-datastore. أجرِ هذا التغيير ليظهر ملف requirements.txt على النحو التالي:
flask
google-cloud-datastore
google-cloud-tasks
لا يحتوي ملف requirements.txt هذا على أي أرقام إصدارات، ما يعني أنّه تم اختيار أحدث الإصدارات. في حال حدوث أي حالات عدم توافق، يُعدّ استخدام أرقام الإصدارات لتحديد الإصدارات المتوافقة مع التطبيق من الممارسات الشائعة.
app.yaml
لا يتيح وقت تشغيل الجيل الثاني من App Engine مكتبات مدمجة تابعة لجهات خارجية كما هو الحال في الإصدار 2.x، كما لا يتيح نسخ المكتبات غير المدمجة. الشرط الوحيد لحِزم الجهات الخارجية هو إدراجها في requirements.txt. نتيجةً لذلك، يمكن حذف قسم libraries بالكامل من app.yaml.
من التغييرات الأخرى أنّ وقت تشغيل Python 3 يتطلّب استخدام أُطر عمل الويب التي تنفّذ التوجيه الخاص بها. نتيجةً لذلك، يجب تغيير جميع معالِجات النصوص البرمجية إلى auto. ومع ذلك، بما أنّه يجب تغيير جميع المسارات إلى auto، ولا يتم عرض أي ملفات ثابتة من نموذج التطبيق هذا، فمن غير المهم توفُّر أي معالِجات any، لذا عليك إزالة قسم handlers بأكمله أيضًا.
الشيء الوحيد المطلوب في app.yaml هو ضبط وقت التشغيل على إصدار متوافق من Python 3، مثل 3.10. أجرِ هذا التغيير ليكون app.yaml الجديد المختصر هو هذا السطر الفردي فقط:
runtime: python310
حذف ملف appengine_config.py والمجلد lib
تعديل استخدام حِزم الجهات الخارجية في أوقات تشغيل الجيل التالي من App Engine:
- المكتبات المضمّنة هي تلك التي تحقّقت منها Google وأتاحتها على خوادم App Engine، وذلك على الأرجح لأنّها تحتوي على رمز C/C++ الذي لا يُسمح للمطوّرين بنشره على السحابة الإلكترونية، ولم تعُد هذه المكتبات متاحة في أوقات التشغيل من الجيل الثاني.
- لم يعُد من الضروري نسخ المكتبات غير المضمّنة (المعروفة أحيانًا باسم "البيع" أو "التجميع الذاتي") في أوقات التشغيل من الجيل الثاني. بدلاً من ذلك، يجب إدراجها في
requirements.txtحيث يثبّتها نظام التصميم تلقائيًا نيابةً عنك في وقت النشر.
نتيجةً لهذه التغييرات في إدارة حِزم الجهات الخارجية، لن تحتاج إلى الملف appengine_config.py أو المجلد lib، لذا احذفهما. في أوقات التشغيل من الجيل الثاني، يثبِّت App Engine تلقائيًا حِزم الجهات الخارجية المُدرَجة في requirements.txt. تلخيص:
- عدم تضمين مكتبات تابعة لجهات خارجية أو نسخها أو تجميعها ذاتيًا، ويجب إدراجها في
requirements.txt - لا يمكن وضع
pip installفي مجلدlib، ما يعني عدم إمكانية إنشاء مجلدlibعلى الإطلاق - لا تتضمّن بطاقة البيانات مكتبات مضمّنة تابعة لجهات خارجية (وبالتالي لا يوجد قسم
libraries) فيapp.yaml، بل يجب إدراجها فيrequirements.txt - عدم توفّر أي مكتبات تابعة لجهات خارجية يمكن الرجوع إليها من تطبيقك يعني عدم توفّر ملف
appengine_config.py
إنّ إدراج جميع مكتبات الجهات الخارجية المطلوبة في requirements.txt هو الشرط الوحيد الذي يجب أن يستوفيه المطوّر.
5- تعديل ملفات التطبيق
لا يوجد سوى ملف تطبيق واحد، وهو main.py، لذا تؤثر جميع التغييرات في هذا القسم في هذا الملف فقط. في ما يلي توضيح "للاختلافات" بشأن التغييرات الإجمالية التي يجب إجراؤها لإعادة تصميم الرمز الحالي في التطبيق الجديد. وليس مطلوبًا من القرّاء قراءة الرمز سطرًا بسطر، لأنّ الغرض منه هو ببساطة الحصول على نظرة عامة مصوّرة على ما هو مطلوب في عملية إعادة التصميم هذه (ولكن يمكنك فتحها في علامة تبويب جديدة أو تنزيلها وتكبيرها إذا أردت).

تعديل عمليات الاستيراد والإعداد
يستخدم قسم الاستيراد في main.py للوحدة التدريبية 8 Cloud NDB وCloud Tasks، ويجب أن يبدو على النحو التالي:
قبل:
from datetime import datetime
import json
import logging
import time
from flask import Flask, render_template, request
import google.auth
from google.cloud import ndb, tasks
app = Flask(__name__)
ds_client = ndb.Client()
ts_client = tasks.CloudTasksClient()
تم تبسيط عملية التسجيل وتحسينها في أوقات التشغيل من الجيل الثاني، مثل Python 3:
- للحصول على تجربة تسجيل شاملة، استخدِم Cloud Logging.
- لتسجيل الدخول ببساطة، ما عليك سوى إرسال طلب إلى
stdout(أوstderr) عبرprint() - لا حاجة إلى استخدام وحدة
loggingفي Python (لذا أزِلها)
نتيجةً لذلك، احذف عملية استيراد logging واستبدِل google.cloud.ndb بـ google.cloud.datastore. وبالمثل، غيِّر ds_client للإشارة إلى برنامج Datastore بدلاً من برنامج NDB. بعد إجراء هذه التغييرات، سيظهر الجزء العلوي من تطبيقك الجديد على النحو التالي:
بعد:
from datetime import datetime
import json
import time
from flask import Flask, render_template, request
import google.auth
from google.cloud import datastore, tasks
app = Flask(__name__)
ds_client = datastore.Client()
ts_client = tasks.CloudTasksClient()
الترحيل إلى Cloud Datastore
حان الوقت الآن لاستبدال استخدام مكتبة برامج NDB بـ Datastore. يتطلّب كلّ من App Engine NDB وCloud NDB نموذج بيانات (فئة)، وهو Visit لهذا التطبيق. تعمل الدالة store_visit() بالطريقة نفسها في جميع وحدات نقل البيانات الأخرى: فهي تسجّل زيارة من خلال إنشاء سجلّ Visit جديد، وحفظ عنوان IP الخاص بالعميل الزائر وبرنامج وكيل المستخدم (نوع المتصفّح).
قبل:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
ومع ذلك، لا تستخدم Cloud Datastore فئة نموذج بيانات، لذا احذف الفئة. بالإضافة إلى ذلك، لا تنشئ Cloud Datastore طابعًا زمنيًا تلقائيًا عند إنشاء السجلات، ما يتطلّب منك إجراء ذلك يدويًا باستخدام طلب datetime.now().
بدون فئة البيانات، يجب أن يبدو store_visit() المعدَّل على النحو التالي:
بعد:
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
entity = datastore.Entity(key=ds_client.key('Visit'))
entity.update({
'timestamp': datetime.now(),
'visitor': '{}: {}'.format(remote_addr, user_agent),
})
ds_client.put(entity)
وظيفة المفتاح هي fetch_visits(). لا ينفّذ هذا الإجراء طلب البحث الأصلي عن آخر Visits فحسب، بل يسترد أيضًا الطابع الزمني لآخر Visit معروض وينشئ مهمة الإرسال التي تستدعي /trim (وبالتالي trim()) لحذف Visits القديمة بشكل مجمّع. في ما يلي مثال على استخدام Cloud NDB:
قبل:
def fetch_visits(limit):
'get most recent visits & add task to delete older visits'
with ds_client.context():
data = Visit.query().order(-Visit.timestamp).fetch(limit)
oldest = time.mktime(data[-1].timestamp.timetuple())
oldest_str = time.ctime(oldest)
logging.info('Delete entities older than %s' % oldest_str)
task = {
'app_engine_http_request': {
'relative_uri': '/trim',
'body': json.dumps({'oldest': oldest}).encode(),
'headers': {
'Content-Type': 'application/json',
},
}
}
ts_client.create_task(parent=QUEUE_PATH, task=task)
return (v.to_dict() for v in data), oldest_str
التغييرات الأساسية:
- استبدِل طلب البحث في Cloud NDB بطلب البحث المكافئ في Cloud Datastore، إذ تختلف أنماط طلبات البحث قليلاً.
- لا تتطلّب Datastore استخدام أداة إدارة السياق ولا تفرض عليك استخراج بياناتها (باستخدام
to_dict()) كما تفعل Cloud NDB. - استبدال مكالمات التسجيل بـ
print()
بعد إجراء هذه التغييرات، سيبدو fetch_visits() على النحو التالي:
بعد:
def fetch_visits(limit):
'get most recent visits & add task to delete older visits'
query = ds_client.query(kind='Visit')
query.order = ['-timestamp']
visits = list(query.fetch(limit=limit))
oldest = time.mktime(visits[-1]['timestamp'].timetuple())
oldest_str = time.ctime(oldest)
print('Delete entities older than %s' % oldest_str)
task = {
'app_engine_http_request': {
'relative_uri': '/trim',
'body': json.dumps({'oldest': oldest}).encode(),
'headers': {
'Content-Type': 'application/json',
},
}
}
ts_client.create_task(parent=QUEUE_PATH, task=task)
return visits, oldest_str
عادةً ما يكون هذا كل ما هو ضروري. مع ذلك، هناك مشكلة رئيسية واحدة.
(اختياري) إنشاء قائمة انتظار جديدة (لإرسال الإشعارات)
في الوحدة 7، أضفنا استخدام App Engine taskqueue إلى تطبيق الوحدة 1 الحالي. إحدى المزايا الرئيسية لتوفُّر مهام الدفع كميزة قديمة في App Engine هي إنشاء قائمة انتظار "تلقائية" تلقائيًا. عندما تم نقل هذا التطبيق إلى Cloud Tasks في الوحدة 8، كانت قائمة الانتظار التلقائية هذه متوفّرة، لذا لم يكن علينا القلق بشأنها في ذلك الوقت. سيتغيّر ذلك في الوحدة التدريبية 9.
أحد الجوانب المهمة التي يجب مراعاتها هو أنّ تطبيق App Engine الجديد لم يعُد يستخدم خدمات App Engine، وبالتالي لم يعُد بإمكانك افتراض أنّ App Engine ينشئ تلقائيًا قائمة انتظار المهام في منتج مختلف (Cloud Tasks). على النحو المكتوب، سيتعذّر إنشاء مهمة في fetch_visits() (لصف انتظار غير موجود). يجب توفير دالة جديدة للتحقّق مما إذا كان هناك قائمة انتظار ("تلقائية")، وإنشاء واحدة إذا لم تكن متوفّرة.
استدعِ هذه الدالة _create_queue_if()، وأضِفها إلى تطبيقك فوق fetch_visits() مباشرةً لأنّ هذا هو المكان الذي يتم استدعاؤها فيه. نص الدالة المطلوب إضافتها:
def _create_queue_if():
'app-internal function creating default queue if it does not exist'
try:
ts_client.get_queue(name=QUEUE_PATH)
except Exception as e:
if 'does not exist' in str(e):
ts_client.create_queue(parent=PATH_PREFIX,
queue={'name': QUEUE_PATH})
return True
تتطلّب الدالة create_queue() في Cloud Tasks مسار الاسم الكامل للصف باستثناء اسم الصف. لتبسيط الأمر، أنشئ متغيرًا آخر PATH_PREFIX يمثّل QUEUE_PATH ناقص اسم قائمة الانتظار (QUEUE_PATH.rsplit('/', 2)[0]). أضِف تعريف هذا المتغير بالقرب من أعلى الصفحة حتى تبدو مجموعة الرموز التي تتضمّن جميع عمليات تعيين الثوابت على النحو التالي:
_, PROJECT_ID = google.auth.default()
REGION_ID = 'REGION_ID' # replace w/your own
QUEUE_NAME = 'default' # replace w/your own
QUEUE_PATH = ts_client.queue_path(PROJECT_ID, REGION_ID, QUEUE_NAME)
PATH_PREFIX = QUEUE_PATH.rsplit('/', 2)[0]
عدِّل الآن السطر الأخير في fetch_visits() لاستخدام _create_queue_if()، مع إنشاء قائمة الانتظار أولاً إذا لزم الأمر، ثم إنشاء المهمة بعد ذلك:
if _create_queue_if():
ts_client.create_task(parent=QUEUE_PATH, task=task)
return visits, oldest_str
يجب أن يبدو كل من _create_queue_if() وfetch_visits() الآن على النحو التالي في المجمل:
def _create_queue_if():
'app-internal function creating default queue if it does not exist'
try:
ts_client.get_queue(name=QUEUE_PATH)
except Exception as e:
if 'does not exist' in str(e):
ts_client.create_queue(parent=PATH_PREFIX,
queue={'name': QUEUE_PATH})
return True
def fetch_visits(limit):
'get most recent visits & add task to delete older visits'
query = ds_client.query(kind='Visit')
query.order = ['-timestamp']
visits = list(query.fetch(limit=limit))
oldest = time.mktime(visits[-1]['timestamp'].timetuple())
oldest_str = time.ctime(oldest)
print('Delete entities older than %s' % oldest_str)
task = {
'app_engine_http_request': {
'relative_uri': '/trim',
'body': json.dumps({'oldest': oldest}).encode(),
'headers': {
'Content-Type': 'application/json',
},
}
}
if _create_queue_if():
ts_client.create_task(parent=QUEUE_PATH, task=task)
return visits, oldest_str
باستثناء الحاجة إلى إضافة هذا الرمز الإضافي، يظلّ باقي رمز Cloud Tasks كما هو في الوحدة التدريبية 8. آخر جزء من الرمز الذي يجب إلقاء نظرة عليه هو معالج المهام.
معالج تعديل (إرسال) المهمة
في معالج المهام، trim()، يستعلم رمز Cloud NDB عن الزيارات الأقدم من أقدم زيارة معروضة. يستخدم طلب بحث يتضمّن المفاتيح فقط لتسريع العملية، فلماذا يتم جلب جميع البيانات إذا كنت بحاجة إلى معرّفات الزيارات فقط؟ بعد الحصول على جميع أرقام تعريف الزيارات، احذفها كلها دفعةً واحدة باستخدام الدالة delete_multi() في Cloud NDB.
قبل:
@app.route('/trim', methods=['POST'])
def trim():
'(push) task queue handler to delete oldest visits'
oldest = float(request.get_json().get('oldest'))
with ds_client.context():
keys = Visit.query(
Visit.timestamp < datetime.fromtimestamp(oldest)
).fetch(keys_only=True)
nkeys = len(keys)
if nkeys:
logging.info('Deleting %d entities: %s' % (
nkeys, ', '.join(str(k.id()) for k in keys)))
ndb.delete_multi(keys)
else:
logging.info(
'No entities older than: %s' % time.ctime(oldest))
return '' # need to return SOME string w/200
كما هو الحال مع fetch_visits()، يتضمّن الجزء الأكبر من التغييرات استبدال رمز Cloud NDB بـ Cloud Datastore، وتعديل أنماط طلبات البحث، وإزالة استخدام مدير السياق، وتغيير طلبات التسجيل إلى print().
بعد:
@app.route('/trim', methods=['POST'])
def trim():
'(push) task queue handler to delete oldest visits'
oldest = float(request.get_json().get('oldest'))
query = ds_client.query(kind='Visit')
query.add_filter('timestamp', '<', datetime.fromtimestamp(oldest))
query.keys_only()
keys = list(visit.key for visit in query.fetch())
nkeys = len(keys)
if nkeys:
print('Deleting %d entities: %s' % (
nkeys, ', '.join(str(k.id) for k in keys)))
ds_client.delete_multi(keys)
else:
print('No entities older than: %s' % time.ctime(oldest))
return '' # need to return SOME string w/200
لم يتم إجراء أي تغييرات على معالج التطبيق الرئيسي root().
نقل إلى Python 3
تم تصميم نموذج التطبيق هذا ليعمل على الإصدارَين 2 و3 من Python. تمت تغطية أي تغييرات خاصة بلغة Python 3 في وقت سابق في الأقسام ذات الصلة من هذا البرنامج التعليمي. لا يلزم اتّخاذ أي خطوات إضافية أو استخدام مكتبات توافق.
تعديل على Cloud Tasks
الإصدار النهائي من مكتبة برامج Cloud Tasks المتوافق مع Python 2 هو 1.5.0. في وقت كتابة هذا المستند، كان أحدث إصدار من مكتبة البرامج للغة Python 3 متوافقًا تمامًا مع هذا الإصدار، وبالتالي لا يلزم إجراء أي تحديثات أخرى.
تعديل نموذج HTML
لا يلزم إجراء أي تغييرات في ملف نموذج HTML، templates/index.html، أيضًا، وبالتالي يختتم هذا جميع التغييرات اللازمة للوصول إلى تطبيق الوحدة 9.
6. الملخّص/التنظيف
نشر التطبيق والتحقّق منه
بعد إكمال تحديثات الرمز البرمجي، وخاصةً النقل إلى Python 3، يمكنك نشر تطبيقك باستخدام gcloud app deploy. يجب أن تكون المخرجات مماثلة لتلك الناتجة عن التطبيقات من الوحدتين 7 و8، باستثناء أنّك نقلت إذن الوصول إلى قاعدة البيانات إلى مكتبة برامج Cloud Datastore، وأنّك أجريت ترقية إلى الإصدار 3 من Python:

تُكمل هذه الخطوة الدرس العملي. ندعوك إلى مقارنة الرمز بما هو موجود في المجلد 9. تهانينا!
تَنظيم
للجمهور العام
إذا انتهيت من استخدام التطبيق في الوقت الحالي، ننصحك بإيقاف تطبيق App Engine لتجنُّب تحمّل رسوم. ومع ذلك، إذا أردت إجراء المزيد من الاختبارات أو التجارب، تتضمّن منصة App Engine حصّة مجانية، وبالتالي لن يتم تحصيل رسوم منك طالما أنّك لا تتجاوز مستوى الاستخدام هذا. هذا السعر خاص بالحوسبة، ولكن قد تكون هناك رسوم أيضًا مقابل خدمات App Engine ذات الصلة، لذا يُرجى الاطّلاع على صفحة الأسعار للحصول على مزيد من المعلومات. إذا كان نقل البيانات هذا يتضمّن خدمات سحابية أخرى، يتم إصدار فواتير لها بشكل منفصل. في كلتا الحالتين، إذا كان ذلك منطبقًا، راجِع القسم "خاص بهذا الدرس التطبيقي حول الترميز" أدناه.
للتوضيح، يؤدي النشر على منصة حوسبة بدون خادم في Google Cloud، مثل App Engine، إلى تكبُّد تكاليف بسيطة للإنشاء والتخزين. تتضمّن Cloud Build حصة مجانية خاصة بها، وكذلك Cloud Storage. ويؤدي تخزين هذه الصورة إلى استهلاك جزء من هذه الحصة. ومع ذلك، قد تكون مقيمًا في منطقة لا تتوفّر فيها هذه الطبقة المجانية، لذا عليك الانتباه إلى استخدامك لمساحة التخزين لتقليل التكاليف المحتملة. تتضمّن "المجلدات" المحدّدة في Cloud Storage التي يجب مراجعتها ما يلي:
console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/imagesconsole.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com- تعتمد روابط مساحة التخزين أعلاه على
PROJECT_IDو *LOC*، على سبيل المثال، "us" إذا كان تطبيقك مستضافًا في الولايات المتحدة.
من ناحية أخرى، إذا كنت لن تواصل استخدام هذا التطبيق أو غيره من الدروس التعليمية البرمجية المتعلقة بنقل البيانات وأردت حذف كل شيء تمامًا، عليك إيقاف مشروعك.
خاص بهذا الدرس التطبيقي حول الترميز
الخدمات المدرَجة أدناه خاصة بهذا الدرس التطبيقي حول الترميز. يُرجى الرجوع إلى مستندات كل منتج للحصول على مزيد من المعلومات:
- تتوفّر في Cloud Tasks فئة مجانية، ويمكنك الاطّلاع على صفحة الأسعار للحصول على مزيد من التفاصيل.
- يتم توفير خدمة App Engine Datastore من خلال Cloud Datastore (Cloud Firestore في وضع Datastore) الذي يتضمّن أيضًا طبقة مجانية. يمكنك الاطّلاع على صفحة الأسعار للحصول على مزيد من المعلومات.
الخطوات التالية
بهذا نكون قد انتهينا من عملية نقل المهام من مهام الدفع في "قائمة انتظار المهام" في App Engine إلى Cloud Tasks. يتم أيضًا تناول عملية النقل الاختيارية من Cloud NDB إلى Cloud Datastore بشكل منفصل (بدون Task Queue أو Cloud Tasks) في الوحدة 3. بالإضافة إلى الوحدة 3، هناك وحدات نقل بيانات أخرى تركّز على التوقّف عن استخدام الخدمات المجمّعة القديمة في App Engine، وتشمل ما يلي:
- الوحدة 2: نقل البيانات من App Engine NDB إلى Cloud NDB
- الوحدة 3: نقل البيانات من Cloud NDB إلى Cloud Datastore
- الوحدتان 12 و13: نقل البيانات من App Engine Memcache إلى Cloud Memorystore
- الوحدتان 15 و16: نقل البيانات من App Engine Blobstore إلى Cloud Storage
- الوحدتان 18 و19: من App Engine Task Queue (مهام السحب) إلى Cloud Pub/Sub
لم تعُد App Engine هي المنصة الوحيدة بدون خادم في Google Cloud. إذا كان لديك تطبيق صغير على App Engine أو تطبيق بوظائف محدودة وتريد تحويله إلى خدمة مصغّرة مستقلة، أو إذا كنت تريد تقسيم تطبيق متكامل إلى عدة مكونات قابلة لإعادة الاستخدام، ستكون هذه أسبابًا وجيهة للنقل إلى Cloud Functions. إذا أصبحت عملية إنشاء الحاويات جزءًا من سير عمل تطوير التطبيقات، خاصةً إذا كانت تتألف من مسار CI/CD (التكامل المستمر/التسليم المتواصل أو النشر المستمر)، ننصحك بالانتقال إلى Cloud Run. تتناول الوحدات التدريبية التالية هذه السيناريوهات:
- نقل البيانات من App Engine إلى Cloud Functions: يمكنك الاطّلاع على الوحدة 11
- الانتقال من App Engine إلى Cloud Run: يمكنك الاطّلاع على الوحدة 4 لتضمين تطبيقك في حاوية باستخدام Docker، أو الوحدة 5 لإجراء ذلك بدون حاويات أو معرفة بـ Docker أو
Dockerfile
إنّ الانتقال إلى منصة أخرى بلا خادم هو أمر اختياري، وننصحك بمراجعة أفضل الخيارات لتطبيقاتك وحالات الاستخدام قبل إجراء أي تغييرات.
بغض النظر عن وحدة النقل التي ستختارها، يمكنك الوصول إلى كل محتوى Serverless Migration Station (الدروس البرمجية والفيديوهات ورمز المصدر [عند توفّره]) من خلال مستودع المصدر المفتوح. يوفّر مستودع README أيضًا إرشادات حول عمليات نقل البيانات التي يجب أخذها في الاعتبار وأي "ترتيب" ذي صلة لوحدات نقل البيانات.
7. مراجع إضافية
مشاكل/ملاحظات بشأن الدروس التطبيقية حول الترميز
إذا واجهت أي مشاكل في هذا الدرس العملي، يُرجى البحث عن مشكلتك أولاً قبل إرسالها. روابط للبحث عن مشاكل جديدة وإنشائها:
مراجع لنقل البيانات
يمكنك العثور على روابط لمجلدات المستودع للوحدة التدريبية 8 (البداية) والوحدة التدريبية 9 (النهاية) في الجدول أدناه. يمكن أيضًا الوصول إليها من مستودع جميع عمليات نقل Codelab في App Engine الذي يمكنك استنساخه أو تنزيل ملف ZIP منه.
Codelab | Python 2 | Python 3 |
(غير متوفر) | ||
الوحدة 9 | (غير متوفر) |
مراجع متوفرة على الإنترنت
في ما يلي مراجع على الإنترنت قد تكون ذات صلة بهذا البرنامج التعليمي:
App Engine
- مستندات App Engine
- وقت تشغيل Python 2 App Engine (البيئة العادية)
- وقت تشغيل Python 3 App Engine (البيئة العادية)
- الاختلافات بين أوقات تشغيل Python 2 و3 في App Engine (البيئة العادية)
- دليل نقل البيانات من Python 2 إلى Python 3 في App Engine (البيئة العادية)
- معلومات الأسعار والحصص في App Engine
Cloud NDB
Cloud Datastore
Cloud Tasks
معلومات أخرى حول السحابة الإلكترونية
- Python على Google Cloud Platform
- مكتبات برامج Python في Google Cloud
- فئة "دائمًا مجانية" في Google Cloud
- Google Cloud SDK (أداة سطر الأوامر
gcloud) - جميع مستندات Google Cloud
الترخيص
يخضع هذا العمل لترخيص المشاع الإبداعي مع نسب العمل إلى مؤلفه 2.0 Generic License.