Hàm đám mây HTTP trong Python

1. Giới thiệu

b158ce75c3cccd6d.png

Python là một ngôn ngữ lập trình nguồn mở phổ biến được các nhà khoa học dữ liệu, nhà phát triển ứng dụng web, quản trị viên hệ thống và nhiều người khác sử dụng.

Cloud Functions là một nền tảng điện toán không máy chủ hoạt động theo sự kiện. Cloud Functions cho phép bạn viết mã mà không cần lo lắng về việc cung cấp tài nguyên hoặc mở rộng quy mô để đáp ứng các yêu cầu thay đổi.

Có 2 loại Cloud Functions:

  • Các hàm HTTP phản hồi các yêu cầu HTTP. Bạn sẽ tạo một vài thành phần trong lớp học lập trình này.
  • Các hàm nền được kích hoạt bởi các sự kiện, chẳng hạn như một thông báo được xuất bản trên Cloud Pub/Sub hoặc một tệp được tải lên Cloud Storage. Chúng ta không đề cập đến vấn đề này trong lớp học này, nhưng bạn có thể đọc thêm trong tài liệu.

efb3268e3b74ed4f.png

Lớp học lập trình này sẽ hướng dẫn bạn cách tạo Cloud Functions của riêng mình bằng Python.

Sản phẩm bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ xuất bản một Cloud Function. Khi được gọi qua HTTP, hàm này sẽ hiển thị biểu trưng"Python Powered":

a7aaf656b78050fd.png

Kiến thức bạn sẽ học được

  • Cách viết một Cloud Function HTTP.
  • Cách viết một Cloud Function HTTP nhận đối số.
  • Cách kiểm thử một Cloud Function HTTP.
  • Cách chạy máy chủ HTTP cục bộ bằng Python để dùng thử hàm này.
  • Cách viết một Cloud Function HTTP trả về hình ảnh.

2. Thiết lập và yêu cầu

Thiết lập môi trường theo tốc độ của riêng bạn

  1. Đăng nhập vào Google Cloud Console rồi tạo một dự án mới hoặc sử dụng lại một dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Tên dự án là tên hiển thị của những người tham gia dự án này. Đây là một chuỗi ký tự mà các API của Google không sử dụng. Bạn luôn có thể cập nhật thông tin này.
  • Mã dự án là giá trị duy nhất trên tất cả các dự án trên Google Cloud và không thể thay đổi (bạn không thể thay đổi mã này sau khi đã đặt). Cloud Console sẽ tự động tạo một chuỗi duy nhất; thường thì bạn không cần quan tâm đến chuỗi này. Trong hầu hết các lớp học lập trình, bạn sẽ cần tham chiếu đến Mã dự án (thường được xác định là PROJECT_ID). Nếu không thích mã nhận dạng được tạo, bạn có thể tạo một mã nhận dạng ngẫu nhiên khác. Hoặc bạn có thể thử tên người dùng của riêng mình để xem tên đó có được chấp nhận hay không. Bạn không thể thay đổi tên này sau bước này và tên này sẽ tồn tại trong suốt thời gian của dự án.
  • Để bạn nắm được thông tin, có một giá trị thứ ba là Số dự án mà một số API sử dụng. Tìm hiểu thêm về cả 3 giá trị này trong tài liệu.
  1. Tiếp theo, bạn cần bật tính năng thanh toán trong Cloud Console để sử dụng các tài nguyên/API trên Cloud. Việc thực hiện lớp học lập trình này sẽ không tốn nhiều chi phí, nếu có. Để tắt các tài nguyên nhằm tránh bị tính phí ngoài phạm vi hướng dẫn này, bạn có thể xoá các tài nguyên đã tạo hoặc xoá dự án. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí trị giá 300 USD.

Khởi động Cloud Shell

Mặc dù có thể vận hành Google Cloud từ xa trên máy tính xách tay, nhưng trong lớp học lập trình này, bạn sẽ sử dụng Cloud Shell, một môi trường dòng lệnh chạy trên đám mây.

Kích hoạt Cloud Shell

  1. Trong Cloud Console, hãy nhấp vào Kích hoạt Cloud Shell 853e55310c205094.png.

3c1dabeca90e44e5.png

Nếu đây là lần đầu tiên bạn khởi động Cloud Shell, bạn sẽ thấy một màn hình trung gian mô tả về Cloud Shell. Nếu bạn thấy màn hình trung gian, hãy nhấp vào Tiếp tục.

9c92662c6a846a5c.png

Quá trình cung cấp và kết nối với Cloud Shell chỉ mất vài giây.

9f0e51b578fecce5.png

Máy ảo này được trang bị tất cả các công cụ phát triển cần thiết. Nền tảng này cung cấp một thư mục chính có dung lượng 5 GB và chạy trong Google Cloud, giúp tăng cường đáng kể hiệu suất mạng và hoạt động xác thực. Bạn có thể thực hiện hầu hết, nếu không muốn nói là tất cả, công việc của mình trong lớp học lập trình này bằng trình duyệt.

Sau khi kết nối với Cloud Shell, bạn sẽ thấy rằng mình đã được xác thực và dự án được đặt thành mã dự án của bạn.

  1. Chạy lệnh sau trong Cloud Shell để xác nhận rằng bạn đã được xác thực:
gcloud auth list

Đầu ra của lệnh

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Chạy lệnh sau trong Cloud Shell để xác nhận rằng lệnh gcloud biết về dự án của bạn:
gcloud config list project

Đầu ra của lệnh

[core]
project = <PROJECT_ID>

Nếu không, bạn có thể đặt nó bằng lệnh sau:

gcloud config set project <PROJECT_ID>

Đầu ra của lệnh

Updated property [core/project].

Đảm bảo bạn đã bật API Cloud Functions và Cloud Build

Chạy lệnh sau từ Cloud Shell để đảm bảo bạn đã bật API Cloud Functions và Cloud Build:

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

Lưu ý: Cloud Build sẽ được gọi bằng lệnh gcloud functions deploy và tự động tạo mã của bạn thành một hình ảnh vùng chứa.

Tải mã nguồn xuống

Trong cửa sổ dòng lệnh Cloud Shell, hãy chạy các lệnh sau:

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

Kiểm tra nội dung của thư mục nguồn:

ls

Bạn sẽ có các tệp sau:

main.py  python-powered.png  test_main.py  web_app.py

3. Giới thiệu Cloud Functions HTTP

HTTP Cloud Functions trong Python được viết dưới dạng các hàm Python thông thường. Hàm này phải chấp nhận một đối số flask.Request duy nhất, thường được đặt tên là request.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

Bạn có thể mở tệp bằng trình chỉnh sửa dòng lệnh mà mình muốn (nano, vim hoặc emacs). Bạn cũng có thể mở tệp này trong Cloud Shell Editor sau khi đặt thư mục nguồn làm không gian làm việc:

cloudshell open-workspace .

Hãy triển khai hàm này dưới dạng một Hàm HTTP trên đám mây bằng lệnh gcloud functions deploy:

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Đầu ra của lệnh:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Lưu ý về các lựa chọn gcloud functions deploy:

  • --runtime: Chỉ định thời gian chạy ngôn ngữ. Đối với Python, giá trị này hiện có thể là python37, python38, python39, python310 hoặc python312. Xem phần Môi trường thời gian chạy.
  • --trigger-http: Hàm sẽ được chỉ định một điểm cuối. Các yêu cầu HTTP (POST, PUT, GET, DELETE và OPTIONS) đến điểm cuối sẽ kích hoạt quá trình thực thi hàm.
  • --allow-unauthenticated: Hàm này sẽ là hàm công khai, cho phép tất cả người gọi mà không cần kiểm tra quá trình xác thực.
  • Để tìm hiểu thêm, hãy xem phần gcloud functions deploy.

Để kiểm thử hàm này, bạn có thể nhấp vào URL httpsTrigger.url xuất hiện trong đầu ra của lệnh ở trên. Bạn cũng có thể truy xuất URL theo cách lập trình và gọi hàm bằng các lệnh sau:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Bạn sẽ nhận được kết quả sau:

Hello World! 👋

4. Viết một HTTP Cloud Function nhận các đối số

Các hàm sẽ linh hoạt hơn khi chấp nhận đối số. Hãy xác định một hàm mới hello_name hỗ trợ tham số name:

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

Hãy triển khai hàm mới này:

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Đầu ra của lệnh:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Để kiểm thử hàm này, bạn có thể nhấp vào URL httpsTrigger.url xuất hiện trong đầu ra của lệnh ở trên. Bạn cũng có thể truy xuất URL theo cách lập trình và gọi hàm bằng các lệnh sau:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Bạn sẽ nhận được kết quả mặc định:

Hello World! 🚀

Bạn nhận được kết quả mặc định vì đối số name chưa được đặt. Thêm một tham số vào URL:

curl -w "\n" $URL?name=YOUR%20NAME

Lần này, bạn sẽ nhận được câu trả lời tuỳ chỉnh:

Hello YOUR NAME! 🚀

Bước tiếp theo là thêm các kiểm thử đơn vị để đảm bảo các hàm của bạn vẫn hoạt động như dự kiến khi mã nguồn được cập nhật.

5. Viết bài kiểm thử

Các Cloud Functions HTTP trong Python được kiểm thử bằng mô-đun unittest trong thư viện chuẩn. Bạn không cần chạy trình mô phỏng hoặc mô phỏng khác để kiểm thử hàm của mình, chỉ cần mã Python thông thường.

Sau đây là ví dụ về một chương trình kiểm thử cho các hàm hello_worldhello_name:

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. Các bài kiểm thử Python được viết giống như các tệp Python khác. Chúng bắt đầu bằng một nhóm các lệnh nhập, sau đó xác định các lớp và hàm.
  2. Khai báo kiểm thử có dạng class TestHello(TestCase). Đây phải là một lớp kế thừa từ unittest.TestCase.
  3. Lớp kiểm thử có các phương thức, mỗi phương thức phải bắt đầu bằng test_, đại diện cho từng trường hợp kiểm thử.
  4. Mỗi trường hợp kiểm thử sẽ kiểm thử một trong các hàm của chúng ta bằng cách mô phỏng tham số request (tức là thay thế tham số đó bằng một đối tượng giả có dữ liệu cụ thể cần thiết cho quá trình kiểm thử).
  5. Sau khi gọi từng hàm, quy trình kiểm thử sẽ kiểm tra phản hồi HTTP để đảm bảo phản hồi đó đúng như chúng ta mong đợi.

main.py phụ thuộc vào flask, hãy đảm bảo bạn đã cài đặt khung Flask trong môi trường thử nghiệm:

pip install flask

Việc cài đặt Flask sẽ cho ra kết quả tương tự như sau:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Chạy các kiểm thử này cục bộ:

python -m unittest

Ba bài kiểm thử đơn vị này phải vượt qua:

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

Tiếp theo, bạn sẽ tạo một hàm mới trả về biểu trưng "Python Powered".

6. Viết Cloud Function HTTP "Python Powered"

Hãy làm cho hàm mới thú vị hơn một chút bằng cách trả về hình ảnh "Python Powered" cho mọi yêu cầu:

a7aaf656b78050fd.png

Sau đây là đoạn mã để thực hiện việc này:

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

Triển khai hàm python_powered mới:

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Đầu ra của lệnh:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Để kiểm thử hàm này, hãy nhấp vào URL httpsTrigger.url xuất hiện trong đầu ra lệnh ở trên. Nếu mọi thứ hoạt động bình thường, bạn sẽ thấy biểu trưng "Python Powered" trong một thẻ trình duyệt mới!

Tiếp theo, bạn sẽ tạo một ứng dụng để có thể chạy và dùng thử hàm cục bộ trước khi triển khai.

7. Chạy hàm cục bộ

Bạn có thể chạy một hàm HTTP cục bộ bằng cách tạo một ứng dụng web và gọi hàm đó trong một tuyến đường. Bạn có thể thêm tệp này vào cùng thư mục với hàm của mình. Tệp có tên web_app.py có nội dung sau:

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. Tệp này tạo một ứng dụng Flask.
  2. Thao tác này đăng ký một tuyến đường tại URL cơ sở được xử lý bằng một hàm có tên là index().
  3. Sau đó, hàm index() sẽ gọi hàm python_powered của chúng ta, truyền yêu cầu hiện tại cho hàm này.

Đảm bảo rằng bạn đã cài đặt khung Flask trong môi trường phát triển:

pip install flask

Việc cài đặt Flask sẽ cho ra kết quả tương tự như sau:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Để chạy ứng dụng này cục bộ, hãy chạy lệnh sau:

python web_app.py

Bây giờ, hãy sử dụng tính năng Xem trước trên web của Cloud Shell để kiểm thử ứng dụng web trong trình duyệt. Trong Cloud Shell, hãy nhấp vào nút "Xem trước trên web" rồi chọn "Xem trước trên cổng 8080":

6c9ff9e5c692c58e.gif

Cloud Shell sẽ mở URL xem trước trên dịch vụ proxy của nó trong một cửa sổ trình duyệt mới. Bản xem trước trên web chỉ cho phép truy cập vào tài khoản người dùng của bạn qua HTTPS. Nếu mọi thứ hoạt động bình thường, bạn sẽ thấy biểu trưng "Python Powered"!

8e5c3ead11cfd103.png

8. Xin chúc mừng!

b158ce75c3cccd6d.png

Bạn đã triển khai Cloud Functions HTTP, sử dụng các hàm thành ngữ xử lý yêu cầu web bằng khung Flask.

Giá của Cloud Functions dựa trên tần suất gọi hàm, bao gồm cả bậc miễn phí cho những hàm không chạy thường xuyên. Sau khi kiểm thử xong Cloud Functions, bạn có thể xoá các hàm này bằng cách sử dụng gcloud:

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

Bạn cũng có thể xoá các hàm này khỏi bảng điều khiển Cloud.

Chúng tôi hy vọng bạn sẽ thích sử dụng Cloud Functions bằng Python!