Di chuyển từ App Engine Blobstore sang Cloud Storage (Mô-đun 16)

1. Tổng quan

Loạt lớp học lập trình về Trạm di chuyển không máy chủ (hướng dẫn thực hành theo tiến độ riêng) và video có liên quan nhằm giúp các nhà phát triển Google Cloud không máy chủ không hiện đại hoá các ứng dụng của họ bằng cách hướng dẫn họ thực hiện một hoặc nhiều quá trình di chuyển, chủ yếu là ngừng sử dụng các dịch vụ cũ. Việc này giúp ứng dụng của bạn dễ di chuyển hơn, đồng thời mang đến cho bạn nhiều lựa chọn và độ linh hoạt hơn, cho phép bạn tích hợp và truy cập vào nhiều sản phẩm của Cloud hơn, đồng thời dễ dàng nâng cấp lên bản phát hành ngôn ngữ mới hơn. Mặc dù ban đầu tập trung vào những người dùng Cloud sớm nhất, chủ yếu là các nhà phát triển App Engine (môi trường tiêu chuẩn), nhưng loạt bài này đủ rộng để bao gồm các nền tảng không máy chủ khác như Cloud FunctionsCloud Run hoặc ở những nơi khác nếu có.

Lớp học lập trình này hướng dẫn bạn cách di chuyển từ App Engine Blobstore sang Cloud Storage. Ngoài ra còn có quá trình di chuyển ngầm định từ:

Hãy tham khảo bất kỳ mô-đun di chuyển nào có liên quan để biết thêm thông tin từng bước.

Bạn sẽ tìm hiểu cách

  • Thêm việc sử dụng API/thư viện App Engine Blobstore
  • Lưu trữ nội dung người dùng tải lên dịch vụ Blobstore
  • Chuẩn bị cho bước tiếp theo để di chuyển sang Cloud Storage

Bạn cần có

Khảo sát

Bạn sẽ sử dụng hướng dẫn này như thế nào?

Chỉ có thể đọc Đọc và hoàn thành bài tập

Bạn đánh giá thế nào về trải nghiệm sử dụng Python?

Người mới tập Trung cấp Thành thạo

Bạn đánh giá thế nào về trải nghiệm sử dụng các dịch vụ của Google Cloud?

Người mới tập Trung cấp Thành thạo

2. Thông tin khái quát

Lớp học lập trình này bắt đầu bằng ứng dụng mẫu trong Mô-đun 15 và trình bày cách di chuyển từ Blobstore (và NDB) sang Cloud Storage (và Cloud NDB). Quá trình di chuyển bao gồm việc thay thế các phần phụ thuộc trên các dịch vụ đi kèm cũ của App Engine, cho phép bạn di chuyển ứng dụng của mình sang một nền tảng đám mây không máy chủ khác hoặc nền tảng lưu trữ khác nếu muốn.

Quá trình di chuyển này đòi hỏi nhiều nỗ lực hơn một chút so với các quá trình di chuyển khác trong loạt hướng dẫn này. Blobstore có các phần phụ thuộc trên khung webapp ban đầu và đó là lý do ứng dụng mẫu sử dụng khung webapp2 thay vì Flask. Hướng dẫn này mô tả việc di chuyển sang Cloud Storage, Cloud NDB, Flask và Python 3.

Ứng dụng vẫn ghi lại "lượt truy cập" của người dùng cuối và hiển thị 10 cấu phần phần mềm gần đây nhất, nhưng lớp học lập trình trước đó (Mô-đun 15) đã thêm chức năng mới để phù hợp với việc sử dụng Blobstore: ứng dụng nhắc người dùng cuối tải một cấu phần phần mềm lên (một tệp) tương ứng với "lượt truy cập" của họ. Người dùng có thể làm như vậy hoặc chọn "bỏ qua" chọn không tham gia. Bất kể người dùng quyết định thế nào, trang tiếp theo đều hiển thị cùng một kết quả với các phiên bản trước đó của ứng dụng và cho thấy những lượt truy cập gần đây nhất. Một điểm khác biệt nữa là các lượt truy cập chứa cấu phần phần mềm tương ứng sẽ có một "chế độ xem" để hiển thị cấu phần phần mềm của lượt truy cập. Lớp học lập trình này sẽ triển khai quá trình di chuyển nêu trên mà vẫn giữ nguyên chức năng được mô tả.

3. Thiết lập/Chuẩn bị

Trước khi chuyển sang phần chính của hướng dẫn, hãy thiết lập dự án, lấy mã, sau đó triển khai ứng dụng cơ sở để biết rằng mình đã bắt đầu làm việc với mã nguồn.

1. Thiết lập dự án

Nếu đã triển khai ứng dụng Mô-đun 15, bạn nên sử dụng lại chính dự án (và mã) đó. Ngoài ra, bạn có thể tạo một dự án hoàn toàn mới hoặc sử dụng lại một dự án hiện có khác. Đảm bảo dự án này có một tài khoản thanh toán đang hoạt động và đã bật App Engine.

2. Tải ứng dụng mẫu cơ sở

Một trong những điều kiện tiên quyết của lớp học lập trình này là phải có một ứng dụng mẫu của Mô-đun 15 hoạt động được. Nếu chưa có, bạn có thể lấy trong Học phần 15 "BẮT ĐẦU" thư mục (đường liên kết bên dưới). Lớp học lập trình này sẽ hướng dẫn bạn qua từng bước, kết thúc bằng mã tương tự như trong Mô-đun 16 " hướng dẫn hoàn thiện" .

Thư mục của các tệp BẮT ĐẦU Mô-đun 15 sẽ có dạng như sau:

$ ls
README.md       app.yaml        main-gcs.py     main.py         templates

Tệp main-gcs.py là một phiên bản thay thế của main.py trong Mô-đun 15, cho phép lựa chọn một bộ chứa trên Cloud Storage khác với giá trị mặc định của URL được chỉ định của một ứng dụng dựa trên mã của dự án: PROJECT_ID.appspot.com. Ngoài các kỹ thuật di chuyển tương tự, tệp này không đóng vai trò gì trong lớp học lập trình này (Mô-đun 16) cho tệp đó nếu muốn.

3. (Triển khai lại) ứng dụng cơ sở

Các bước chuẩn bị còn lại để thực hiện ngay:

  1. Làm quen lại bằng công cụ dòng lệnh gcloud
  2. Triển khai lại ứng dụng mẫu bằng gcloud app deploy
  3. Xác nhận rằng ứng dụng chạy trên App Engine mà không gặp vấn đề nào

Sau khi thực hiện thành công các bước đó và xác nhận rằng ứng dụng Mô-đun 15 của bạn hoạt động. Trang đầu tiên chào người dùng bằng một biểu mẫu nhắc họ tải tệp cấu phần phần mềm về việc truy cập lên cùng với lựa chọn "bỏ qua" để chọn không tham gia:

f5b5f9f19d8ae978.png

Sau khi người dùng tải một tệp lên hoặc bỏ qua, ứng dụng này sẽ hiển thị các thông tin quen thuộc như "lượt truy cập gần đây nhất" trang:

f5ac6b98ee8a34cb.png

Các lượt truy cập giới thiệu cấu phần phần mềm sẽ có một "lượt xem" ở bên phải dấu thời gian truy cập để hiển thị (hoặc tải xuống) cấu phần phần mềm. Sau khi xác nhận chức năng của ứng dụng, bạn đã sẵn sàng di chuyển từ các dịch vụ cũ của App Engine (webapp2, NDB, Blobstore) sang các dịch vụ thay thế hiện đại (Flask, Cloud NDB, Cloud Storage).

4. Cập nhật tệp cấu hình

Ba tệp cấu hình được đưa vào sử dụng cho phiên bản cập nhật của ứng dụng. Các tác vụ bắt buộc bao gồm:

  1. Cập nhật các thư viện tích hợp sẵn bắt buộc của bên thứ ba trong app.yaml cũng như để cơ hội di chuyển sang Python 3
  2. Thêm requirements.txt để chỉ định tất cả thư viện bắt buộc chưa được tích hợp sẵn
  3. Thêm appengine_config.py để ứng dụng hỗ trợ cả thư viện bên thứ ba tích hợp sẵn và không tích hợp

app.yaml

Chỉnh sửa tệp app.yaml bằng cách cập nhật phần libraries. Xoá jinja2 và thêm grpcio, setuptoolsssl. Chọn phiên bản mới nhất có sẵn cho cả 3 thư viện. Ngoài ra, hãy thêm lệnh runtime của Python 3, nhưng đã nhận xét. Khi bạn hoàn tất, nó sẽ có dạng như sau (nếu bạn chọn Python 3.9):

TRƯỚC KHI:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: jinja2
  version: latest

SAU KHI:

#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

Thay đổi này chủ yếu xử lý các thư viện tích hợp sẵn Python 2 có trên các máy chủ App Engine (vì vậy, bạn không phải tự nhóm chúng). Chúng tôi đã xoá Jinja2 vì thành phần này đi kèm với Flask mà chúng tôi sẽ thêm vào reqs.txt. Bất cứ khi nào sử dụng các thư viện ứng dụng của Google Cloud, chẳng hạn như các thư viện dành cho Cloud NDB và Cloud Storage, các thư viện grpcio và công cụ thiết lập đều cần thiết. Cuối cùng, bản thân Cloud Storage yêu cầu thư viện SSL. Chỉ thị thời gian chạy được nhận xét ở trên cùng là dành cho thời điểm bạn đã sẵn sàng chuyển ứng dụng này sang Python 3. Chúng tôi sẽ đề cập đến chủ đề này ở cuối hướng dẫn này.

requirements.txt

Thêm tệp requirements.txt, yêu cầu có khung Flask và thư viện ứng dụng Cloud NDB và Cloud Storage, không có thư viện nào tích hợp sẵn. Tạo tệp có nội dung sau:

flask
google-cloud-ndb
google-cloud-storage

Môi trường thời gian chạy Python 2 App Engine yêu cầu tự nhóm các thư viện của bên thứ ba không được tích hợp sẵn, vì vậy hãy thực thi lệnh sau để cài đặt các thư viện này vào thư mục lib:

pip install -t lib -r requirements.txt

Nếu bạn có cả Python 2 và 3 trên máy phát triển của mình, bạn có thể phải sử dụng lệnh pip2 để đảm bảo nhận được phiên bản Python 2 của các thư viện này. Sau khi nâng cấp lên Python 3, bạn không cần phải tự gói nữa.

appengine_config.py

Thêm tệp appengine_config.py hỗ trợ các thư viện tích hợp sẵn của bên thứ ba và không tích hợp sẵn. Tạo tệp có nội dung sau:

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)

Các bước vừa hoàn thành sẽ tương tự hoặc giống với các bước nêu trong phần Cài đặt thư viện cho ứng dụng Python 2 trong tài liệu của App Engine, và cụ thể hơn là nội dung của appengine_config.py phải khớp với nội dung trong Bước 5 ở đó.

Công việc trên tệp cấu hình đã hoàn tất, vì vậy, hãy chuyển sang ứng dụng.

5. Sửa đổi tệp ứng dụng

Nhập

Nhóm các thay đổi đầu tiên cho main.py bao gồm hoán đổi tất cả những nội dung sẽ được thay thế. Sau đây là nội dung thay đổi:

  1. webapp2 được thay thế bằng bình giữ nhiệt
  2. Thay vì sử dụng Jinja2 trong webapp2_extras, hãy sử dụng Jinja2 đi kèm với Flask
  3. Cloud NDB và Cloud Storage thay thế cho Blobstore và NDB của App Engine
  4. Các trình xử lý Blobstore trong webapp được thay thế bằng kết hợp mô-đun thư viện chuẩn io, Flask và các tiện ích werkzeug
  5. Theo mặc định, Blobstore ghi vào bộ chứa Cloud Storage được đặt tên theo URL của ứng dụng (PROJECT_ID.appspot.com). Vì chúng tôi đang chuyển sang thư viện ứng dụng Cloud Storage nên google.auth được dùng để lấy mã dự án chỉ định chính xác tên bộ chứa. (Bạn có thể thay đổi tên bộ chứa vì tên này không còn được cố định giá trị trong mã nữa.)

TRƯỚC KHI:

import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers

Triển khai các thay đổi trong danh sách trên bằng cách thay thế phần nhập hiện tại trong main.py bằng đoạn mã dưới đây.

SAU KHI:

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

Khởi tạo và hỗ trợ Jinja2 không cần thiết

Khối mã tiếp theo cần thay thế là BaseHandler chỉ định việc sử dụng Jinja2 qua webapp2_extras. Điều này là không cần thiết vì Jinja2 đi kèm với Flask và là công cụ tạo mẫu mặc định của nó, vì vậy hãy loại bỏ nó.

Ở phía Mô-đun 16, hãy tạo thực thể cho các đối tượng mà chúng ta không có trong ứng dụng cũ. Trong đó có việc khởi chạy ứng dụng Flask và tạo các ứng dụng API cho Cloud NDB và Cloud Storage. Cuối cùng, chúng ta đặt tên bộ chứa Cloud Storage như mô tả ở trên trong phần nhập. Dưới đây là trước và sau khi triển khai những nội dung cập nhật này:

TRƯỚC KHI:

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))

SAU KHI:

app = Flask(__name__)
ds_client = ndb.Client()
gcs_client = storage.Client()
_, PROJECT_ID = google.auth.default()
BUCKET = '%s.appspot.com' % PROJECT_ID

Cập nhật quyền truy cập vào Datastore

Cloud NDB chủ yếu tương thích với App Engine NDB. Một điểm khác biệt đã được đề cập là cần có ứng dụng API. Một lý do khác là yêu cầu quyền truy cập vào Datastore phải được kiểm soát bởi trình quản lý ngữ cảnh Python của ứng dụng API. Về cơ bản, điều này có nghĩa là tất cả các lệnh gọi truy cập Datastore sử dụng thư viện ứng dụng Cloud NDB chỉ có thể xảy ra trong các khối with Python.

Đó là một thay đổi; hai là Blobstore và các đối tượng của nó, ví dụ: Cloud Storage không hỗ trợ các BlobKey, vì vậy, hãy thay đổi file_blob thành ndb.StringProperty. Dưới đây là lớp mô hình dữ liệu cũng như các hàm store_visit()fetch_visits() được cập nhật để phản ánh những thay đổi này:

TRƯỚC KHI:

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)

SAU KHI:

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)

Dưới đây là hình ảnh minh hoạ những thay đổi đã được thực hiện từ trước đến nay:

a8f74ca392275822.png

Cập nhật trình xử lý

Trình xử lý tải lên

Trình xử lý trong webapp2 là các lớp khi đang là các hàm trong Flask. Thay vì sử dụng phương thức động từ HTTP, Flask sử dụng động từ này để trang trí hàm. Blobstore và các trình xử lý webapp của Blobstore được thay thế bằng chức năng của Cloud Storage cũng như Flask và các tiện ích tương ứng:

TRƯỚC KHI:

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)

SAU KHI:

@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)

Một số lưu ý về nội dung cập nhật này:

  • Thay vì blob_id, các cấu phần phần mềm tệp hiện được xác định theo tên tệp (fname) nếu có và None nếu không (người dùng chọn không tải tệp lên).
  • Các trình xử lý Blobstore đã loại bỏ quy trình tải lên từ người dùng, nhưng Cloud Storage thì không. Vì vậy, bạn có thể thấy mã mới được thêm để thiết lập đối tượng và vị trí (bộ chứa) cho tệp blob, cũng như lệnh gọi thực hiện việc tải lên thực tế. (upload_from_file()).
  • webapp2 sử dụng một bảng định tuyến ở cuối tệp ứng dụng trong khi các tuyến của Flask được tìm thấy trong mỗi trình xử lý được trang trí.
  • Cả hai trình xử lý đều kết thúc chức năng của chúng bằng cách chuyển hướng đến trang chủ ( / ) trong khi vẫn duy trì yêu cầu POST bằng mã trả về HTTP 307.

Tải trình xử lý xuống

Việc cập nhật trình xử lý tải xuống sẽ tuân theo một mẫu tương tự như trình xử lý tải lên, chỉ khác là có ít mã hơn nhiều để xem. Thay thế chức năng Blobstore và webapp bằng Cloud Storage và Flask tương đương:

TRƯỚC KHI:

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)

SAU KHI:

@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)

Lưu ý về nội dung cập nhật này:

  • Xin nhắc lại, Flask trang trí các hàm của trình xử lý theo tuyến của chúng trong khi webapp thực hiện việc này trong bảng định tuyến ở dưới cùng, vì vậy, hãy nhận ra cú pháp so khớp mẫu của hàm sau ('/view/([^/]+)?') so với của Flask ('/view/<path:fname>').
  • Tương tự như với trình xử lý tải lên, phía Cloud Storage cần thực hiện thêm một số thao tác để thực hiện các chức năng mà các trình xử lý Blobstore tóm tắt, cụ thể là xác định tệp (blob) đang được đề cập và tải xuống rõ ràng lệnh gọi phương thức send_blob() của tệp nhị phân so với trình xử lý Blobstore.
  • Trong cả hai trường hợp, lỗi HTTP 404 sẽ được trả về cho người dùng nếu không tìm thấy cấu phần phần mềm.

Trình xử lý chính

Những thay đổi cuối cùng đối với ứng dụng chính sẽ diễn ra trong trình xử lý chính. Các phương thức động từ HTTP webapp2 được thay thế bằng một hàm duy nhất kết hợp chức năng của các phương thức đó. Thay thế lớp MainHandler bằng hàm root() và xoá bảng định tuyến webapp2 như minh hoạ dưới đây:

TRƯỚC KHI:

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)

SAU KHI:

@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)

Thay vì phân tách các phương thức get()post(), về cơ bản, chúng là một câu lệnh if-else trong root(). Ngoài ra, vì root() là một hàm đơn nhất nên chỉ có một lệnh gọi để kết xuất mẫu cho cả GETPOST trong khi thực sự không thể thực hiện được việc này trong webapp2.

Dưới đây là hình ảnh minh hoạ cho loạt thay đổi thứ hai và cũng là cuối cùng này đối với main.py:

5ec38818c32fec2.png.

(không bắt buộc) Tính năng "nâng cao" khả năng tương thích ngược

Vì vậy, giải pháp được tạo ở trên hoạt động hoàn hảo... nhưng chỉ khi bạn bắt đầu từ đầu và chưa có tệp do Blobstore tạo. Vì chúng ta đã cập nhật ứng dụng để xác định tệp theo tên tệp thay vì BlobKey, nên ứng dụng Mô-đun 16 đã hoàn thiện sẽ không thể xem các tệp Blobstore. Nói cách khác, chúng tôi đã thực hiện một thay đổi không tương thích ngược khi thực hiện quá trình di chuyển này. Hiện tại, chúng tôi giới thiệu một phiên bản thay thế của main.py có tên là main-migrate.py (có trong kho lưu trữ) nhằm thu hẹp khoảng cách này.

"Tiện ích" đầu tiên để hỗ trợ các tệp do Blobstore tạo là một mô hình dữ liệu có BlobKeyProperty (ngoài StringProperty cho các tệp do Cloud Storage tạo):

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()

Thuộc tính file_blob sẽ dùng để xác định các tệp do Blobstore tạo, còn file_gcs dành cho các tệp trên Cloud Storage. Bây giờ, khi tạo lượt truy cập mới, hãy lưu trữ rõ ràng một giá trị trong file_gcs thay vì file_blob, vì vậy store_visit sẽ hơi khác một chút:

TRƯỚC KHI:

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()

SAU KHI:

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()

Khi tìm nạp các lượt truy cập gần đây nhất, hãy "chuẩn hoá" dữ liệu trước khi gửi đến mẫu:

TRƯỚC KHI:

@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)

SAU KHI:

@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)

Tiếp theo, hãy xác nhận sự tồn tại của file_blob hoặc file_gcs (hoặc không có). Nếu có tệp, hãy chọn tệp hiện có và sử dụng giá trị nhận dạng đó (BlobKey đối với tệp do Blobstore tạo hoặc tên tệp cho các tệp được tạo bằng Cloud Storage). Khi chúng tôi nói "Tệp do Cloud Storage tạo", ý chúng tôi là các tệp được tạo bằng thư viện ứng dụng Cloud Storage. Blobstore cũng ghi vào Cloud Storage, nhưng trong trường hợp này, đó sẽ là các tệp do Blobstore tạo.

Bây giờ, quan trọng hơn là hàm etl_visits() này được dùng để chuẩn hoá hoặc ETL (trích xuất, biến đổi và tải) dữ liệu cho người dùng cuối là gì? Thông báo sẽ có dạng như sau:

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]

Kết quả này có thể giống như những gì bạn mong đợi: mã lặp lại qua tất cả các lượt truy cập và cho mỗi lượt truy cập, lấy nguyên văn dữ liệu khách truy cập và dấu thời gian, sau đó kiểm tra xem file_gcs hoặc file_blob có tồn tại không và nếu có, hãy chọn một trong số đó (hoặc None nếu không có).

Dưới đây là hình minh hoạ sự khác biệt giữa main.pymain-migrate.py:

718b05b2adadb2e1.png.

Nếu bạn bắt đầu từ đầu khi chưa có các tệp do Blobstore tạo, hãy sử dụng main.py. Tuy nhiên, nếu bạn đang chuyển đổi và muốn các tệp được hỗ trợ do cả Blobstore Cloud Storage tạo ra, hãy xem main-migrate.py làm ví dụ về cách xử lý các tình huống như giúp bạn lên kế hoạch di chuyển ứng dụng của mình. Khi thực hiện các quá trình di chuyển phức tạp, các trường hợp đặc biệt có thể phát sinh, vì vậy, ví dụ này nhằm thể hiện mong muốn hiện đại hơn với việc hiện đại hoá các ứng dụng thực bằng dữ liệu thực.

6. Tóm tắt/Dọn dẹp

Phần này tóm tắt lớp học lập trình này bằng cách triển khai ứng dụng, xác minh rằng ứng dụng hoạt động như dự kiến và trong mọi đầu ra được phản ánh. Sau khi xác thực ứng dụng, hãy thực hiện mọi bước dọn dẹp và xem xét các bước tiếp theo.

Triển khai và xác minh ứng dụng

Trước khi triển khai lại ứng dụng, hãy nhớ chạy pip install -t lib -r requirements.txt để tải các thư viện bên thứ ba tự nhóm đó vào thư mục lib. Nếu bạn muốn chạy giải pháp có khả năng tương thích ngược, trước tiên hãy đổi tên main-migrate.py thành main.py. Bây giờ, hãy chạy gcloud app deploy và xác nhận rằng ứng dụng này hoạt động giống hệt với ứng dụng Mô-đun 15. Màn hình biểu mẫu có dạng như sau:

f5b5f9f19d8ae978.png

Trang về lượt truy cập gần đây nhất trông giống như sau:

f5ac6b98ee8a34cb.png

Chúc mừng bạn đã hoàn thành lớp học lập trình này về việc thay thế App Engine Blobstore bằng Cloud Storage, App Engine NDB bằng Cloud NDB và webapp2 bằng Flask. Mã của bạn bây giờ sẽ khớp với nội dung trong thư mục chốt (HOÀN TẤT (Mô-đun 16)). main-migrate.py thay thế cũng có trong thư mục đó.

"Di chuyển" Python 3

Chỉ lệnh runtime Python 3 đã nhận xét ở đầu app.yaml là tất cả những gì cần thiết để chuyển ứng dụng này sang Python 3. Bản thân mã nguồn đã tương thích với Python 3, vì vậy bạn không cần thay đổi gì. Để triển khai dưới dạng ứng dụng Python 3, hãy thực thi các bước sau:

  1. Huỷ nhận xét về lệnh runtime của Python 3 ở đầu app.yaml.
  2. Xoá tất cả các dòng khác trong app.yaml.
  3. Xoá tệp appengine_config.py. (không sử dụng trong thời gian chạy Python 3)
  4. Xoá thư mục lib nếu thư mục này tồn tại. (không cần thiết với thời gian chạy Python 3)

Dọn dẹp

Giải pháp chung

Nếu bạn đã hoàn tất, chúng tôi khuyên bạn nên tắt ứng dụng App Engine để tránh phát sinh thanh toán. Tuy nhiên, nếu bạn muốn kiểm tra hoặc thử nghiệm thêm, nền tảng App Engine có hạn mức miễn phí, và bạn sẽ không bị tính phí, miễn là bạn không vượt quá cấp sử dụng đó. Đó là cho dịch vụ điện toán nhưng bạn cũng có thể bị tính phí cho các dịch vụ có liên quan của App Engine, vì vậy hãy xem trang giá của dịch vụ này để biết thêm thông tin. Nếu quá trình di chuyển này liên quan đến các dịch vụ khác trên Google Cloud, thì những dịch vụ đó sẽ được tính phí riêng. Trong cả hai trường hợp (nếu có), hãy xem phần "Dành riêng cho lớp học lập trình này" phần dưới đây.

Để công bố đầy đủ thông tin, việc triển khai cho một nền tảng điện toán không máy chủ của Google Cloud như App Engine sẽ làm phát sinh chi phí bản dựng và bộ nhớ thấp. Cloud Build cũng có hạn mức miễn phí riêng, tương tự như Cloud Storage. Việc lưu trữ hình ảnh đó sẽ sử dụng hết một phần hạn mức đó. Tuy nhiên, bạn có thể sống ở một khu vực không có bậc miễn phí như vậy, vì vậy hãy chú ý đến mức sử dụng bộ nhớ của bạn để giảm thiểu chi phí tiềm ẩn. "Thư mục" cụ thể trên Cloud Storage bạn nên xem xét, bao gồm:

  • 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
  • Các đường liên kết lưu trữ ở trên phụ thuộc vào PROJECT_ID và hoạt động *LOC*của bạn, ví dụ: "us" nếu ứng dụng của bạn được lưu trữ ở Hoa Kỳ.

Mặt khác, nếu bạn không định tiếp tục sử dụng ứng dụng này hoặc các lớp học lập trình di chuyển khác có liên quan và muốn xoá hoàn toàn mọi thứ, hãy ngừng dự án của bạn.

Dành riêng cho lớp học lập trình này

Các dịch vụ được liệt kê dưới đây là những dịch vụ dành riêng cho lớp học lập trình này. Hãy tham khảo tài liệu của từng sản phẩm để biết thêm thông tin:

Xin lưu ý rằng nếu đã di chuyển từ Mô-đun 15 sang Mô-đun 16, bạn vẫn sẽ có dữ liệu trong Blobstore. Do đó, chúng tôi đưa thông tin về giá của mô-đun này vào phần trên.

Các bước tiếp theo

Ngoài hướng dẫn này, bạn cũng cần cân nhắc những mô-đun di chuyển khác tập trung vào việc ngừng sử dụng các dịch vụ cũ theo gói:

  • Mô-đun 2: di chuyển từ App Engine ndb sang Cloud NDB
  • Mô-đun 7-9: di chuyển từ Hàng đợi tác vụ của App Engine để đẩy các tác vụ sang Cloud Tasks
  • Mô-đun 12-13: di chuyển từ App Engine Memcache sang Cloud Memorystore
  • Mô-đun 18-19: di chuyển từ Hàng đợi tác vụ của App Engine (Tác vụ kéo) sang Cloud Pub/Sub

App Engine không còn là nền tảng không máy chủ duy nhất trên Google Cloud nữa. Nếu bạn có một ứng dụng App Engine nhỏ hoặc một ứng dụng có chức năng hạn chế và muốn biến ứng dụng đó thành một dịch vụ vi mô độc lập, hoặc bạn muốn chia một ứng dụng nguyên khối thành nhiều thành phần có thể sử dụng lại, thì đây là những lý do chính đáng để bạn cân nhắc chuyển sang Cloud Functions. Nếu việc tích hợp vùng chứa đã trở thành một phần trong quy trình phát triển ứng dụng, đặc biệt là khi quy trình đó bao gồm một quy trình CI/CD (tích hợp liên tục/phân phối hoặc triển khai liên tục), hãy cân nhắc chuyển sang Cloud Run. Những trường hợp này được đề cập trong các mô-đun sau:

  • Di chuyển từ App Engine sang Cloud Functions: xem Học phần 11
  • Di chuyển từ App Engine sang Cloud Run: xem Mô-đun 4 để vùng chứa ứng dụng của bạn bằng Docker, hoặc Mô-đun 5 để triển khai mà không cần vùng chứa, kiến thức về Docker hoặc Dockerfile

Việc chuyển sang một nền tảng không máy chủ khác là không bắt buộc. Bạn nên cân nhắc các lựa chọn phù hợp nhất cho ứng dụng và trường hợp sử dụng của mình trước khi điều chỉnh bất cứ điều gì.

Bất kể bạn cân nhắc mô-đun di chuyển nào tiếp theo, bạn đều có thể truy cập vào tất cả nội dung của Trạm di chuyển không máy chủ (lớp học lập trình, video, mã nguồn [nếu có]) tại kho lưu trữ nguồn mở của chúng. README của kho lưu trữ này cũng cung cấp hướng dẫn về những quá trình di chuyển cần xem xét và mọi "đơn đặt hàng" có liên quan Mô-đun di chuyển.

7. Tài nguyên khác

Vấn đề/ý kiến phản hồi về lớp học lập trình này

Nếu bạn gặp vấn đề với lớp học lập trình này, vui lòng tìm vấn đề của bạn trước khi gửi. Đường liên kết để tìm kiếm và báo cáo vấn đề mới:

Tài nguyên di chuyển

Bạn có thể tìm thấy các đường liên kết đến các thư mục repo cho Mô-đun 15 (START) và Mô-đun 16 (Finish) trong bảng bên dưới. Bạn cũng có thể truy cập vào các tệp này qua kho lưu trữ dành cho mọi quá trình di chuyển trong lớp học lập trình App Engine. Tại đây, bạn có thể sao chép hoặc tải tệp ZIP xuống.

Codelab

Python 2

Python 3

Mô-đun 15

Không có

Mô-đun 16 (lớp học lập trình này)

(tương tự như Python 2)

Tài nguyên trực tuyến

Dưới đây là các tài nguyên trực tuyến có thể phù hợp với hướng dẫn này:

Blobstore và Cloud Storage của App Engine

Nền tảng App Engine

Thông tin khác về đám mây

Python

Video

Giấy phép

Tác phẩm này được cấp phép theo Giấy phép chung Ghi nhận tác giả Creative Commons 2.0.