1. ภาพรวม
ชุด Codelab ของ Serverless Migration Station (บทแนะนำแบบลงมือปฏิบัติจริงที่ทำตามได้ด้วยตนเอง) และวิดีโอที่เกี่ยวข้องมีจุดมุ่งหมายเพื่อช่วยให้นักพัฒนาแอป Google Cloud แบบไร้เซิร์ฟเวอร์ปรับปรุงแอปพลิเคชันให้ทันสมัยโดยแนะนำการย้ายข้อมูลอย่างน้อย 1 รายการ ซึ่งส่วนใหญ่เป็นการย้ายข้อมูลออกจากบริการเดิม การทำเช่นนี้จะทำให้แอปของคุณพกพาได้มากขึ้น และช่วยให้คุณมีตัวเลือกและความยืดหยุ่นมากขึ้น ซึ่งจะช่วยให้คุณผสานรวมและเข้าถึงผลิตภัณฑ์ระบบคลาวด์ที่หลากหลายยิ่งขึ้น รวมถึงอัปเกรดเป็นภาษาเวอร์ชันใหม่ๆ ได้ง่ายขึ้น แม้ว่าในตอนแรกจะมุ่งเน้นไปที่ผู้ใช้ Cloud รุ่นแรกๆ ซึ่งส่วนใหญ่เป็นนักพัฒนาซอฟต์แวร์ App Engine (สภาพแวดล้อมมาตรฐาน) แต่ชุดข้อมูลนี้ก็ครอบคลุมแพลตฟอร์มแบบไร้เซิร์ฟเวอร์อื่นๆ เช่น Cloud Functions และ Cloud Run หรือที่อื่นๆ หากเกี่ยวข้อง
วัตถุประสงค์ของ Codelab นี้คือการพอร์ตแอปตัวอย่างของโมดูลที่ 8 ไปยัง Python 3 รวมถึงเปลี่ยนการเข้าถึง Datastore (Cloud Firestore ในโหมด Datastore) จากการใช้ Cloud NDB ไปยังไลบรารีของไคลเอ็นต์ Cloud Datastore ดั้งเดิม และอัปเกรดเป็นไลบรารีของไคลเอ็นต์ Cloud Tasks เวอร์ชันล่าสุด
เราได้เพิ่มการใช้คิวงานสำหรับงาน push ในโมดูลที่ 7 จากนั้นจึงย้ายข้อมูลการใช้งานดังกล่าวไปยัง Cloud Tasks ในโมดูลที่ 8 ในโมดูลที่ 9 นี้ เราจะมาดู Python 3 และ Cloud Datastore กันต่อ ผู้ที่ใช้คิวงานสำหรับงานดึงจะย้ายข้อมูลไปยัง Cloud Pub/Sub และควรดูโมดูลที่ 18-19 แทน
คุณจะได้เรียนรู้วิธีต่อไปนี้
- พอร์ตแอปตัวอย่างของโมดูลที่ 8 เป็น Python 3
- เปลี่ยนการเข้าถึง Datastore จาก Cloud NDB เป็นไลบรารีของไคลเอ็นต์ Cloud Datastore
- อัปเกรดเป็นไลบรารีไคลเอ็นต์ Cloud Tasks เวอร์ชันล่าสุด
สิ่งที่คุณต้องมี
- โปรเจ็กต์ Google Cloud Platform ที่มีบัญชีสำหรับการเรียกเก็บเงินของ GCP ที่ใช้งานอยู่
- ทักษะ Python ขั้นพื้นฐาน
- มีความรู้พื้นฐานเกี่ยวกับคำสั่ง Linux ทั่วไป
- ความรู้พื้นฐานเกี่ยวกับการพัฒนาและการทําให้แอป App Engine ใช้งานได้
- แอป App Engine ของโมดูลที่ 8 ที่ใช้งานได้: ทำตามโค้ดแล็บโมดูลที่ 8 (แนะนำ) หรือคัดลอกแอปโมดูลที่ 8 จากที่เก็บ
แบบสำรวจ
คุณจะใช้บทแนะนำนี้อย่างไร
คุณจะให้คะแนนประสบการณ์การใช้งาน Python เท่าใด
คุณจะให้คะแนนประสบการณ์การใช้บริการ Google Cloud เท่าใด
2. ฉากหลัง
โมดูลที่ 7 แสดงวิธีใช้พุชทาสก์ของคิวงาน App Engine ในแอป App Engine ที่ใช้ Python 2 Flask ในโมดูลที่ 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 (และ/หรือแอป App Engine Python 3) ที่ใช้ไลบรารีของไคลเอ็นต์ Cloud Datastore อยู่แล้ว และต้องการรวมโค้ดเบสเพื่อเข้าถึง Datastore ด้วยไลบรารีของไคลเอ็นต์เพียงรายการเดียว Cloud NDB สร้างขึ้นสำหรับนักพัฒนาแอป App Engine ใน Python 2 โดยเฉพาะเพื่อเป็นเครื่องมือการย้ายข้อมูล Python 3 ดังนั้นหากคุณยังไม่มีโค้ดที่ใช้ไลบรารีของไคลเอ็นต์ Cloud Datastore ก็ไม่จำเป็นต้องพิจารณาการย้ายข้อมูลนี้
สุดท้ายนี้ การพัฒนาไลบรารีของไคลเอ็นต์ Cloud Tasks จะดำเนินการใน Python 3 เท่านั้น ดังนั้นเราจึง "ย้ายข้อมูล" จาก Python 2 เวอร์ชันสุดท้ายเวอร์ชันหนึ่งไปยัง Python 3 เวอร์ชันล่าสุด โชคดีที่ไม่มีการเปลี่ยนแปลงที่ทำให้เกิดข้อขัดข้องจาก Python 2 ซึ่งหมายความว่าคุณไม่จำเป็นต้องดำเนินการใดๆ เพิ่มเติมที่นี่
บทแนะนำนี้มีขั้นตอนต่อไปนี้
- การตั้งค่า/การเตรียมการ
- อัปเดตการกำหนดค่า
- แก้ไขโค้ดแอปพลิเคชัน
3. การตั้งค่า/การเตรียมการ
ส่วนนี้จะอธิบายวิธี
- ตั้งค่าโปรเจ็กต์ Cloud
- รับแอปตัวอย่างพื้นฐาน
- (อีกครั้ง) นำไปใช้งานและตรวจสอบแอปพื้นฐาน
ขั้นตอนเหล่านี้จะช่วยให้คุณเริ่มต้นด้วยโค้ดที่ใช้งานได้และพร้อมสำหรับการย้ายข้อมูลไปยังบริการของ Cloud
1. ตั้งค่าโปรเจ็กต์
หากทำ Codelab โมดูลที่ 8 เสร็จแล้ว ให้ใช้โปรเจ็กต์ (และโค้ด) เดียวกันนั้นซ้ำ หรือจะสร้างโปรเจ็กต์ใหม่หรือนำโปรเจ็กต์อื่นที่มีอยู่มาใช้ซ้ำก็ได้ ตรวจสอบว่าโปรเจ็กต์มีบัญชีสำหรับการเรียกเก็บเงินที่ใช้งานอยู่และแอป App Engine ที่เปิดใช้แล้ว ค้นหารหัสโปรเจ็กต์เนื่องจากคุณจะต้องใช้รหัสนี้ในระหว่าง Codelab นี้ โดยใช้รหัสเมื่อใดก็ตามที่พบตัวแปร PROJECT_ID
2. รับแอปตัวอย่างพื้นฐาน
ข้อกำหนดเบื้องต้นอย่างหนึ่งคือแอป App Engine ของโมดูล 8 ที่ใช้งานได้ ให้ทำ Codelab ของโมดูล 8 (แนะนำ) หรือคัดลอกแอปของโมดูล 8 จากที่เก็บ ไม่ว่าคุณจะใช้โค้ดของคุณเองหรือโค้ดของเรา เราจะเริ่มที่โค้ดของโมดูลที่ 8 ("START") Codelab นี้จะแนะนำขั้นตอนการย้ายข้อมูลให้คุณ โดยจะจบด้วยโค้ดที่คล้ายกับโค้ดในโฟลเดอร์ที่เก็บข้อมูลของโมดูล 9 ("FINISH")
- เริ่มต้น: ที่เก็บโมดูล 8
- เสร็จสิ้น: ที่เก็บโมดูล 9
- ที่เก็บทั้งหมด (โคลนหรือดาวน์โหลด ZIP)
ไม่ว่าคุณจะใช้แอป Module 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รวมถึงได้อ่านการใช้งานแล้ว - (ไม่บังคับ) ตั้งค่าโปรเจ็กต์ Cloud ด้วย
gcloud config set projectPROJECT_IDหากไม่ต้องการป้อนPROJECT_IDกับคำสั่งgcloudแต่ละคำสั่งที่คุณออก - ทำให้แอปตัวอย่างใช้งานได้ด้วย
gcloud app deploy - ยืนยันว่าแอปทำงานได้ตามที่คาดไว้โดยไม่มีปัญหา หากคุณทำ Codelab โมดูลที่ 8 เสร็จแล้ว แอปจะแสดงผู้เข้าชมยอดนิยมพร้อมกับการเข้าชมล่าสุด (ดังภาพด้านล่าง) ที่ด้านล่างจะมีข้อบ่งชี้ของงานเก่ากว่าซึ่งจะถูกลบ

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

ขั้นตอนนี้จะทำให้ Codelab เสร็จสมบูรณ์ เราขอเชิญคุณเปรียบเทียบโค้ดของคุณกับโค้ดในโฟลเดอร์โมดูล 9 ยินดีด้วย
ล้างข้อมูล
ทั่วไป
หากคุณดำเนินการเสร็จแล้วในตอนนี้ เราขอแนะนำให้ปิดใช้แอป App Engine เพื่อไม่ให้มีการเรียกเก็บเงิน อย่างไรก็ตาม หากต้องการทดสอบหรือทดลองเพิ่มเติม แพลตฟอร์ม App Engine มีโควต้าฟรี ดังนั้นตราบใดที่คุณไม่เกินระดับการใช้งานดังกล่าว คุณก็จะไม่ถูกเรียกเก็บเงิน ซึ่งเป็นค่าบริการสำหรับการประมวลผล แต่อาจมีค่าบริการสำหรับบริการ App Engine ที่เกี่ยวข้องด้วย ดังนั้นโปรดดูข้อมูลเพิ่มเติมในหน้าการกำหนดราคา หากการย้ายข้อมูลนี้เกี่ยวข้องกับบริการอื่นๆ ของระบบคลาวด์ ระบบจะเรียกเก็บเงินสำหรับบริการเหล่านั้นแยกต่างหาก ไม่ว่าจะในกรณีใดก็ตาม หากมี ให้ดูส่วน "เฉพาะสำหรับ 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/imagesconsole.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com- ลิงก์พื้นที่เก็บข้อมูลด้านบนจะขึ้นอยู่กับ
PROJECT_IDและ *LOC*ation ของคุณ เช่น "us" หากแอปโฮสต์อยู่ในสหรัฐอเมริกา
ในทางกลับกัน หากคุณจะไม่ดำเนินการต่อกับแอปพลิเคชันนี้หรือ Codelab การย้ายข้อมูลอื่นๆ ที่เกี่ยวข้อง และต้องการลบทุกอย่างออกทั้งหมด ให้ปิดโปรเจ็กต์
เฉพาะสำหรับ Codelab นี้
บริการที่ระบุไว้ด้านล่างเป็นบริการเฉพาะสำหรับโค้ดแล็บนี้ ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบของแต่ละผลิตภัณฑ์
- Cloud Tasks มีระดับฟรี โปรดดูรายละเอียดเพิ่มเติมในหน้าราคา
- บริการ App Engine Datastore ให้บริการโดย Cloud Datastore (Cloud Firestore ในโหมด Datastore) ซึ่งมีระดับฟรีด้วย ดูข้อมูลเพิ่มเติมได้ที่หน้าการกำหนดราคา
ขั้นตอนถัดไป
การย้ายข้อมูลจากงานพุชคิวงานของ App Engine ไปยัง Cloud Tasks ก็มีเพียงเท่านี้ นอกจากนี้ Module 3 ยังครอบคลุมการย้ายข้อมูลจาก Cloud NDB ไปยัง Cloud Datastore (ไม่บังคับ) ด้วย (ไม่มี Task Queue หรือ Cloud Tasks) นอกเหนือจากโมดูลที่ 3 แล้ว ยังมีโมดูลการย้ายข้อมูลอื่นๆ ที่มุ่งเน้นการย้ายออกจากบริการแบบกลุ่มเดิมของ App Engine ที่ควรพิจารณา ได้แก่
- โมดูลที่ 2: ย้ายข้อมูลจาก App Engine NDB ไปยัง Cloud NDB
- โมดูลที่ 3: ย้ายข้อมูลจาก Cloud NDB ไปยัง Cloud Datastore
- โมดูล 12-13: ย้ายข้อมูลจาก Memcache ของ App Engine ไปยัง Cloud Memorystore
- โมดูล 15-16: ย้ายข้อมูลจาก App Engine Blobstore ไปยัง Cloud Storage
- โมดูล 18-19: คิวงานของ App Engine (งานแบบดึง) ไปยัง Cloud Pub/Sub
App Engine ไม่ใช่แพลตฟอร์มแบบไร้เซิร์ฟเวอร์เพียงอย่างเดียวใน Google Cloud อีกต่อไป หากคุณมีแอป App Engine ขนาดเล็กหรือแอปที่มีฟังก์ชันการทำงานจำกัด และต้องการเปลี่ยนให้เป็นไมโครเซอร์วิสแบบสแตนด์อโลน หรือต้องการแยกแอปแบบ Monolithic ออกเป็นคอมโพเนนต์ที่นำกลับมาใช้ใหม่ได้หลายรายการ นี่เป็นเหตุผลที่ดีที่ควรพิจารณาเปลี่ยนไปใช้ Cloud Functions หากการใช้คอนเทนเนอร์กลายเป็นส่วนหนึ่งของเวิร์กโฟลว์การพัฒนาแอปพลิเคชัน โดยเฉพาะอย่างยิ่งหากประกอบด้วยไปป์ไลน์ CI/CD (การผสานรวมอย่างต่อเนื่อง/การนำส่งหรือการติดตั้งใช้งานอย่างต่อเนื่อง) ให้พิจารณาการย้ายข้อมูลไปยัง Cloud Run สถานการณ์เหล่านี้จะครอบคลุมในโมดูลต่อไปนี้
- ย้ายข้อมูลจาก App Engine ไปยัง Cloud Functions: ดูโมดูลที่ 11
- ย้ายข้อมูลจาก App Engine ไปยัง Cloud Run: ดูโมดูลที่ 4 เพื่อจัดคอนเทนเนอร์แอปด้วย Docker หรือโมดูลที่ 5 เพื่อดำเนินการโดยไม่ต้องใช้คอนเทนเนอร์ ความรู้เกี่ยวกับ Docker หรือ
Dockerfile
การเปลี่ยนไปใช้แพลตฟอร์มแบบไม่ใช้เซิร์ฟเวอร์อื่นเป็นทางเลือก และเราขอแนะนำให้พิจารณาตัวเลือกที่ดีที่สุดสำหรับแอปและ Use Case ของคุณก่อนทำการเปลี่ยนแปลงใดๆ
ไม่ว่าคุณจะพิจารณาโมดูลการย้ายข้อมูลใดเป็นโมดูลถัดไป คุณจะเข้าถึงเนื้อหาของ Serverless Migration Station ทั้งหมด (Codelab, วิดีโอ, ซอร์สโค้ด [หากมี]) ได้ที่ที่เก็บแบบโอเพนซอร์ส README ของที่เก็บยังให้คำแนะนำเกี่ยวกับการย้ายข้อมูลที่ควรพิจารณาและ "ลำดับ" ที่เกี่ยวข้องของโมดูลการย้ายข้อมูลด้วย
7. แหล่งข้อมูลเพิ่มเติม
ปัญหา/ความคิดเห็นเกี่ยวกับ Codelabs
หากพบปัญหาเกี่ยวกับ Codelab นี้ โปรดค้นหาปัญหาของคุณก่อนที่จะยื่นเรื่อง ลิงก์สำหรับค้นหาและสร้างปัญหาใหม่
แหล่งข้อมูลเกี่ยวกับการย้ายข้อมูล
คุณจะดูลิงก์ไปยังโฟลเดอร์ที่เก็บสำหรับโมดูลที่ 8 (START) และโมดูลที่ 9 (FINISH) ได้ในตารางด้านล่าง นอกจากนี้ คุณยังเข้าถึงได้จากที่เก็บสำหรับการย้ายข้อมูล Codelab ของ App Engine ทั้งหมด ซึ่งคุณสามารถโคลนหรือดาวน์โหลดไฟล์ ZIP ได้
Codelab | Python 2 | Python 3 |
(ไม่มี) | ||
โมดูล 9 | (ไม่มี) |
แหล่งข้อมูลออนไลน์
ด้านล่างนี้คือแหล่งข้อมูลออนไลน์ที่อาจเกี่ยวข้องกับบทแนะนำนี้
App Engine
- เอกสารประกอบของ App Engine
- รันไทม์ของ App Engine (สภาพแวดล้อมมาตรฐาน) สำหรับ Python 2
- รันไทม์ Python 3 App Engine (สภาพแวดล้อมมาตรฐาน)
- ความแตกต่างระหว่างรันไทม์ของ Python 2 และ 3 ใน App Engine (สภาพแวดล้อมมาตรฐาน)
- คู่มือการย้ายข้อมูลจาก Python 2 ไปยัง 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
ใบอนุญาต
ผลงานนี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์สำหรับยอมรับสิทธิของผู้สร้าง (Creative Commons Attribution License) 2.0 แบบทั่วไป