ย้ายข้อมูล Python 2 App Engine Cloud NDB และ แอป Cloud Tasks ไปยัง Python 3 และ Cloud Datastore (โมดูล 9)

1. ภาพรวม

ชุดโปรแกรม Codelab สำหรับการย้ายข้อมูลแบบ Serverless (บทแนะนำแบบลงมือทำด้วยตนเอง) และวิดีโอที่เกี่ยวข้องมีจุดประสงค์เพื่อช่วยให้นักพัฒนาแอป Google Cloud Serverless ปรับการดำเนินการให้ทันสมัยได้ด้วยคำแนะนำการย้ายข้อมูลอย่างน้อย 1 รายการ โดยให้ย้ายออกจากบริการเดิมเป็นหลัก การดำเนินการดังกล่าวทำให้แอปพกพาไปได้ทุกที่ รวมถึงมอบตัวเลือกและความยืดหยุ่นที่มากขึ้น ทำให้สามารถผสานรวมและเข้าถึงผลิตภัณฑ์ Cloud ที่หลากหลายยิ่งขึ้น และอัปเกรดเป็นรุ่นภาษาใหม่ๆ ได้ง่ายยิ่งขึ้น แม้ว่าในช่วงแรกจะมุ่งเน้นที่ผู้ใช้ Cloud รุ่นแรกสุด ซึ่งเป็นนักพัฒนา App Engine (สภาพแวดล้อมมาตรฐาน) เป็นหลัก แต่ชุดโซลูชันนี้ก็กว้างพอที่จะรวมแพลตฟอร์มแบบ Serverless อื่นๆ เช่น Cloud Functions และ Cloud Run หรือแพลตฟอร์มอื่นๆ ที่เกี่ยวข้อง

วัตถุประสงค์ของ Codelab นี้คือพอร์ตแอปตัวอย่างโมดูล 8 ไปยัง Python 3 รวมถึงเปลี่ยนการเข้าถึง Datastore (Cloud Firestore ในโหมด Datastore) จากการใช้ Cloud NDB เป็นไลบรารีของไคลเอ็นต์ Cloud Datastore แบบดั้งเดิม และอัปเกรดเป็นไลบรารีไคลเอ็นต์ Cloud Tasks เวอร์ชันล่าสุด

เราได้เพิ่มการใช้คิวงานสำหรับงานพุชในโมดูล 7 จากนั้นจึงย้ายข้อมูลการใช้งานดังกล่าวไปยัง Cloud Tasks ในโมดูล 8 ในโมดูล 9 เราจะไปต่อที่ Python 3 และ Cloud Datastore ผู้ที่ใช้คิวงานสำหรับงานพุลจะย้ายข้อมูลไปยัง Cloud Pub/Sub และควรอ้างอิงโมดูล 18-19 แทน

คุณจะได้เรียนรู้วิธีต่อไปนี้

  • พอร์ตแอปโมดูล 8 ตัวอย่างไปยัง Python 3
  • เปลี่ยนการเข้าถึง Datastore จาก Cloud NDB เป็นไลบรารีไคลเอ็นต์ Cloud Datastore
  • อัปเกรดเป็นไลบรารีของไคลเอ็นต์ Cloud Tasks เวอร์ชันล่าสุด

สิ่งที่ต้องมี

แบบสำรวจ

คุณจะใช้บทแนะนำนี้อย่างไร

โปรดอ่านเท่านั้น อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์การใช้งาน Python อย่างไร

มือใหม่ ระดับกลาง ผู้ชำนาญ

คุณจะให้คะแนนความพึงพอใจในการใช้บริการ Google Cloud อย่างไร

มือใหม่ ระดับกลาง ผู้ชำนาญ

2. ข้อมูลเบื้องต้น

โมดูล 7 สาธิตวิธีใช้งานพุชคิวงานของ App Engine ในแอป Python 2 Flask App Engine ในโมดูล 8 คุณจะย้ายข้อมูลแอปดังกล่าวจากคิวงานไปยัง 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 อยู่แล้ว และต้องการรวมฐานของ Codebase ของคุณเพื่อเข้าถึง Datastore ด้วยไลบรารีของไคลเอ็นต์เพียงรายการเดียว Cloud NDB สร้างขึ้นสำหรับนักพัฒนาซอฟต์แวร์ Python 2 App Engine โดยเฉพาะ เพื่อให้เป็นเครื่องมือย้ายข้อมูล Python 3 ดังนั้น หากคุณยังไม่มีโค้ดที่ใช้ไลบรารีไคลเอ็นต์ Cloud Datastore คุณก็ไม่ต้องพิจารณาการย้ายข้อมูลนี้

สุดท้าย การพัฒนาไลบรารีของไคลเอ็นต์ Cloud Tasks จะดำเนินต่อไปใน Python 3 เท่านั้น ดังนั้นเราจึงทำการ "ย้ายข้อมูล" จาก Python 2 เวอร์ชันสุดท้ายสู่ Python 3 ร่วมสมัย โชคดีที่ไม่มีการเปลี่ยนแปลงที่ส่งผลกับส่วนอื่นในระบบจาก Python 2 ซึ่งหมายความว่าคุณไม่ต้องดำเนินการใดๆ เพิ่มเติม

บทแนะนำนี้มีขั้นตอนต่อไปนี้

  1. การตั้งค่า/งานล่วงหน้า
  2. อัปเดตการกำหนดค่า
  3. แก้ไขโค้ดของแอปพลิเคชัน

3. การตั้งค่า/งานล่วงหน้า

ส่วนนี้จะอธิบายวิธี:

  1. ตั้งค่าโปรเจ็กต์ที่อยู่ในระบบคลาวด์
  2. รับแอปตัวอย่างพื้นฐาน
  3. (อีกครั้ง) ติดตั้งใช้งานและตรวจสอบแอปพื้นฐาน

ขั้นตอนเหล่านี้ช่วยให้มั่นใจว่าคุณเริ่มต้นด้วยโค้ดที่ใช้งานได้และพร้อมสำหรับการย้ายข้อมูลไปยังบริการระบบคลาวด์

1. สร้างโปรเจ็กต์

หากคุณทำ Codelab ของโมดูล 8 เสร็จแล้ว ให้ใช้โปรเจ็กต์ (และโค้ด) เดียวกันนั้นซ้ำ หรือสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์อื่นที่มีอยู่ซ้ำ ตรวจสอบว่าโปรเจ็กต์มีบัญชีสำหรับการเรียกเก็บเงินที่ใช้งานอยู่และแอป App Engine ที่เปิดใช้แล้ว หารหัสโปรเจ็กต์ตามความต้องการเพื่อนำไปใช้ในระหว่าง Codelab นี้ โดยใช้เมื่อใดก็ตามที่คุณพบตัวแปร PROJECT_ID

2. รับแอปตัวอย่างพื้นฐาน

ข้อกำหนดเบื้องต้นอย่างหนึ่งคือแอป App Engine โมดูล 8 ที่ใช้งานได้ กล่าวคือ ทำ Codelab ของโมดูล 8 ให้เสร็จสิ้น (แนะนำ) หรือคัดลอกแอปโมดูล 8 จากที่เก็บ ไม่ว่าคุณจะใช้โค้ดของคุณเองหรือของเรา โค้ดโมดูล 8 คือส่วนที่เราจะเริ่มต้น ("START") Codelab นี้จะแนะนำการย้ายข้อมูล สรุปด้วยโค้ดที่คล้ายคลึงกับสิ่งที่อยู่ในโฟลเดอร์ที่เก็บของโมดูล 9 ("FINISH")

ไม่ว่าคุณจะใช้แอป Module 7 ใด โฟลเดอร์ควรมีลักษณะตามด้านล่าง ซึ่งอาจมีโฟลเดอร์ lib ด้วยเช่นกัน

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

3. (อีกครั้ง) ติดตั้งใช้งานและตรวจสอบแอปพื้นฐาน

ดำเนินการตามขั้นตอนต่อไปนี้เพื่อทำให้แอปโมดูล 8 ใช้งานได้

  1. ลบโฟลเดอร์ lib หากมี แล้วเรียกใช้ pip install -t lib -r requirements.txt เพื่อป้อนข้อมูล lib ใหม่ คุณอาจต้องใช้ pip2 แทน หากติดตั้งทั้ง Python 2 และ 3 ไว้ในเครื่องการพัฒนาแล้ว
  2. ตรวจสอบว่าคุณได้ติดตั้งและเริ่มต้นเครื่องมือบรรทัดคำสั่ง gcloud และตรวจสอบการใช้งานแล้ว
  3. (ไม่บังคับ) ให้ตั้งค่าโปรเจ็กต์ที่อยู่ในระบบคลาวด์ด้วย gcloud config set project PROJECT_ID หากไม่ต้องการป้อน PROJECT_ID ด้วยคำสั่ง gcloud แต่ละรายการที่คุณออก
  4. ทำให้แอปตัวอย่างใช้งานได้ด้วย gcloud app deploy
  5. ยืนยันว่าแอปทำงานตามที่คาดไว้โดยไม่มีปัญหา หากคุณทำ Codelab ของโมดูล 8 เสร็จสมบูรณ์แล้ว แอปจะแสดงผู้เข้าชมอันดับสูงสุดพร้อมกับการเข้าชมล่าสุด (ภาพด้านล่าง) ที่ด้านล่างจะบ่งบอกถึงงานเก่าที่จะถูกลบ

4aa8a2cb5f527079.png

4. อัปเดตการกำหนดค่า

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 ไม่รองรับไลบรารีของบุคคลที่สามแบบบิวท์อิน เช่น เวอร์ชัน 2.x และไม่รองรับการคัดลอกไลบรารีที่ไม่มีในตัว ข้อกำหนดเพียงอย่างเดียวสำหรับแพ็กเกจของบุคคลที่สามคือการแสดงแพ็กเกจเหล่านั้นใน requirements.txt ด้วยเหตุนี้คุณลบส่วน libraries ทั้งหมดของ app.yaml ได้

อีกการอัปเดตหนึ่งคือรันไทม์ของ 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++ ที่นักพัฒนาซอฟต์แวร์ไม่ได้รับอนุญาตให้ทำให้ใช้งานได้ในระบบคลาวด์ เนื่องจากไลบรารีเหล่านี้ไม่พร้อมใช้งานในรันไทม์รุ่นที่ 2 อีกต่อไป
  • ไม่จำเป็นต้องคัดลอกไลบรารีที่ไม่มีในตัว (บางครั้งเรียกว่า "ผู้ให้บริการ" หรือ "การรวมกลุ่มอีเมลด้วยตนเอง") ในรันไทม์รุ่นที่ 2 อีกต่อไป แต่ควรแสดงอยู่ใน requirements.txt ซึ่งระบบบิลด์จะติดตั้งในนามของคุณโดยอัตโนมัติเมื่อถึงเวลาทำให้ใช้งานได้

การเปลี่ยนแปลงการจัดการแพ็กเกจของบุคคลที่สามทำให้ไม่จำเป็นต้องใช้ไฟล์ appengine_config.py และโฟลเดอร์ lib ให้ลบออก ในรันไทม์รุ่นที่ 2 App Engine จะติดตั้งแพ็กเกจของบุคคลที่สามที่แสดงอยู่ใน requirements.txt โดยอัตโนมัติ สรุป:

  1. ไม่มีไลบรารีของบุคคลที่สามที่จัดกลุ่มไว้เองหรือคัดลอกมา แสดงรายการใน requirements.txt
  2. ไม่มี pip install ในโฟลเดอร์ lib ซึ่งหมายความว่าไม่มีเครื่องหมายจุด lib ของโฟลเดอร์
  3. ไม่แสดงข้อมูลไลบรารีของบุคคลที่สามในตัว (จึงไม่มีส่วน libraries) ใน app.yaml แสดงรายการใน requirements.txt
  4. การไม่มีไลบรารีของบุคคลที่สามให้อ้างอิงจากแอปหมายความว่าไม่มีไฟล์ appengine_config.py

ข้อกำหนดเพียงอย่างเดียวสำหรับนักพัฒนาซอฟต์แวร์คือการระบุรายการไลบรารีของบุคคลที่สามที่ต้องการใน requirements.txt

5. อัปเดตไฟล์แอปพลิเคชัน

มีไฟล์แอปพลิเคชันเพียงไฟล์เดียว ซึ่งก็คือ main.py ดังนั้นการเปลี่ยนแปลงทั้งหมดในส่วนนี้จึงมีผลกับไฟล์นั้นเท่านั้น ด้านล่างนี้เป็น "ความแตกต่าง" ภาพการเปลี่ยนแปลงโดยรวมที่ต้องทำเพื่อเปลี่ยนโครงสร้างโค้ดที่มีอยู่ไปไว้ในแอปใหม่ ผู้อ่านไม่ควรอ่านโค้ดทีละบรรทัด เนื่องจากจุดประสงค์ของผู้อ่านคือเพื่อดูภาพภาพรวมของสิ่งที่จำเป็นต้องเปลี่ยนโครงสร้าง (แต่สามารถเปิดในแท็บใหม่หรือดาวน์โหลดและซูมเข้าได้หากต้องการ)

5d043768ba7be742.png

อัปเดตการนำเข้าและการเริ่มต้น

ส่วนการนำเข้าใน 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()

การบันทึกจะลดความซับซ้อนและปรับปรุงขึ้นในรันไทม์รุ่นที่ 2 เช่น Python 3 ดังนี้

  • โปรดใช้ Cloud Logging เพื่อประสบการณ์การบันทึกที่ครอบคลุม
  • สำหรับการบันทึกอย่างง่าย เพียงส่งไปยัง stdout (หรือ stderr) ผ่าน print()
  • ไม่จำเป็นต้องใช้โมดูล Python logging (โปรดนำออก)

ดังนั้น ให้ลบการนำเข้า 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 และ User Agent ของไคลเอ็นต์ที่เข้าชม (ประเภทเบราว์เซอร์)

ก่อนหน้า:

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() เครื่องมือนี้ไม่เพียงแค่ทำการค้นหาต้นฉบับสำหรับ Visit ล่าสุดเท่านั้น แต่ยังจับการประทับเวลาของ Visit ล่าสุดที่แสดงและสร้างงานพุชที่เรียกใช้ /trim (เช่น trim()) เพื่อลบ Visit เก่าๆ เป็นจำนวนมาก ซึ่งกำลังใช้ 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

การเปลี่ยนแปลงหลักๆ มีดังนี้

  1. สลับการค้นหา Cloud NDB เป็น Cloud Datastore ที่เทียบเท่า รูปแบบคำค้นหาจะแตกต่างกันเล็กน้อย
  2. Datastore ไม่จำเป็นต้องใช้เครื่องมือจัดการบริบท และทำให้คุณแยกข้อมูลของ (ด้วย to_dict()) ได้เช่นเดียวกับ Cloud NDB
  3. แทนที่การบันทึกการโทรด้วย 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

ซึ่งโดยปกติจะเป็นสิ่งที่จำเป็น ขออภัย มีปัญหาร้ายแรง 1 ข้อ

(อาจ) สร้างคิว (พุช) ใหม่

ในโมดูล 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

แอปตัวอย่างนี้ออกแบบมาให้ทำงานทั้งใน Python 2 และ 3 เราได้อธิบายการเปลี่ยนแปลงสำหรับ Python 3 โดยเฉพาะไว้ก่อนหน้านี้ในส่วนที่เกี่ยวข้องของบทแนะนำนี้ ไม่ต้องมีขั้นตอนเพิ่มเติมหรือไลบรารีความเข้ากันได้

ข้อมูลอัปเดตเกี่ยวกับงานระบบคลาวด์

ไลบรารีของไคลเอ็นต์ 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 และได้อัปเกรดเป็น Python 3 แล้ว

แอปการเข้าชมโมดูล 7

ขั้นตอนนี้จะทำให้ Codelab เสร็จสมบูรณ์ ขอเชิญให้คุณเปรียบเทียบโค้ดกับสิ่งที่อยู่ในโฟลเดอร์โมดูล 9 ยินดีด้วย

ล้างข้อมูล

ทั่วไป

หากดำเนินการเสร็จแล้ว เราขอแนะนำให้คุณปิดใช้แอป App Engine เพื่อหลีกเลี่ยงการเรียกเก็บเงิน อย่างไรก็ตาม หากคุณต้องการทดสอบหรือทดลองเพิ่มเติม แพลตฟอร์ม App Engine จะมีโควต้าฟรี และตราบใดที่คุณใช้งานไม่เกินระดับการใช้งานดังกล่าว เราก็จะไม่เรียกเก็บเงิน ค่าดังกล่าวมีไว้สําหรับการประมวลผล แต่ก็อาจมีการเรียกเก็บเงินค่าบริการ App Engine ที่เกี่ยวข้องด้วย ดังนั้นโปรดดูข้อมูลเพิ่มเติมในหน้าราคา หากการย้ายข้อมูลนี้เกี่ยวข้องกับบริการระบบคลาวด์อื่นๆ ระบบจะเรียกเก็บเงินแยกต่างหาก ในทั้ง 2 กรณี หากมี โปรดดูส่วน "เฉพาะสำหรับ Codelab นี้" ด้านล่าง

เพื่อการเปิดเผยข้อมูลทั้งหมด การทำให้ใช้งานได้กับแพลตฟอร์มการประมวลผลแบบ Serverless ของ Google Cloud อย่าง App Engine จะมีค่าใช้จ่ายในการสร้างและพื้นที่เก็บข้อมูลเล็กน้อย 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*ของคุณ เช่น "us" หากแอปของคุณโฮสต์ในสหรัฐอเมริกา

ในทางกลับกัน หากคุณไม่ต้องการดำเนินการกับแอปพลิเคชันนี้หรือ Codelab การย้ายข้อมูลอื่นๆ ที่เกี่ยวข้องต่อไป และต้องการลบทุกอย่างออกทั้งหมด ให้ปิดโปรเจ็กต์

เฉพาะสำหรับ Codelab นี้

บริการในรายการด้านล่างเป็นบริการเฉพาะสำหรับ Codelab นี้ โปรดดูข้อมูลเพิ่มเติมในเอกสารประกอบของผลิตภัณฑ์แต่ละรายการ

  • Cloud Tasks มีรุ่นฟรี ดูรายละเอียดเพิ่มเติมในหน้าราคา
  • บริการ App Engine Datastore ให้บริการโดย Cloud Datastore (Cloud Firestore ในโหมด Datastore) ซึ่งมีรุ่นฟรีเช่นกัน ดูข้อมูลเพิ่มเติมได้ที่หน้าราคา

ขั้นตอนถัดไป

การย้ายข้อมูลจากคิวงานของ App Engine จะพุชงานไปยัง Cloud Tasks เป็นการสรุปข้อมูล นอกจากนี้ การย้ายข้อมูลที่ไม่บังคับจาก Cloud NDB ไปยัง Cloud Datastore ยังครอบคลุมอยู่ในโมดูล 3 ด้วย (ไม่มีคิวงานหรือ Cloud Tasks) นอกเหนือจากโมดูล 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 (พุลงาน) ไปยัง Cloud Pub/Sub

App Engine ไม่ใช่แพลตฟอร์มแบบ Serverless เพียงแพลตฟอร์มเดียวใน Google Cloud อีกต่อไป หากคุณมีแอป App Engine ขนาดเล็กหรือแอปที่มีฟังก์ชันการทำงานที่จำกัดและต้องการเปลี่ยนเป็น Microservice แบบสแตนด์อโลน หรือต้องการแตกแอปโมโนลิธให้เป็นคอมโพเนนต์ที่ใช้ซ้ำได้หลายรายการ เหตุผลที่ดีเหล่านี้ควรพิจารณาเปลี่ยนไปใช้ Cloud Functions หากการขนส่งด้วยคอนเทนเนอร์เป็นส่วนหนึ่งของเวิร์กโฟลว์การพัฒนาแอปพลิเคชัน โดยเฉพาะอย่างยิ่งหากประกอบด้วยไปป์ไลน์ CI/CD (การรวมอย่างต่อเนื่อง/การส่งมอบหรือการติดตั้งใช้งานอย่างต่อเนื่อง) ให้ลองย้ายข้อมูลไปยัง Cloud Run สถานการณ์เหล่านี้ครอบคลุมในโมดูลต่อไปนี้

  • ย้ายข้อมูลจาก App Engine ไปยัง Cloud Functions: ดูโมดูล 11
  • ย้ายข้อมูลจาก App Engine ไปยัง Cloud Run: ดูโมดูล 4 เพื่อสร้างคอนเทนเนอร์แอปด้วย Docker หรือโมดูล 5 เพื่อดำเนินการดังกล่าวโดยไม่ต้องมีคอนเทนเนอร์ ไม่มีความรู้เกี่ยวกับ Docker หรือDockerfile

คุณจะเปลี่ยนไปใช้แพลตฟอร์มแบบ Serverless อื่นหรือไม่ก็ได้ และเราขอแนะนำให้พิจารณาตัวเลือกที่ดีที่สุดสำหรับแอปและ Use Case ของคุณก่อนทำการเปลี่ยนแปลงใดๆ

ไม่ว่าคุณจะพิจารณาโมดูลการย้ายข้อมูลใดในครั้งถัดไป คุณสามารถเข้าถึงเนื้อหาของสถานีย้ายข้อมูลแบบ Serverless (โค้ดแล็บ วิดีโอ ซอร์สโค้ด [เมื่อพร้อมให้บริการ]) ทั้งหมดได้ที่ที่เก็บโอเพนซอร์ส README ของที่เก็บยังให้คำแนะนำเกี่ยวกับการย้ายข้อมูลที่ควรพิจารณาและ "คำสั่ง" ที่เกี่ยวข้อง โมดูลการย้ายข้อมูล

7. แหล่งข้อมูลเพิ่มเติม

ปัญหา/ความคิดเห็นของ Codelab

หากมีปัญหาใดๆ เกี่ยวกับ Codelab นี้ โปรดค้นหาปัญหาของคุณก่อนยื่น ลิงก์สำหรับค้นหาและสร้างปัญหาใหม่

ทรัพยากรการย้ายข้อมูล

คุณสามารถดูลิงก์ไปยังโฟลเดอร์ที่เก็บสำหรับโมดูล 8 (START) และโมดูล 9 (FINISH) ได้ในตารางด้านล่าง นอกจากนี้ยังเข้าถึงได้จากที่เก็บสำหรับการย้ายข้อมูล Codelab ทั้งหมดของ App Engine ซึ่งจะโคลนหรือดาวน์โหลดไฟล์ ZIP ได้

Codelab

Python 2

Python 3

โมดูล 8

รหัส

(ไม่มี)

โมดูล 9

(ไม่มี)

รหัส

แหล่งข้อมูลออนไลน์

ด้านล่างนี้คือแหล่งข้อมูลออนไลน์ที่อาจเกี่ยวข้องกับบทแนะนำนี้

App Engine

NDB ระบบคลาวด์

Cloud Datastore

Cloud Tasks

ข้อมูลอื่นๆ เกี่ยวกับระบบคลาวด์

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้ใบอนุญาตทั่วไปครีเอทีฟคอมมอนส์แบบระบุแหล่งที่มา 2.0