Cách sử dụng App Engine Memcache trong các ứng dụng Flask (Mô-đun 12)

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 đưa và sử dụng App Engine Memcache vào ứng dụng mẫu từ Lớp học lập trình Mô-đun 1. Chúng ta thêm cách sử dụng Memcache trong hướng dẫn của Mô-đun 12 này, sau đó di chuyển sang Cloud Memorystore tiếp theo trong Mô-đun 13.

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

  • Dùng API/thư viện App Engine Memcache
  • Thêm tính năng lưu vào bộ nhớ đệm vào ứng dụng NDB cơ bản của Python 2 App Engine App Engine

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

Để di chuyển từ App Engine Memcache, hãy thêm hoạt động sử dụng này vào ứng dụng NDB của Flask và App Engine hiện có từ Lớp học lập trình Mô-đun 1. Ứng dụng mẫu hiển thị 10 lượt truy cập gần đây nhất của người dùng. Nếu cùng một người dùng làm mới trình duyệt của mình, thì việc liên tục tạo các thực thể Lượt truy cập mới và tìm nạp các lượt truy cập gần đây nhất từ Datastore sẽ không tối ưu. Vì vậy, chúng tôi sẽ lưu các lượt truy cập gần đây nhất đó vào bộ nhớ đệm.

Nếu cùng một khách truy cập truy cập vào trang, thì các lượt truy cập đó sẽ được trả về từ bộ nhớ đệm. Nếu người dùng mới truy cập vào trang web hoặc một giờ đã trôi qua, bộ nhớ đệm sẽ được xoá và thay thế bằng các mục nhập gần đây nhất (chưa kể một Lượt truy cập mới đã đăng ký). Sau khi triển khai tính năng tích hợp Memcache của App Engine, chúng ta có thể di chuyển nó sang Cloud Memorystore trong lớp học lập trình tiếp theo (Mô-đun 13).

Hướng dẫn này bao gồm các bước sau:

  1. Thiết lập/Chuẩn bị
  2. Cập nhật cấu hình
  3. Sửa đổi mã xử lý ứng dụng

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 đã hoàn thành lớp học lập trình Mô-đun 1, bạn nên sử dụng lại chính dự án (và mã nguồn). 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 1 hoạt động được. Nếu bạn chưa có tài khoản, hãy xem toàn bộ một trong hai hướng dẫn ở trên trước khi tiếp tục thực hiện các bước này. Hoặc nếu đã quen với nội dung của Mô-đun 1, bạn có thể bắt đầu bằng mã Mô-đun 1 bên dưới.

Cho dù bạn sử dụng mã của chúng tôi hay của bạn, mã Mô-đun 1 là nơi chúng ta sẽ BẮT ĐẦU. 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ư những gì trong thư mục kho lưu trữ Mô-đun 11 (gián đoạn).

Thư mục của các tệp BẮT ĐẦU Mô-đun 1 (của bạn hoặc của chúng tôi) sẽ có dạng như sau:

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

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 thi thành công các bước đó và thấy ứng dụng web của bạn hoạt động (với kết quả tương tự như bên dưới), bạn đã sẵn sàng thêm chức năng lưu vào bộ nhớ đệm cho ứng dụng của mình.

a7a9d2b80d706a2b.png

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

Không cần thay đổi gì đối với các tệp cấu hình App Engine chuẩn (app.yaml, requirements.txt, appengine_config.py).

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

Vì chúng ta chỉ thêm App Engine API nên không có gói bên ngoài nào liên quan, nghĩa là không cần cập nhật tệp cấu hình (app.yaml, requirements.txt, appengine_config.py). Chỉ có một tệp ứng dụng là main.py, vì vậy, tất cả thay đổi trong phần này chỉ ảnh hưởng đến tệp đó.

Nhập

Bước quan trọng nhất là nhập thư viện Memcache, google.appengine.api.memcache. Do chúng ta sẽ lưu các lượt truy cập gần đây nhất vào bộ nhớ đệm trong một giờ, hãy thêm hằng số cho số giây trong một giờ. Dưới đây là giao diện trước đây của mã và thay đổi này:

TRƯỚC KHI:

from flask import Flask, render_template, request
from google.appengine.ext import ndb

app = Flask(__name__)

SAU KHI:

from flask import Flask, render_template, request
from google.appengine.api import memcache
from google.appengine.ext import ndb

app = Flask(__name__)
HOUR = 3600

Thêm chức năng lưu vào bộ nhớ đệm với tính năng hỗ trợ của Memcache

Thay đổi quan trọng nhất là việc thêm chức năng lưu vào bộ nhớ đệm trong ứng dụng của chúng ta. Cụ thể hơn, chúng tôi nên lưu các lượt truy cập gần đây nhất vào bộ nhớ đệm, kiểm tra xem có sẵn các lượt truy cập được lưu trong bộ nhớ đệm hay không và cố gắng sử dụng các kết quả được lưu trong bộ nhớ đệm nhiều nhất có thể theo kế hoạch của chúng tôi. Sau đây là các bước mà ứng dụng sẽ thực hiện để hoàn thành mục tiêu của chúng ta:

  1. Đặt lượt ghé thăm hiện tại và đặt tên là visitor
  2. Cố gắng tìm nạp visits gần đây nhất từ bộ nhớ đệm
  3. Nếu bộ nhớ đệm trống hoặc khách truy cập gần đây nhất (visits[0]['visitor']) khác với visitor hiện tại: hãy lưu trữ lượt truy cập mới nhất này, tìm nạp các lượt truy cập gần đây nhất và lưu vào bộ nhớ đệm trong 1 giờ.
  4. Hiển thị visits cho người dùng thông qua mẫu web

Dưới đây là hình ảnh trước và sau của những thay đổi này:

TRƯỚC KHI:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

SAU KHI:

@app.route('/')
def root():
    'main application (GET) handler'
    # check for (hour-)cached visits
    ip_addr, usr_agt = request.remote_addr, request.user_agent
    visitor = '{}: {}'.format(ip_addr, usr_agt)
    visits = memcache.get('visits')

    # register visit & run DB query if cache empty or new visitor
    if not visits or visits[0]['visitor'] != visitor:
        store_visit(ip_addr, usr_agt)
        visits = list(fetch_visits(10))
        memcache.set('visits', visits, HOUR)  # set() not add()

    return render_template('index.html', visits=visits)

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

b1242503602f7bf0.png

Như vậy là tất cả thay đổi cần thiết để thêm việc sử dụng memcache của App Engine vào ứng dụng mẫu của Mô-đun 1. Hãy cùng xây dựng và triển khai ứng dụng này để xem ứng dụng hoạt động!

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

Triển khai lại ứng dụng bằng gcloud app deploy và xác nhận rằng ứng dụng hoạt động bình thường. Mã của bạn bây giờ sẽ khớp với nội dung trong phần HOÀN TẤT, thư mục Mô-đun 12. Kết quả phải giống với ứng dụng Mô-đun 1 mà bạn đã triển khai trước đó:

a7a9d2b80d706a2b.png

Tất cả những gì chúng tôi làm là tăng tốc trải nghiệm người dùng cho cùng một người dùng. Khi làm mới, bạn sẽ nhận được kết quả trực tiếp từ bộ nhớ đệm, tức là không tạo lượt truy cập mới cũng như không thực hiện tìm nạp Datastore.

Chúc mừng bạn đã hoàn thành lớp học lập trình của Mô-đun 12 để thêm việc sử dụng dịch vụ memcache của App Engine vào ứng dụng mẫu. Bây giờ, bạn có thể chuyển ứng dụng Python 2 này sang Python 3 trong bước bổ sung.

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:

  • Dịch vụ Memcache của App Engine có hai phiên bản khác nhau, mỗi phiên bản có cấu trúc giá riêng. Vì vậy, bạn phải theo dõi cách sử dụng đó vì nó liên quan đến thanh toán.
  • Dịch vụ App Engine Datastore do Cloud Datastore (Cloud Firestore ở chế độ Datastore) cung cấp, cũng có một bậc miễn phí; hãy xem trang giá của YouTube để biết thêm thông tin.

Các bước tiếp theo

Bước di chuyển logic tiếp theo cần xem xét sẽ được đề cập trong Mô-đun 13, hướng dẫn các nhà phát triển cách di chuyển từ dịch vụ App Engine memcache sang Cloud Memorystore. Những quá trình di chuyển này là không bắt buộc và dành cho người dùng muốn thực hiện nhiều bước để hiện đại hoá ứng dụng. Dịch vụ Cloud Memorystore là một bản nâng cấp đáng kể cho memcache của App Engine vì nhiều lý do:

  • Cloud Memorystore không phải dạng không máy chủ. Điều này có nghĩa là bạn phải phân bổ một máy chủ cho bộ nhớ đệm. Cloud Memorystore cũng không có bậc miễn phí. Cả hai yếu tố này đều có thể có tác động đáng kể đến chi phí.
  • Cloud Memorystore hỗ trợ một cặp cơ chế lưu trữ cơ bản (công cụ lưu vào bộ nhớ đệm), RedisMemcached.
  • Cloud Memorystore (dành cho Redis) có bộ tính năng phong phú và chuyên sâu hơn nhiều so với App Engine Memcache.
  • Để sử dụng Cloud Memorystore, bạn phải thiết lập một máy chủ Cloud Memorystore, thêm máy chủ đó vào mạng Google Cloud VPC, sau đó yêu cầu ứng dụng App Engine của bạn dùng mạng đó để giao tiếp với máy chủ Memorystore của bạn.

Nếu bạn không cần tất cả các tính năng hiện có từ Cloud Memorystore hoặc lo ngại về tác động của nó đến chi phí, bạn có thể tiếp tục sử dụng App Engine Memcache.

Ngoài Mô-đun 13, chúng tôi còn có hàng loạt hoạt động di chuyển khác có thể thực hiện, chẳng hạn như Cloud NDB và Cloud Datastore hoặc Cloud Tasks. Ngoài ra, chúng tôi cũng sẽ di chuyển giữa nhiều sản phẩm sang Cloud Run và Cloud Functions. Bạn sẽ tìm thấy tất cả các yêu cầu này tại kho lưu trữ di chuyển.

Bước tiếp theo có thể thực hiện khác là chuyển sang Python 3. Bước này được đề cập trong phần tiếp theo dưới dạng một bước không bắt buộc.

7. THƯỞNG: Di chuyển sang Python 3

Tổng quan

Phần này bao gồm nội dung bổ sung không bắt buộc di chuyển Ứng dụng mô-đun 12 mà chúng ta vừa hoàn tất ở trên sang Python 3. Chúng ta bắt đầu với cấu hình, sau đó là ứng dụng.

Đơn giản hoá app.yaml

Một trong những lợi ích của thời gian chạy Python 3 là app.yaml có thể được đơn giản hoá đáng kể.

TRƯỚC KHI:

Dưới đây là nội dung trong app.yaml ở phần kết của Phụ lục 12:

runtime: python27
threadsafe: yes
api_version: 1

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

Vì môi trường thời gian chạy Python 3 yêu cầu các khung web thực hiện định tuyến riêng, nên tất cả trình xử lý định tuyến trong app.yaml phải được thay đổi thành auto. Nếu không có tệp tĩnh nào được phân phát, người dùng có thể xoá ngay toàn bộ phần handlers:. Ngoài ra, cả threadsafeapi_version đều không được dùng nữa.

SAU KHI:

Với những thay đổi bắt buộc như mô tả, đây là app.yaml thay thế cho Python 3:

runtime: python39
app_engine_apis: true

Dòng duy nhất cần giải thích là app_engine_apis: true. Khi các dịch vụ App Engine cũ được cung cấp cho môi trường thời gian chạy thế hệ thứ hai vào năm 2021, một số môi trường thời gian chạy, trong đó có Python 3, sẽ yêu cầu phương thức khởi động bổ sung để truy cập vào các API đó như ndb, taskqueuememcache. Dòng này trong cấu hình phục vụ mục đích đó.

Cập nhật required.txt

Bắt buộc phải có một quy trình tự khởi động khác của các API gốc trong requirements.txt: phải bao gồm quyền truy cập vào SDK App Engine mới.

TRƯỚC KHI:

Dưới đây là nội dung trong app.yaml ở phần kết của Phụ lục 12:

flask

SAU KHI:

Bạn chỉ cần thêm App Engine Python SDK và bạn sẽ có như sau:

flask
appengine-python-standard

Xoá appengine_config.py và lib

Môi trường thời gian chạy App Engine thế hệ mới cải tiến cách sử dụng gói của bên thứ ba:

  • Thư viện tích hợp là những thư viện đã được Google xem xét kỹ lưỡng và cung cấp trên các máy chủ App Engine, có thể là do các thư viện đó chứa mã C/C++ mà nhà phát triển không được phép triển khai lên đám mây. Các thư viện này không còn được hỗ trợ trong thời gian chạy thế hệ thứ 2 nữa.
  • Bạn không còn cần sao chép thư viện không tích hợp sẵn (đôi khi được gọi là "cung cấp" hoặc "tự gộp nhóm") trong thời gian chạy thế hệ thứ 2 nữa. Thay vào đó, các ứng dụng đó phải được liệt kê trong requirements.txt. Tại đây, hệ thống xây dựng sẽ tự động cài đặt các thành phần này thay cho bạn tại thời điểm triển khai.

Do những thay đổi đó đối với tính năng quản lý gói của bên thứ ba, bạn không cần tệp appengine_config.py và thư mục lib. Vì vậy, hãy xoá chúng. Trong thời gian chạy thế hệ thứ 2, App Engine tự động cài đặt các gói bên thứ ba được liệt kê trong requirements.txt. Tóm tắt:

  1. Không có thư viện bên thứ ba tự nhóm hoặc sao chép; liệt kê chúng trong requirements.txt
  2. Không có pip install trong thư mục lib, nghĩa là không có khoảng thời gian của thư mục lib
  3. Không liệt kê thư viện bên thứ ba tích hợp sẵn (do đó không có phần libraries) trong app.yaml; liệt kê chúng trong requirements.txt
  4. Không có thư viện bên thứ ba để tham chiếu từ ứng dụng của bạn nghĩa là không có tệp appengine_config.py

Việc liệt kê tất cả thư viện của bên thứ ba mong muốn trong requirements.txt là yêu cầu duy nhất của nhà phát triển.

Cập nhật ứng dụng để dùng SDK App Engine

Như đã đề cập ở trên, các ứng dụng Python 3 cần phải sửa đổi một số điểm để truy cập vào các dịch vụ đi kèm của App Engine:

  1. Gói App Engine SDK (trong requirements.txt)
  2. Kích hoạt SDK App Engine (trong app.yaml)
  3. Gói đối tượng WSGI (trong main.py)

Cặp đầu tiên đã hoàn tất ở trên, vì vậy yêu cầu cuối cùng là cập nhật main.py.

TRƯỚC KHI:

Dưới đây là main.py của Python 2 ở phần kết của Mô-đun 12:

from flask import Flask, render_template, request
from google.appengine.api import memcache
from google.appengine.ext import ndb

app = Flask(__name__)
HOUR = 3600

SAU KHI:

Đối với cổng Python 3, hãy nhập SDK và gói đối tượng ứng dụng Flask vào cổng đó (trình bao bọc SDK), dẫn đến kết quả như sau:

from flask import Flask, render_template, request
from google.appengine.api import memcache, wrap_wsgi_app
from google.appengine.ext import ndb

app = Flask(__name__)
app.wsgi_app = wrap_wsgi_app(app.wsgi_app)
HOUR = 3600

Nhà phát triển cần thực hiện những thay đổi này đối với các ứng dụng Python khi chuyển từ phiên bản 2.x sang 3.x để truy cập vào các dịch vụ theo gói. Nếu bạn không sử dụng Flask, cũng có các mẫu Django và Kim tự tháp trong tài liệu. Nếu mã Python 2 của bạn không phải là ứng dụng web, thì bạn chỉ cần thêm gói SDK là đủ khi chuyển sang Python 3. Mã xử lý ứng dụng của chúng ta ban đầu được tạo ra để hoạt động theo Python 2 và 3, vì vậy bạn không cần thay đổi khả năng tương thích.

Triển khai ứng dụng

Sau khi hoàn tất những thay đổi trên, bạn có thể triển khai ứng dụng mẫu đã cập nhật. (Không có vấn đề gì khi bạn triển khai ứng dụng phiên bản Python 3 qua phiên bản Python 2 gốc trong cùng dự án GCP.) Hành vi của ứng dụng phải giữ nguyên. Nếu bạn cần so sánh ứng dụng đã cập nhật với ứng dụng của chúng tôi, hãy xem thư mục Mô-đun 12b trong kho lưu trữ di chuyển. Để tìm hiểu thêm về khả năng hỗ trợ của các dịch vụ đi kèm App Engine trong thời gian chạy mới nhất như Python 3, hãy xem thông báo về việc ra mắt tính năng cũng như Lớp học lập trình Mô-đun 17.

Chúc mừng bạn đã hoàn thành bước thưởng trong Mô-đun 12! Ngoài ra, hãy xem tài liệu về cách chuẩn bị tệp cấu hình cho thời gian chạy Python 3. Hãy xem lại mục Tóm tắt/Dọn dẹp ở trên để biết các bước tiếp theo và cách dọn dẹp.

8. Tài nguyên khác

Dưới đây là các tài nguyên bổ sung dành cho nhà phát triển đang tìm hiểu thêm về Phụ lục di chuyển này hoặc mô-đun di chuyển có liên quan cũng như các sản phẩm có liên quan. Trong đó có cả những nơi để đưa ra ý kiến phản hồi về nội dung này, các đường liên kết đến mã nguồn và nhiều thông tin hữu ích khác của tài liệu.

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 2 (START) và Mô-đun 12 (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

Học phần 1

(không có trong hướng dẫn này)

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

Tài liệu tham khảo 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:

App Engine

Cloud Memorystore và Cloud Datastore

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

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.