1. نظرة عامة
تهدف سلسلة الدروس التطبيقية حول الترميز في Serverless Migration Station (دروس تعليمية عملية ذاتية التوجيه) والفيديوهات ذات الصلة إلى مساعدة مطوّري الحوسبة بدون خادم في Google Cloud على تحديث تطبيقاتهم من خلال توجيههم خلال عملية نقل واحدة أو أكثر، وبشكل أساسي الابتعاد عن الخدمات القديمة. يؤدي ذلك إلى جعل تطبيقاتك أكثر قابلية للنقل ويمنحك المزيد من الخيارات والمرونة، ما يتيح لك التكامل مع مجموعة أوسع من منتجات Cloud والوصول إليها والترقية بسهولة أكبر إلى إصدارات اللغات الأحدث. على الرغم من أنّ هذه السلسلة تركّز في البداية على مستخدمي Cloud الأوائل، وبشكل أساسي مطوّري App Engine (البيئة العادية)، إلا أنّها واسعة بما يكفي لتشمل منصات أخرى بدون خادم، مثل Cloud Functions وCloud Run، أو غيرها إن أمكن.
يوضّح لك هذا الدرس التطبيقي حول الترميز كيفية نقل البيانات من App Engine Blobstore إلى Cloud Storage. هناك أيضًا عمليات نقل ضمنية من:
- إطار عمل الويب
webapp2إلى Flask (مشمول في الوحدة 1) - App Engine NDB إلى Cloud NDB للوصول إلى Datastore (مشمول في الوحدة 2)
- من Python 2 إلى Python 3 (التطبيق الذي تم نقله متوافق مع Python 2 وPython 3)
يُرجى الرجوع إلى أي وحدات نقل ذات صلة للحصول على مزيد من المعلومات المفصّلة.
ستتعرَّف على كيفية إجراء ما يلي:
- إضافة استخدام واجهة برمجة التطبيقات/مكتبة App Engine Blobstore
- تخزين عمليات تحميل المستخدمين في خدمة Blobstore
- الاستعداد للخطوة التالية لنقل البيانات إلى Cloud Storage
المتطلبات
- مشروع على Google Cloud Platform يتضمّن حساب فوترة نشطًا على Google Cloud Platform
- مهارات أساسية في لغة Python
- معرفة عملية بأوامر Linux الشائعة
- معرفة أساسية بتطوير تطبيقات App Engine ونشرها
- تطبيق App Engine من الوحدة 15 يعمل بشكل صحيح: أكمِل الدرس التطبيقي حول الترميز للوحدة 15 (يُنصح بذلك) أو انسخ تطبيق الوحدة 15 من المستودع
استطلاع
كيف ستستخدم هذا البرنامج التعليمي؟
كيف تقيّم تجربتك مع Python؟
ما هو تقييمك لتجربة استخدام خدمات Google Cloud؟
2. خلفية
يبدأ هذا الدرس التطبيقي حول الترميز بنموذج التطبيق من الوحدة 15 ويوضّح كيفية نقل البيانات من Blobstore (وNDB) إلى Cloud Storage (وCloud NDB). تتضمّن عملية نقل البيانات استبدال الاعتماديات في الخدمات المجمّعة القديمة في App Engine، ما يتيح لك نقل تطبيقاتك إلى منصة أخرى للحوسبة بدون خادم في Cloud أو منصة استضافة أخرى إذا أردت ذلك.
تتطلّب عملية نقل البيانات هذه جهدًا أكبر مقارنةً بعمليات النقل الأخرى في هذه السلسلة. يعتمد Blobstore على إطار عمل تطبيق الويب الأصلي، ولهذا السبب يستخدم نموذج التطبيق إطار عمل webapp2 بدلاً من Flask. يتناول هذا البرنامج التعليمي عمليات نقل البيانات إلى Cloud Storage وCloud NDB وFlask وPython 3.
لا يزال التطبيق يسجّل "زيارات" المستخدمين النهائيين ويعرض آخر عشر زيارات، ولكن الدرس التطبيقي السابق حول الترميز (الوحدة 15) أضاف وظائف جديدة لاستيعاب استخدام Blobstore: يطلب التطبيق من المستخدمين النهائيين تحميل عنصر (ملف) يتوافق مع "زيارتهم". ويمكن للمستخدمين إجراء ذلك أو اختيار "التخطّي" لإيقاف الميزة. بغض النظر عن قرار المستخدم، تعرض الصفحة التالية النتيجة نفسها التي عرضتها الإصدارات السابقة من هذا التطبيق، أي أحدث الزيارات. هناك ميزة إضافية أخرى تتمثل في أن الزيارات التي تحتوي على عناصر مقابلة تتميز برابط "عرض" لعرض عنصر الزيارة. ينفّذ هذا الدرس التطبيقي حول الترميز عمليات نقل البيانات المذكورة سابقًا مع الحفاظ على الوظائف الموضّحة.
3- الإعداد/العمل المسبق
قبل الانتقال إلى الجزء الرئيسي من البرنامج التعليمي، لنبدأ بإعداد مشروعنا والحصول على الرمز، ثم نشر التطبيق الأساسي لنتأكّد من أنّنا بدأنا برمز يعمل بشكل صحيح.
1. إعداد المشروع
إذا سبق لك نشر تطبيق الوحدة 15، ننصحك بإعادة استخدام المشروع (والرمز) نفسه. يمكنك بدلاً من ذلك إنشاء مشروع جديد تمامًا أو إعادة استخدام مشروع حالي آخر. تأكَّد من أنّ المشروع يتضمّن حساب فوترة نشطًا وأنّ App Engine مفعَّل.
2. الحصول على نموذج تطبيق أساسي
من المتطلبات الأساسية لهذا الدرس التطبيقي حول الترميز أن يكون لديك نموذج تطبيق الوحدة 15 يعمل. إذا لم يكن لديك، يمكنك الحصول عليه من مجلد "START" (البدء) في الوحدة 15 (الرابط أدناه). يرشدك هذا الدرس التطبيقي حول الترميز خلال كل خطوة، وينتهي برمز يشبه ما هو موجود في مجلد "FINISH" في الوحدة 16.
- START: Module 15 folder (Python 2)
- إنهاء: مجلد الوحدة 16 (Python 2)
- المستودع بالكامل (لاستنساخ ملف ZIP أو تنزيله)
يجب أن يبدو دليل ملفات START للوحدة 15 على النحو التالي:
$ ls README.md app.yaml main-gcs.py main.py templates
ملف main-gcs.py هو نسخة بديلة من ملف main.py من الوحدة 15، ويسمح باختيار حزمة Cloud Storage مختلفة عن عنوان URL التلقائي المخصّص للتطبيق استنادًا إلى رقم تعريف المشروع: PROJECT_ID.appspot.com. لا يؤدي هذا الملف أي دور في هذا الدرس التطبيقي حول الترميز (الوحدة 16) بخلاف إمكانية تطبيق أساليب نقل مشابهة على هذا الملف إذا رغبت في ذلك.
3- (إعادة) نشر التطبيق الأساسي
خطوات العمل التمهيدي المتبقية التي يجب تنفيذها الآن:
- التعرّف مجددًا على أداة سطر الأوامر
gcloud - إعادة نشر نموذج التطبيق باستخدام
gcloud app deploy - تأكَّد من أنّ التطبيق يعمل على App Engine بدون مشاكل
بعد تنفيذ هذه الخطوات بنجاح والتأكّد من عمل تطبيق الوحدة 15، تعرض الصفحة الأولى للمستخدمين نموذجًا يطلب منهم تحميل ملف بيانات الزيارة، بالإضافة إلى خيار "التخطّي" لعدم المشاركة:

بعد أن يحمّل المستخدمون ملفًا أو يتخطّون هذه الخطوة، يعرض التطبيق صفحة "أحدث الزيارات" المألوفة:

ستتضمّن الزيارات التي تحتوي على عنصر رابط "عرض" على يسار الطابع الزمني للزيارة لعرض العنصر (أو تنزيله). بعد التأكّد من وظائف التطبيق، ستكون جاهزًا لنقل البيانات من خدمات App Engine القديمة (webapp2 وNDB وBlobstore) إلى البدائل المعاصرة (Flask وCloud NDB وCloud Storage).
4. تعديل ملفات الإعداد
هناك ثلاثة ملفات إعدادات للإصدار المحدَّث من تطبيقنا، والمهام المطلوبة هي:
- تحديث المكتبات الخارجية المضمّنة المطلوبة في
app.yaml، بالإضافة إلى إتاحة إمكانية نقل البيانات إلى Python 3 - أضِف
requirements.txtيحدّد جميع المكتبات المطلوبة غير المضمّنة - أضِف
appengine_config.pyلكي يتوافق التطبيق مع مكتبات الجهات الخارجية المضمّنة وغير المضمّنة
app.yaml
عدِّل ملف app.yaml من خلال تعديل قسم libraries. إزالة jinja2 وإضافة grpcio وsetuptools وssl اختَر أحدث إصدار متاح للمكتبات الثلاث. أضِف أيضًا توجيه Python 3 runtime، ولكن مع الإشارة إلى أسطر رموز باعتبارها تعليقًا. عند الانتهاء، من المفترض أن تظهر على النحو التالي (إذا اخترت Python 3.9):
قبل:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
بعد:
#runtime: python39
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: grpcio
version: latest
- name: setuptools
version: latest
- name: ssl
version: latest
تتعامل التغييرات بشكل أساسي مع مكتبات Python 2 المضمّنة المتاحة على خوادم App Engine (لذلك ليس عليك تجميعها ذاتيًا). لقد أزلنا Jinja2 لأنّه يأتي مع Flask، والذي سنضيفه إلى reqs.txt. عند استخدام مكتبات برامج Google Cloud، مثل تلك الخاصة بـ Cloud NDB وCloud Storage، تكون هناك حاجة إلى grpcio وsetuptools. أخيرًا، يتطلب Cloud Storage نفسه مكتبة ssl. توجيه وقت التشغيل الذي تمت الإشارة إلى أسطر رموزه باعتبارها تعليقًا في الأعلى مخصّص للحالات التي تكون فيها مستعدًا لنقل هذا التطبيق إلى Python 3. وسنتناول هذا الموضوع في نهاية هذا الدليل التعليمي.
requirements.txt
أضِف ملف requirements.txt يتطلّب إطار عمل Flask ومكتبات برامج Cloud NDB وCloud Storage، علمًا بأنّ أيًا منها ليس مدمجًا. أنشِئ الملف بالمحتوى التالي:
flask
google-cloud-ndb
google-cloud-storage
تتطلّب بيئة وقت تشغيل Python 2 في App Engine تجميعًا ذاتيًا لمكتبات الجهات الخارجية غير المضمّنة، لذا نفِّذ الأمر التالي لتثبيت هذه المكتبات في مجلد lib:
pip install -t lib -r requirements.txt
إذا كان لديك كل من Python 2 و3 على جهاز التطوير، قد تحتاج إلى استخدام الأمر pip2 لضمان الحصول على إصدارات Python 2 من هذه المكتبات. بعد الترقية إلى Python 3، لن تحتاج إلى تجميع الحِزم بنفسك.
appengine_config.py
أضِف ملف appengine_config.py يتوافق مع المكتبات الخارجية المضمّنة وغير المضمّنة. أنشِئ الملف بالمحتوى التالي:
import pkg_resources
from google.appengine.ext import vendor
# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)
يُفترض أن تكون الخطوات التي أكملتها للتو مشابهة أو مطابقة للخطوات المدرَجة في قسم تثبيت المكتبات لتطبيقات Python 2 في مستندات App Engine، وبشكل أكثر تحديدًا، يجب أن يتطابق محتوى appengine_config.py مع ما هو وارد في الخطوة 5 هناك.
اكتمل العمل على ملفات الإعداد، لذا لننتقل إلى التطبيق.
5- تعديل ملفات التطبيق
عمليات الاستيراد
تشمل المجموعة الأولى من التغييرات في "main.py" استبدال كل العناصر التي سيتم تغييرها. في ما يلي التغييرات التي سيتم إجراؤها:
- تم استبدال
webapp2بـ Flask - بدلاً من استخدام Jinja2 من
webapp2_extras، استخدِم Jinja2 المضمَّن في Flask - تم استبدال App Engine Blobstore وNDB بـ Cloud NDB وCloud Storage
- يتم استبدال معالِجات Blobstore في
webappبمجموعة من وحدة مكتبةioالعادية وFlask وأدواتwerkzeugالمساعدة - تلقائيًا، يكتب Blobstore في حزمة Cloud Storage تحمل اسم عنوان URL لتطبيقك (
PROJECT_ID.appspot.com). وبما أنّنا ننقل البيانات إلى مكتبة برامج Cloud Storage، يتم استخدامgoogle.authللحصول على رقم تعريف المشروع من أجل تحديد اسم الحزمة نفسه بالضبط. (يمكنك تغيير اسم الحزمة لأنّه لم يعُد مضمّنًا في الرمز البرمجي).
قبل:
import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers
نفِّذ التغييرات الواردة في القائمة أعلاه من خلال استبدال قسم الاستيراد الحالي في main.py بمقتطف الرمز أدناه.
بعد:
import io
from flask import (Flask, abort, redirect, render_template,
request, send_file, url_for)
from werkzeug.utils import secure_filename
import google.auth
from google.cloud import exceptions, ndb, storage
التهيئة ودعم Jinja2 غير الضروري
مجموعة الرموز التالية التي يجب استبدالها هي BaseHandler التي تحدّد استخدام Jinja2 من webapp2_extras. هذا غير ضروري لأن Jinja2 يأتي مع Flask وهو محرك القوالب الافتراضي الخاص به، لذا قم بإزالته.
في الوحدة 16، يتم إنشاء كائنات لم تكن متوفّرة في التطبيق القديم، ويشمل ذلك إعداد تطبيق Flask وإنشاء عملاء واجهة برمجة التطبيقات لكل من Cloud NDB وCloud Storage. أخيرًا، نجمع اسم حزمة Cloud Storage كما هو موضّح أعلاه في قسم عمليات الاستيراد. في ما يلي مقارنة بين ما قبل تنفيذ هذه التعديلات وما بعدها:
قبل:
class BaseHandler(webapp2.RequestHandler):
'Derived request handler mixing-in Jinja2 support'
@webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_response(self, _template, **context):
self.response.write(self.jinja2.render_template(_template, **context))
بعد:
app = Flask(__name__)
ds_client = ndb.Client()
gcs_client = storage.Client()
_, PROJECT_ID = google.auth.default()
BUCKET = '%s.appspot.com' % PROJECT_ID
تعديل إذن الوصول إلى Datastore
يتوافق Cloud NDB في الغالب مع App Engine NDB. أحد الاختلافات التي تم تناولها هو الحاجة إلى عميل واجهة برمجة التطبيقات. والسبب الآخر هو أنّ Datastore يتطلّب التحكّم في الوصول إلى Datastore من خلال مدير سياق Python لعميل واجهة برمجة التطبيقات. يعني ذلك بشكل أساسي أنّ جميع طلبات الوصول إلى Datastore باستخدام مكتبة برامج Cloud NDB لا يمكن إجراؤها إلا ضمن مجموعات Python with.
هذا تغيير واحد، والتغيير الآخر هو أنّ Blobstore وعناصره، مثل BlobKey، غير متوافقة مع Cloud Storage، لذا عليك تغيير file_blob إلى ndb.StringProperty بدلاً من ذلك. في ما يلي فئة نموذج البيانات والدالتان store_visit() وfetch_visits() المعدَّلتان اللتان تعكسان هذه التغييرات:
قبل:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
file_blob = ndb.BlobKeyProperty()
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob=upload_key).put()
def fetch_visits(limit):
'get most recent visits'
return Visit.query().order(-Visit.timestamp).fetch(limit)
بعد:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
file_blob = ndb.StringProperty()
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob=upload_key).put()
def fetch_visits(limit):
'get most recent visits'
with ds_client.context():
return Visit.query().order(-Visit.timestamp).fetch(limit)
في ما يلي تمثيل مصوّر للتغييرات التي تم إجراؤها حتى الآن:

تعديل المعالِجات
معالج التحميل
المعالِجات في webapp2 هي فئات، بينما هي دوال في Flask. بدلاً من طريقة فعل HTTP، يستخدم Flask الفعل لتزيين الدالة. يتم استبدال Blobstore ومعالِجات webapp الخاصة به بوظائف من Cloud Storage بالإضافة إلى Flask وأدواته المساعدة:
قبل:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
'Upload blob (POST) handler'
def post(self):
uploads = self.get_uploads()
blob_id = uploads[0].key() if uploads else None
store_visit(self.request.remote_addr, self.request.user_agent, blob_id)
self.redirect('/', code=307)
بعد:
@app.route('/upload', methods=['POST'])
def upload():
'Upload blob (POST) handler'
fname = None
upload = request.files.get('file', None)
if upload:
fname = secure_filename(upload.filename)
blob = gcs_client.bucket(BUCKET).blob(fname)
blob.upload_from_file(upload, content_type=upload.content_type)
store_visit(request.remote_addr, request.user_agent, fname)
return redirect(url_for('root'), code=307)
في ما يلي بعض الملاحظات حول هذا التعديل:
- بدلاً من
blob_id، يتم الآن تحديد بيانات الملفات حسب اسم الملف (fname) إذا كان متوفّرًا، وNoneفي الحالات الأخرى (إذا اختار المستخدم عدم تحميل ملف). - أخفت معالِجات Blobstore عملية التحميل عن المستخدمين، ولكن Cloud Storage لا تفعل ذلك، لذا يمكنك الاطّلاع على الرمز البرمجي المُضاف حديثًا والذي يحدّد كائن blob الخاص بالملف وموقعه (الحزمة) بالإضافة إلى الطلب الذي ينفّذ عملية التحميل الفعلية. (
upload_from_file()). - يستخدم
webapp2جدول توجيه في أسفل ملف التطبيق، بينما يتم العثور على مسارات Flask في كل معالج مزخرف. - يُنهي كلا المعالجَين وظائفهما من خلال إعادة التوجيه إلى الصفحة الرئيسية (
/) مع الحفاظ على طلبPOSTباستخدام رمز الإرجاع HTTP 307.
معالج التنزيل
يتبع تحديث معالج التنزيل نمطًا مشابهًا لمعالج التحميل، ولكن هناك قدر أقل بكثير من التعليمات البرمجية التي يجب النظر إليها. استبدِل وظائف Blobstore وwebapp بما يعادلها في Cloud Storage وFlask:
قبل:
class ViewBlobHandler(blobstore_handlers.BlobstoreDownloadHandler):
'view uploaded blob (GET) handler'
def get(self, blob_key):
self.send_blob(blob_key) if blobstore.get(blob_key) else self.error(404)
بعد:
@app.route('/view/<path:fname>')
def view(fname):
'view uploaded blob (GET) handler'
blob = gcs_client.bucket(BUCKET).blob(fname)
try:
media = blob.download_as_bytes()
except exceptions.NotFound:
abort(404)
return send_file(io.BytesIO(media), mimetype=blob.content_type)
ملاحظات حول هذا التحديث:
- مرة أخرى، يزيّن Flask دوال المعالِج بمسارها، بينما يفعل
webappذلك في جدول توجيه في الأسفل، لذا يجب التعرّف على بنية مطابقة الأنماط في الأخير('/view/([^/]+)?') مقارنةً ببنية Flask ('/view/<path:fname>'). - كما هو الحال مع معالج التحميل، هناك بعض العمل الإضافي المطلوب على جانب Cloud Storage للوظائف التي تم استبعادها بواسطة معالجات Blobstore، أي تحديد الملف (blob) المعني وتنزيل الملف الثنائي بشكل صريح مقابل طلب إجراء
send_blob()واحد لمعالج Blobstore. - في كلتا الحالتَين، يتم عرض خطأ HTTP 404 للمستخدم إذا لم يتم العثور على العنصر.
المعالج الرئيسي
تحدث التغييرات النهائية على التطبيق الرئيسي في المعالج الرئيسي. تم استبدال طرق أفعال HTTP في webapp2 بدالة واحدة تجمع بين وظائفها. استبدِل الفئة MainHandler بالدالة root() وأزِل جدول التوجيه webapp2 كما هو موضّح أدناه:
قبل:
class MainHandler(BaseHandler):
'main application (GET/POST) handler'
def get(self):
self.render_response('index.html',
upload_url=blobstore.create_upload_url('/upload'))
def post(self):
visits = fetch_visits(10)
self.render_response('index.html', visits=visits)
app = webapp2.WSGIApplication([
('/', MainHandler),
('/upload', UploadHandler),
('/view/([^/]+)?', ViewBlobHandler),
], debug=True)
بعد:
@app.route('/', methods=['GET', 'POST'])
def root():
'main application (GET/POST) handler'
context = {}
if request.method == 'GET':
context['upload_url'] = url_for('upload')
else:
context['visits'] = fetch_visits(10)
return render_template('index.html', **context)
بدلاً من استخدام طريقتَي get() وpost() منفصلتَين، هما في الأساس عبارة if-else في root(). أيضًا، بما أنّ root() هي دالة واحدة، هناك استدعاء واحد فقط لعرض النموذج لكل من GET وPOST، في حين أنّ ذلك غير ممكن في webapp2.
في ما يلي تمثيل مصوّر لهذه المجموعة الثانية والنهائية من التغييرات على "main.py":

(اختياري) "تحسين" التوافق مع الأنظمة القديمة
الحلّ الذي تم إنشاؤه أعلاه يعمل بشكل مثالي، ولكن فقط إذا كنت تبدأ من الصفر وليس لديك ملفات تم إنشاؤها بواسطة Blobstore. بما أنّنا عدّلنا التطبيق لتحديد الملفات حسب اسم الملف بدلاً من BlobKey، لن يتمكّن تطبيق الوحدة 16 المكتمل من عرض ملفات Blobstore. بعبارة أخرى، أجرينا تغييرًا غير متوافق مع الأنظمة القديمة أثناء عملية نقل البيانات هذه. نقدّم الآن نسخة بديلة من main.py تُسمّى main-migrate.py (موجودة في المستودع) تحاول سد هذه الفجوة.
"الإضافة" الأولى التي تتيح استخدام الملفات التي تم إنشاؤها من خلال Blobstore هي نموذج بيانات يحتوي على BlobKeyProperty (بالإضافة إلى StringProperty للملفات التي تم إنشاؤها من خلال Cloud Storage):
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
file_blob = ndb.BlobKeyProperty() # backwards-compatibility
file_gcs = ndb.StringProperty()
سيتم استخدام السمة file_blob لتحديد الملفات التي تم إنشاؤها باستخدام Blobstore، بينما سيتم استخدام السمة file_gcs لملفات Cloud Storage. الآن، عند إنشاء زيارات جديدة، يمكنك تخزين قيمة في file_gcs بشكل صريح بدلاً من file_blob، لذا يبدو store_visit مختلفًا بعض الشيء:
قبل:
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob=upload_key).put()
بعد:
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
with ds_client.context():
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_gcs=upload_key).put()
عند جلب أحدث الزيارات، "يتم توحيد" البيانات قبل إرسالها إلى النموذج:
قبل:
@app.route('/', methods=['GET', 'POST'])
def root():
'main application (GET/POST) handler'
context = {}
if request.method == 'GET':
context['upload_url'] = url_for('upload')
else:
context['visits'] = fetch_visits(10)
return render_template('index.html', **context)
بعد:
@app.route('/', methods=['GET', 'POST'])
def root():
'main application (GET/POST) handler'
context = {}
if request.method == 'GET':
context['upload_url'] = url_for('upload')
else:
context['visits'] = etl_visits(fetch_visits(10))
return render_template('index.html', **context)
بعد ذلك، تأكَّد من توفّر file_blob أو file_gcs (أو عدم توفّر أي منهما). إذا كان هناك ملف متاح، اختَر الملف الحالي واستخدِم المعرّف (BlobKey للملفات التي تم إنشاؤها باستخدام Blobstore أو اسم الملف للملفات التي تم إنشاؤها باستخدام Cloud Storage). عندما نقول "الملفات التي تم إنشاؤها في Cloud Storage"، فإننا نعني الملفات التي تم إنشاؤها باستخدام مكتبة برامج Cloud Storage. يكتب Blobstore أيضًا في Cloud Storage، ولكن في هذه الحالة، ستكون هذه الملفات من إنشاء Blobstore.
والأهم من ذلك، ما هي دالة etl_visits() التي تُستخدم لتسوية البيانات أو استخراجها وتحويلها وتحميلها (ETL) للمستخدم النهائي؟ ويظهر على النحو التالي:
def etl_visits(visits):
return [{
'visitor': v.visitor,
'timestamp': v.timestamp,
'file_blob': v.file_gcs if hasattr(v, 'file_gcs') \
and v.file_gcs else v.file_blob
} for v in visits]
من المحتمل أن يبدو الأمر كما توقعت: يتكرر الرمز خلال جميع الزيارات، ولكل زيارة، يأخذ بيانات الزائر والطابع الزمني حرفيًا، ثم يتحقق لمعرفة ما إذا كان file_gcs أو file_blob موجودًا، وإذا كان الأمر كذلك، فاختيار أحدهما (أو None إذا لم يكن أي منهما موجودًا).
في ما يلي توضيح للاختلافات بين main.py وmain-migrate.py:

إذا كنت تبدأ من الصفر بدون ملفات تم إنشاؤها باستخدام Blobstore، استخدِم main.py، ولكن إذا كنت في مرحلة انتقالية وتريد استخدام ملفات متوافقة تم إنشاؤها باستخدام كل من Blobstore و Cloud Storage، يمكنك الاطّلاع على main-migrate.py كمثال على كيفية التعامل مع سيناريو مثل هذا لمساعدتك في التخطيط لعمليات نقل بيانات تطبيقاتك. عند إجراء عمليات نقل معقدة، من المحتمل أن تظهر حالات خاصة، لذا يهدف هذا المثال إلى إظهار تقارب أكبر لتحديث التطبيقات الحقيقية ببيانات حقيقية.
6. الملخّص/التنظيف
يختتم هذا القسم الدرس التطبيقي حول الترميز من خلال نشر التطبيق والتحقّق من أنّه يعمل على النحو المطلوب وفي أي ناتج منعكس. بعد التحقّق من التطبيق، نفِّذ أي خطوات لإزالة البيانات غير الضرورية وفكِّر في الخطوات التالية.
نشر التطبيق والتحقّق منه
قبل إعادة نشر تطبيقك، احرص على تشغيل pip install -t lib -r requirements.txt للحصول على مكتبات الجهات الخارجية المجمّعة ذاتيًا في مجلد lib. إذا أردت تشغيل الحلّ المتوافق مع الأنظمة القديمة، عليك أولاً إعادة تسمية main-migrate.py باسم main.py. الآن، شغِّل gcloud app deploy، وتأكَّد من أنّ التطبيق يعمل بشكل مماثل لتطبيق الوحدة 15. تبدو شاشة النموذج على النحو التالي:

تبدو صفحة "أحدث الزيارات" على النحو التالي:

تهانينا على إكمال هذا الدرس التطبيقي حول الترميز الذي يحلّ محلّ App Engine Blobstore مع Cloud Storage وApp Engine NDB مع Cloud NDB وwebapp2 مع Flask. يجب أن يتطابق الرمز الآن مع ما هو موجود في مجلد FINISH (Module 16). يتوفّر أيضًا البديل main-migrate.py في هذا المجلد.
"نقل" Python 3
إنّ توجيه Python 3 runtime الذي تمت الإشارة إلى أسطر رموزه باعتبارها تعليقًا في أعلى app.yaml هو كل ما يلزم لنقل هذا التطبيق إلى Python 3. الرمز المصدر نفسه متوافق مع Python 3، لذا لا يلزم إجراء أي تغييرات عليه. لنشر هذا التطبيق كتطبيق Python 3، نفِّذ الخطوات التالية:
- أزِل التعليق عن توجيه Python 3
runtimeفي أعلىapp.yaml. - احذف جميع الأسطر الأخرى في
app.yaml. - احذف ملف
appengine_config.py. (غير مستخدَم في وقت تشغيل Python 3) - احذف المجلد
libإذا كان متوفّرًا. (غير ضروري مع وقت تشغيل Python 3)
تَنظيم
بنود عامة
إذا انتهيت من العمل في الوقت الحالي، ننصحك بإيقاف تطبيق App Engine لتجنُّب تحمُّل تكاليف الفوترة. ومع ذلك، إذا أردت إجراء المزيد من الاختبارات أو التجارب، فإنّ منصة App Engine توفّر حصة مجانية، ولن يتم تحصيل أي رسوم منك طالما أنّك لا تتجاوز فئة الاستخدام هذه. هذا بالنسبة إلى الحوسبة، ولكن قد تكون هناك أيضًا رسوم لخدمات App Engine ذات الصلة، لذا يُرجى الاطّلاع على صفحة الأسعار لمزيد من المعلومات. إذا كانت عملية النقل هذه تتضمّن خدمات Cloud أخرى، فستتم فوترتها بشكل منفصل. في كلتا الحالتَين، يُرجى الاطّلاع على قسم "تفاصيل خاصة بهذا الدرس التطبيقي حول الترميز" أدناه، إذا كان ذلك منطبقًا.
للكشف الكامل، يؤدي النشر على منصة حوسبة بدون خادم في 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*ation، على سبيل المثال، "us" إذا كان تطبيقك مستضافًا في الولايات المتحدة.
من ناحية أخرى، إذا كنت لن تواصل استخدام هذا التطبيق أو الدروس التطبيقية الأخرى ذات الصلة حول الترميز لنقل البيانات وتريد حذف كل شيء بالكامل، يمكنك إيقاف مشروعك.
خاص بهذا الدرس التطبيقي حول الترميز
الخدمات المدرَجة أدناه فريدة لهذا الدرس التطبيقي حول الترميز. يُرجى الرجوع إلى مستندات كل منتج لمزيد من المعلومات:
- تندرج خدمة Blobstore في App Engine ضمن الحصص والحدود القصوى للبيانات المخزَّنة، لذا يُرجى مراجعة ذلك بالإضافة إلى صفحة الأسعار للخدمات المجمّعة القديمة.
- توفّر خدمة Cloud Storage مستوى مجانيًا لمناطق معيّنة. يمكنك أيضًا الاطّلاع على صفحة الأسعار العامة لمزيد من المعلومات.
- يتم توفير خدمة App Engine Datastore من خلال Cloud Datastore (Cloud Firestore في وضع Datastore) الذي يتضمّن أيضًا فئة مجانية. يُرجى الاطّلاع على صفحة الأسعار لمزيد من المعلومات."
يُرجى العِلم أنّه في حال نقل البيانات من الوحدة 15 إلى الوحدة 16، ستظلّ لديك بيانات في Blobstore، ولهذا السبب نُدرج معلومات الأسعار أعلاه.
الخطوات التالية
بالإضافة إلى هذا الدليل التعليمي، تشمل وحدات نقل البيانات الأخرى التي تركّز على الابتعاد عن الخدمات المجمّعة القديمة ما يلي:
- الوحدة 2: نقل البيانات من
ndbعلى App Engine إلى Cloud NDB - الوحدات من 7 إلى 9: نقل البيانات من مهام الإرسال في قائمة انتظار المهام في App Engine إلى Cloud Tasks
- الوحدتان 12 و13: نقل البيانات من Memcache على App Engine إلى Cloud Memorystore
- الوحدتان 18 و19: نقل البيانات من قائمة انتظار المهام في App Engine (مهام السحب) إلى 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. مراجع إضافية
مشاكل/ملاحظات حول الدروس التطبيقية حول الترميز
إذا واجهت أي مشاكل في هذا الدرس التطبيقي حول الترميز، يُرجى البحث عن مشكلتك أولاً قبل تقديم بلاغ. روابط للبحث عن مشاكل جديدة وإنشائها:
مراجع لنقل البيانات
يمكن العثور على روابط إلى مجلدات المستودع للوحدة 15 (START) والوحدة 16 (FINISH) في الجدول أدناه. ويمكن أيضًا الوصول إليها من مستودع جميع عمليات نقل الدروس التطبيقية حول الترميز في App Engine الذي يمكنك استنساخه أو تنزيل ملف ZIP منه.
درس تطبيقي حول الترميز | Python 2 | Python 3 |
الوحدة 15 | لا ينطبق | |
الوحدة 16 (هذا الدرس التطبيقي حول الترميز) | (نفس Python 2) |
مراجع متوفرة على الإنترنت
في ما يلي مراجع على الإنترنت قد تكون ذات صلة بهذا البرنامج التعليمي:
App Engine Blobstore وCloud Storage
- خدمة App Engine Blobstore
- نقل البيانات إلى مكتبة برامج Cloud Storage
- الصفحة الرئيسية لخدمة Cloud Storage
- مستندات Cloud Storage
منصة App Engine
- مستندات App Engine
- بيئة تشغيل Python 2 App Engine (البيئة العادية)
- استخدام مكتبات App Engine المضمّنة في App Engine بلغة Python 2
- بيئة تشغيل Python 3 App Engine (البيئة العادية)
- الاختلافات بين بيئات التشغيل Python 2 وPython 3 في App Engine (البيئة العادية)
- دليل نقل البيانات من Python 2 إلى Python 3 في App Engine (البيئة العادية)
- معلومات حول أسعار وحصص App Engine
- إطلاق الجيل الثاني من منصة App Engine (2018)
- مقارنة بين منصات الجيلَين الأول والثاني
- دعم طويل الأمد لبيئات التشغيل القديمة
- مستودع نماذج نقل المستندات
- مستودع نماذج نقل البيانات التي يساهم بها المنتدى
معلومات أخرى عن Cloud
- Python على Google Cloud Platform
- مكتبات برامج Google Cloud Python
- فئة "المجانية دائمًا" في Google Cloud
- حزمة تطوير البرامج (SDK) في Google Cloud (أداة سطر الأوامر
gcloud) - جميع مستندات Google Cloud
Python
- أنظمة النماذج Django وJinja2
- إطار عمل الويب
webapp2 - مستندات
webapp2 - روابط "
webapp2_extras" webapp2_extrasمستندات Jinja2- إطار عمل الويب Flask
الفيديوهات
- Serverless Migration Station
- Serverless Expeditions
- الاشتراك في قناة Google Cloud Tech
- الاشتراك في قناة Google Developers
الترخيص
هذا العمل مرخَّص بموجب رخصة المشاع الإبداعي 2.0 العامة مع نسب العمل إلى مؤلفه.