Python 2 App Engine Cloud NDB & को माइग्रेट करना Python 3 और Cloud Datastore (मॉड्यूल 9) पर Cloud Tasks ऐप्लिकेशन इस्तेमाल करना

1. खास जानकारी

सर्वरलेस माइग्रेशन स्टेशन की कोडलैब सीरीज़ (अपने हिसाब से सीखने और प्रैक्टिकल करने वाले ट्यूटोरियल) और इससे जुड़े वीडियो का मकसद, Google Cloud सर्वरलेस डेवलपर की मदद करना है. इससे वे एक या उससे ज़्यादा माइग्रेशन करके, अपने ऐप्लिकेशन को बेहतर बना सकते हैं. इनमें मुख्य रूप से लेगसी सेवाओं से माइग्रेट करना शामिल है. ऐसा करने से, आपके ऐप्लिकेशन को एक जगह से दूसरी जगह ले जाना आसान हो जाता है. साथ ही, आपको ज़्यादा विकल्प और सुविधा मिलती है. इससे आपको Cloud प्रॉडक्ट की ज़्यादा रेंज के साथ इंटिग्रेट करने और उन्हें ऐक्सेस करने में मदद मिलती है. साथ ही, भाषा के नए वर्शन पर आसानी से अपग्रेड किया जा सकता है. शुरुआत में, इस सीरीज़ में मुख्य तौर पर App Engine (स्टैंडर्ड एनवायरमेंट) डेवलपर के लिए कॉन्टेंट शामिल किया गया था. हालांकि, अब इसमें अन्य सर्वरलेस प्लैटफ़ॉर्म के लिए भी कॉन्टेंट शामिल किया गया है. जैसे, Cloud Functions और Cloud Run. इसके अलावा, इसमें अन्य प्लैटफ़ॉर्म के लिए भी कॉन्टेंट शामिल किया गया है.

इस कोडलैब का मकसद, मॉड्यूल 8 के सैंपल ऐप्लिकेशन को Python 3 पर पोर्ट करना है. साथ ही, Datastore (Cloud Firestore in Datastore mode) के ऐक्सेस के लिए, Cloud NDB की जगह Cloud Datastore क्लाइंट लाइब्रेरी का इस्तेमाल करना है. इसके अलावा, Cloud Tasks क्लाइंट लाइब्रेरी को नए वर्शन में अपग्रेड करना है.

हमने मॉड्यूल 7 में, पुश टास्क के लिए टास्क क्यू का इस्तेमाल करने की सुविधा जोड़ी थी. इसके बाद, मॉड्यूल 8 में हमने इस सुविधा को Cloud Tasks पर माइग्रेट कर दिया. मॉड्यूल 9 में, हम Python 3 और Cloud Datastore के बारे में जानेंगे. पुल किए गए टास्क के लिए Task Queues का इस्तेमाल करने वाले लोग, Cloud Pub/Sub पर माइग्रेट करेंगे. उन्हें मॉड्यूल 18-19 देखना चाहिए.

आपको इनके बारे में जानकारी मिलेगी

  • मॉड्यूल 8 के सैंपल ऐप्लिकेशन को Python 3 में पोर्ट करना
  • Datastore के ऐक्सेस को Cloud NDB से Cloud Datastore क्लाइंट लाइब्रेरी पर स्विच करना
  • Cloud Tasks की क्लाइंट लाइब्रेरी के नए वर्शन पर अपग्रेड करना

आपको किन चीज़ों की ज़रूरत होगी

सर्वे

इस ट्यूटोरियल का इस्तेमाल कैसे किया जाएगा?

सिर्फ़ इसे पढ़ें इसे पढ़ें और एक्सरसाइज़ पूरी करें

Python के साथ अपने अनुभव को आप क्या रेटिंग देंगे?

शुरुआती सामान्य एडवांस

Google Cloud की सेवाओं को इस्तेमाल करने के अपने अनुभव को आप क्या रेटिंग देंगे?

शुरुआती सामान्य एडवांस

2. बैकग्राउंड

मॉड्यूल 7 में बताया गया है कि Python 2 Flask App Engine ऐप्लिकेशन में, App Engine Task Queue के पुश टास्क का इस्तेमाल कैसे किया जाता है. मॉड्यूल 8 में, उस ऐप्लिकेशन को Task Queue से Cloud Tasks पर माइग्रेट किया जाता है. मॉड्यूल 9 में, आपको इस प्रोसेस को जारी रखना है. साथ ही, उस ऐप्लिकेशन को Python 3 पर पोर्ट करना है. इसके अलावा, Datastore ऐक्सेस करने के लिए Cloud NDB के बजाय, Cloud 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 से Python 3 में माइग्रेट करने पर, कोई बड़ा बदलाव नहीं होता. इसका मतलब है कि आपको यहां कुछ और करने की ज़रूरत नहीं है.

इस ट्यूटोरियल में ये चरण शामिल हैं:

  1. सेटअप/प्रीवर्क
  2. कॉन्फ़िगरेशन अपडेट करना
  3. ऐप्लिकेशन कोड में बदलाव करना

3. सेटअप/प्रीवर्क

इस सेक्शन में, यह बताया गया है कि:

  1. अपना Cloud प्रोजेक्ट सेट अप करना
  2. बेसलाइन सैंपल ऐप्लिकेशन पाना
  3. बेसलाइन ऐप्लिकेशन को (फिर से) डिप्लॉय करें और उसकी पुष्टि करें

इन चरणों से यह पक्का किया जाता है कि आप काम करने वाले कोड से शुरुआत कर रहे हैं और यह क्लाउड सेवाओं पर माइग्रेट करने के लिए तैयार है.

1. प्रोजेक्ट सेट अप करना

अगर आपने मॉड्यूल 8 का कोडलैब पूरा कर लिया है, तो उसी प्रोजेक्ट और कोड का फिर से इस्तेमाल करें. इसके अलावा, एक नया प्रोजेक्ट बनाएं या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल करें. पक्का करें कि प्रोजेक्ट में चालू बिलिंग खाता हो और App Engine ऐप्लिकेशन चालू हो. अपना प्रोजेक्ट आईडी ढूंढें, क्योंकि इस कोडलैब के दौरान आपको इसकी ज़रूरत होगी. जब भी आपको PROJECT_ID वैरिएबल दिखे, तब इसका इस्तेमाल करें.

2. बेसलाइन सैंपल ऐप्लिकेशन पाना

इसके लिए, Module 8 App Engine ऐप्लिकेशन का काम करना ज़रूरी है: Module 8 का कोडलैब पूरा करें (सुझाया गया) या repo से Module 8 ऐप्लिकेशन कॉपी करें. चाहे हमारा कोड इस्तेमाल करें या अपना, मॉड्यूल 8 के कोड से ही हम शुरू करेंगे ("START"). इस कोडलैब में, माइग्रेट करने का तरीका बताया गया है. इसमें आखिर में ऐसा कोड दिया गया है जो Module 9 के repo फ़ोल्डर ("FINISH") में मौजूद कोड जैसा है.

Module 7 के किसी भी ऐप्लिकेशन का इस्तेमाल करने पर, फ़ोल्डर ऐसा दिखना चाहिए. इसमें lib फ़ोल्डर भी हो सकता है:

$ ls
README.md               appengine_config.py     requirements.txt
app.yaml                main.py                 templates

3. बेसलाइन ऐप्लिकेशन को (फिर से) डिप्लॉय करें और उसकी पुष्टि करें

Module 8 ऐप्लिकेशन को डिप्लॉय करने के लिए, यह तरीका अपनाएं:

  1. अगर lib फ़ोल्डर मौजूद है, तो उसे मिटाएं. इसके बाद, pip install -t lib -r requirements.txt चलाकर lib को फिर से भरें. अगर आपने डेवलपमेंट मशीन पर Python 2 और 3, दोनों इंस्टॉल किए हैं, तो आपको pip2 का इस्तेमाल करना पड़ सकता है.
  2. पक्का करें कि आपने gcloud कमांड-लाइन टूल को इंस्टॉल और शुरू कर लिया हो. साथ ही, इसके इस्तेमाल की समीक्षा कर ली हो.
  3. (ज़रूरी नहीं) अगर आपको हर gcloud कमांड के साथ PROJECT_ID नहीं डालना है, तो gcloud config set project PROJECT_ID का इस्तेमाल करके अपना Cloud प्रोजेक्ट सेट करें.
  4. gcloud app deploy की मदद से, सैंपल ऐप्लिकेशन को डिप्लॉय करना
  5. पुष्टि करें कि ऐप्लिकेशन बिना किसी समस्या के उम्मीद के मुताबिक काम कर रहा है. अगर आपने मॉड्यूल 8 का कोडलैब पूरा कर लिया है, तो ऐप्लिकेशन में सबसे ज़्यादा बार आने वाले लोगों के साथ-साथ हाल ही की विज़िट भी दिखेंगी. इसकी जानकारी यहां दी गई है. सबसे नीचे, पुराने टास्क की जानकारी दी गई है. इन्हें मिटा दिया जाएगा.

4aa8a2cb5f527079.png

4. कॉन्फ़िगरेशन अपडेट करना

requirements.txt

नया requirements.txt, मॉड्यूल 8 के requirements.txt के जैसा ही है. इसमें सिर्फ़ एक बड़ा बदलाव किया गया है: 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 में शामिल करना ज़रूरी है. इस वजह से, app.yaml के पूरे libraries सेक्शन को मिटाया जा सकता है.

एक और अपडेट यह है कि Python 3 रनटाइम के लिए, ऐसे वेब फ़्रेमवर्क का इस्तेमाल करना ज़रूरी है जो खुद राउटिंग करते हैं. इसलिए, सभी स्क्रिप्ट हैंडलर को auto में बदलना होगा. हालांकि, सभी रास्तों को auto में बदलना होगा. साथ ही, इस सैंपल ऐप्लिकेशन से कोई भी स्टैटिक फ़ाइल नहीं दिखाई जाती है. इसलिए, किसी भी हैंडलर का होना ज़रूरी नहीं है. इसलिए, पूरे 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 में दिए गए तीसरे पक्ष के पैकेज अपने-आप इंस्टॉल कर देता है. खास जानकारी जनरेट की जा रही है:

  1. सेल्फ़-बंडल्ड या तीसरे पक्ष की कॉपी की गई लाइब्रेरी नहीं होनी चाहिए. उन्हें requirements.txt में सूची बनाएं
  2. pip install को lib फ़ोल्डर में नहीं ले जाया जा सकता. इसका मतलब है कि lib फ़ोल्डर नहीं बनाया जा सकता
  3. app.yaml में, तीसरे पक्ष की कोई भी लाइब्रेरी शामिल नहीं है. इसलिए, libraries सेक्शन मौजूद नहीं है. इन्हें requirements.txt में शामिल करें
  4. आपके ऐप्लिकेशन में तीसरे पक्ष की किसी भी लाइब्रेरी का इस्तेमाल नहीं किया गया है. इसका मतलब है कि कोई appengine_config.py फ़ाइल नहीं है

डेवलपर के लिए, requirements.txt में सभी ज़रूरी तीसरे पक्ष की लाइब्रेरी की सूची बनाना ही ज़रूरी है.

5. ऐप्लिकेशन की फ़ाइलें अपडेट करना

सिर्फ़ एक ऐप्लिकेशन फ़ाइल, main.py मौजूद है. इसलिए, इस सेक्शन में किए गए सभी बदलाव सिर्फ़ उस फ़ाइल पर लागू होते हैं. नीचे, "डिफ़्स" का एक उदाहरण दिया गया है. इसमें उन सभी बदलावों के बारे में बताया गया है जो मौजूदा कोड को नए ऐप्लिकेशन में फिर से व्यवस्थित करने के लिए ज़रूरी हैं. पाठकों को कोड की हर लाइन को पढ़ने की ज़रूरत नहीं है, क्योंकि इसका मकसद सिर्फ़ यह है कि वे इस रिफ़ैक्टरिंग के लिए ज़रूरी चीज़ों की एक झलक पा सकें. हालांकि, अगर चाहें, तो इसे नए टैब में खोलें या डाउनलोड करके ज़ूम इन करें.

5d043768ba7be742.png

इंपोर्ट और इनिशियलाइज़ेशन को अपडेट करना

मॉड्यूल 8 के लिए main.py में इंपोर्ट सेक्शन, Cloud NDB और Cloud Tasks का इस्तेमाल करता है. यह इस तरह दिखना चाहिए:

BEFORE:

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 का इस्तेमाल करें
  • आसान लॉगिंग के लिए, print() के ज़रिए stdout (या stderr) को भेजें
  • Python logging मॉड्यूल का इस्तेमाल करने की ज़रूरत नहीं है. इसलिए, इसे हटा दें

इसलिए, logging को इंपोर्ट करने की प्रोसेस मिटा दें और google.cloud.ndb को google.cloud.datastore से बदलें. इसी तरह, NDB क्लाइंट के बजाय Datastore क्लाइंट की ओर इशारा करने के लिए, ds_client को बदलें. इन बदलावों के बाद, आपका नया ऐप्लिकेशन अब ऐसा दिखेगा:

AFTER:

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 रिकॉर्ड बनाकर विज़िट रजिस्टर करता है. साथ ही, विज़िट करने वाले क्लाइंट का आईपी पता और उपयोगकर्ता एजेंट (ब्राउज़र टाइप) सेव करता है.

BEFORE:

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() ऐसा दिखना चाहिए:

AFTER:

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() है. यह न सिर्फ़ नई Visit के लिए ओरिजनल क्वेरी करता है, बल्कि यह आखिरी बार दिखाई गई Visit का टाइमस्टैंप भी कैप्चर करता है. साथ ही, यह एक पुश टास्क बनाता है, जो पुरानी Visit को एक साथ मिटाने के लिए /trim (इस तरह trim()) को कॉल करता है. यहां Cloud NDB का इस्तेमाल किया गया है:

BEFORE:

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

मुख्य बदलाव:

  1. Cloud NDB क्वेरी को Cloud Datastore के बराबर क्वेरी से बदलें. क्वेरी के स्टाइल में थोड़ा अंतर होता है.
  2. Datastore में, कॉन्टेक्स्ट मैनेजर का इस्तेमाल करना ज़रूरी नहीं है. साथ ही, यह Cloud NDB की तरह आपको इसका डेटा (to_dict() के साथ) निकालने के लिए भी नहीं कहता.
  3. लॉगिंग कॉल को print() से बदलें

उन बदलावों के बाद, fetch_visits() इस तरह दिखते हैं:

AFTER:

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 में, हमने मौजूदा मॉड्यूल 1 ऐप्लिकेशन में App Engine taskqueue का इस्तेमाल जोड़ा है. पुश टास्क को App Engine की लेगसी सुविधा के तौर पर इस्तेमाल करने का एक मुख्य फ़ायदा यह है कि "डिफ़ॉल्ट" कतार अपने-आप बन जाती है. जब उस ऐप्लिकेशन को मॉड्यूल 8 में Cloud Tasks पर माइग्रेट किया गया था, तब वह डिफ़ॉल्ट कतार पहले से मौजूद थी. इसलिए, हमें अब भी इसके बारे में चिंता करने की ज़रूरत नहीं है. हालांकि, मॉड्यूल 9 में यह बदल जाता है.

एक ज़रूरी बात यह है कि नया App Engine ऐप्लिकेशन, App Engine की सेवाओं का इस्तेमाल नहीं करता. इसलिए, अब यह नहीं माना जा सकता कि App Engine, किसी दूसरे प्रॉडक्ट (Cloud Tasks) में टास्क की सूची अपने-आप बना देता है. लिखे गए तरीके के मुताबिक, fetch_visits() में (ऐसी कतार के लिए जो मौजूद नहीं है) टास्क बनाने पर, टास्क नहीं बनेगा. ("default") कतार मौजूद है या नहीं, यह देखने के लिए एक नए फ़ंक्शन की ज़रूरत होती है. अगर कतार मौजूद नहीं है, तो एक नई कतार बनाएं.

इस फ़ंक्शन _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

Cloud Tasks create_queue() फ़ंक्शन के लिए, कतार के नाम को छोड़कर कतार का पूरा पाथनेम ज़रूरी है. आसानी के लिए, एक और वैरिएबल 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 कोड, सबसे पुरानी विज़िट से पहले की विज़िट के लिए क्वेरी करता है. यह क्वेरी में सिर्फ़ कुंजियों का इस्तेमाल करती है, ताकि प्रोसेस को तेज़ी से पूरा किया जा सके. अगर आपको सिर्फ़ विज़िट आईडी की ज़रूरत है, तो पूरा डेटा फ़ेच करने की क्या ज़रूरत है? सभी विज़िट आईडी मिल जाने के बाद, Cloud NDB के delete_multi() फ़ंक्शन का इस्तेमाल करके, उन्हें एक साथ मिटा दें.

BEFORE:

@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() में बदलना शामिल है.

AFTER:

@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 में पोर्ट करना

इस सैंपल ऐप्लिकेशन को Python 2 और 3, दोनों पर चलाने के लिए डिज़ाइन किया गया था. Python 3 से जुड़े किसी भी बदलाव के बारे में, इस ट्यूटोरियल के संबंधित सेक्शन में पहले ही बताया जा चुका है. इसके लिए, कोई अन्य चरण पूरा करने या कंपैटिबिलिटी लाइब्रेरी की ज़रूरत नहीं होती.

Cloud Tasks से जुड़ा अपडेट

Python 2 के साथ काम करने वाली Cloud Tasks क्लाइंट लाइब्रेरी का आखिरी वर्शन 1.5.0 है. यह लेख लिखते समय, Python 3 के लिए क्लाइंट लाइब्रेरी का नया वर्शन, Python 3 के साथ पूरी तरह से काम करता है. इसलिए, इसे अपडेट करने की ज़रूरत नहीं है.

एचटीएमएल टेंप्लेट अपडेट करना

एचटीएमएल टेंप्लेट फ़ाइल, templates/index.html में भी कोई बदलाव करने की ज़रूरत नहीं है. इसलिए, Module 9 ऐप्लिकेशन पर पहुंचने के लिए, ये सभी ज़रूरी बदलाव किए गए हैं.

6. खास जानकारी/सफ़ाई

ऐप्लिकेशन डिप्लॉय करना और उसकी पुष्टि करना

कोड अपडेट करने के बाद, खास तौर पर Python 3 में पोर्ट करने के बाद, gcloud app deploy का इस्तेमाल करके अपना ऐप्लिकेशन डिप्लॉय करें. आउटपुट, मॉड्यूल 7 और 8 के ऐप्लिकेशन के आउटपुट जैसा ही होना चाहिए. हालांकि, आपने डेटाबेस ऐक्सेस को Cloud Datastore क्लाइंट लाइब्रेरी में ट्रांसफ़र कर दिया है और Python 3 पर अपग्रेड कर दिया है:

Module 7 visitme app

इस चरण के बाद, कोडलैब पूरा हो जाता है. हमारा सुझाव है कि आप अपने कोड की तुलना Module 9 फ़ोल्डर में मौजूद कोड से करें. बधाई हो!

व्यवस्थित करें

सामान्य

अगर आपको अभी और काम नहीं करना है, तो हमारा सुझाव है कि आप अपने App Engine ऐप्लिकेशन को बंद कर दें, ताकि आपसे शुल्क न लिया जाए. हालांकि, अगर आपको कुछ और टेस्ट या एक्सपेरिमेंट करने हैं, तो App Engine प्लैटफ़ॉर्म पर मुफ़्त कोटा उपलब्ध है. इसलिए, जब तक आप इस्तेमाल की उस सीमा से ज़्यादा नहीं होते हैं, तब तक आपसे कोई शुल्क नहीं लिया जाएगा. यह शुल्क कंप्यूट के लिए है. हालांकि, App Engine की सेवाओं के लिए भी शुल्क लिया जा सकता है. इसलिए, ज़्यादा जानकारी के लिए कीमत वाला पेज देखें. अगर इस माइग्रेशन में अन्य क्लाउड सेवाएं शामिल हैं, तो उनके लिए अलग से बिल भेजा जाता है. अगर लागू हो, तो दोनों ही मामलों में, नीचे दिया गया "इस कोडलैब के लिए खास जानकारी" सेक्शन देखें.

पूरी जानकारी के लिए बता दें कि App Engine जैसे Google Cloud के सर्वरलेस कंप्यूट प्लैटफ़ॉर्म पर डिप्लॉय करने से, बिल्ड और स्टोरेज के लिए मामूली शुल्क लगता है. Cloud Build का अपना मुफ़्त कोटा होता है. साथ ही, Cloud Storage का भी अपना मुफ़्त कोटा होता है. उस इमेज को सेव करने के लिए, स्टोरेज कोटा का कुछ हिस्सा इस्तेमाल किया जाता है. हालांकि, ऐसा हो सकता है कि आपके देश/इलाके में बिना किसी शुल्क के स्टोरेज इस्तेमाल करने की सुविधा उपलब्ध न हो. इसलिए, स्टोरेज के इस्तेमाल पर नज़र रखें, ताकि संभावित लागत को कम किया जा सके. Cloud Storage के कुछ "फ़ोल्डर" की समीक्षा करनी चाहिए. इनमें ये शामिल हैं:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • ऊपर दिए गए स्टोरेज लिंक, आपके PROJECT_ID और *LOC*ation पर निर्भर करते हैं. उदाहरण के लिए, अगर आपका ऐप्लिकेशन अमेरिका में होस्ट किया गया है, तो "us" दिखेगा.

दूसरी ओर, अगर आपको इस ऐप्लिकेशन या माइग्रेशन से जुड़े अन्य कोडलैब का इस्तेमाल नहीं करना है और आपको सब कुछ पूरी तरह से मिटाना है, तो अपना प्रोजेक्ट बंद करें.

इस कोडलैब के लिए खास तौर पर

यहां दी गई सेवाएं, इस कोड सीखने की लैब के लिए खास तौर पर बनाई गई हैं. ज़्यादा जानकारी के लिए, हर प्रॉडक्ट का दस्तावेज़ देखें:

अगले चरण

App Engine Task Queue के पुश टास्क से Cloud Tasks पर माइग्रेट करने की प्रोसेस यहां खत्म होती है. Cloud NDB से Cloud Datastore पर माइग्रेट करने का विकल्प भी तीसरे मॉड्यूल में शामिल है. इसमें Task Queue या Cloud Tasks का इस्तेमाल नहीं किया गया है. तीसरे मॉड्यूल के अलावा, माइग्रेशन के अन्य मॉड्यूल भी उपलब्ध हैं. इनमें App Engine की लेगसी बंडल की गई सेवाओं से दूर जाने पर फ़ोकस किया गया है. इनमें ये शामिल हैं:

App Engine अब Google Cloud में सर्वरलेस प्लैटफ़ॉर्म नहीं है. अगर आपके पास कोई छोटा App Engine ऐप्लिकेशन है या ऐसा ऐप्लिकेशन है जिसमें सीमित सुविधाएं हैं और आपको उसे स्टैंडअलोन माइक्रोसेवा में बदलना है या आपको किसी मोनोलिथिक ऐप्लिकेशन को फिर से इस्तेमाल किए जा सकने वाले कई कॉम्पोनेंट में बांटना है, तो Cloud Functions पर माइग्रेट करने के लिए ये अच्छी वजहें हैं. अगर कंटेनर बनाने की प्रोसेस, ऐप्लिकेशन डेवलपमेंट वर्कफ़्लो का हिस्सा बन गई है, तो Cloud Run पर माइग्रेट करें. खास तौर पर, अगर इसमें सीआई/सीडी (लगातार इंटिग्रेशन/लगातार डिलीवरी या डिप्लॉयमेंट) पाइपलाइन शामिल है. इन स्थितियों के बारे में यहां दिए गए मॉड्यूल में बताया गया है:

  • App Engine से Cloud Functions पर माइग्रेट करना: मॉड्यूल 11 देखें
  • App Engine से Cloud Run पर माइग्रेट करना: Docker की मदद से अपने ऐप्लिकेशन को कंटेनर में बदलने के लिए, मॉड्यूल 4 देखें. इसके अलावा, कंटेनर, Docker की जानकारी या Dockerfile के बिना ऐसा करने के लिए, मॉड्यूल 5 देखें

किसी दूसरे सर्वरलेस प्लैटफ़ॉर्म पर स्विच करना ज़रूरी नहीं है. हमारा सुझाव है कि कोई भी बदलाव करने से पहले, अपने ऐप्लिकेशन और इस्तेमाल के उदाहरणों के लिए सबसे सही विकल्पों पर विचार करें.

अगले माइग्रेशन मॉड्यूल के तौर पर किसी भी मॉड्यूल को चुना जा सकता है. हालांकि, Serverless Migration Station का पूरा कॉन्टेंट (कोड लैब, वीडियो, सोर्स कोड [अगर उपलब्ध हो]) इसके ओपन सोर्स रेपो में ऐक्सेस किया जा सकता है. रेपो के README में यह भी बताया गया है कि किन माइग्रेशन पर विचार करना चाहिए. साथ ही, माइग्रेशन मॉड्यूल के "क्रम" के बारे में भी बताया गया है.

7. अन्य संसाधन

कोडलैब से जुड़ी समस्याएं/सुझाव/राय

अगर आपको इस कोडलैब में कोई समस्या मिलती है, तो कृपया शिकायत दर्ज करने से पहले अपनी समस्या खोजें. नई समस्याएं खोजने और बनाने के लिए लिंक:

माइग्रेशन के लिए उपलब्ध संसाधन

मॉड्यूल 8 (START) और मॉड्यूल 9 (FINISH) के लिए, रेपो फ़ोल्डर के लिंक यहाँ दी गई टेबल में दिए गए हैं. इन्हें App Engine के सभी कोडलैब माइग्रेशन के लिए उपलब्ध repo से भी ऐक्सेस किया जा सकता है. इसे क्लोन किया जा सकता है या इसकी ZIP फ़ाइल डाउनलोड की जा सकती है.

कोडलैब

Python 2

Python 3

मॉड्यूल 8

code

(लागू नहीं)

मॉड्यूल 9

(लागू नहीं)

code

ऑनलाइन संसाधन

यहां कुछ ऑनलाइन संसाधन दिए गए हैं, जो इस ट्यूटोरियल के लिए काम के हो सकते हैं:

App Engine

Cloud NDB

Cloud Datastore

Cloud Tasks

क्लाउड से जुड़ी अन्य जानकारी

लाइसेंस

इस काम के लिए, Creative Commons एट्रिब्यूशन 2.0 जेनेरिक लाइसेंस के तहत लाइसेंस मिला है.