Pemrosesan latar belakang dengan Python

Banyak aplikasi perlu melakukan pemrosesan di latar belakang di luar konteks permintaan web. Tutorial ini membuat aplikasi web yang memungkinkan pengguna memasukkan teks untuk diterjemahkan, lalu menampilkan daftar terjemahan sebelumnya. Terjemahan dilakukan dalam proses latar belakang untuk menghindari pemblokiran permintaan pengguna.

Diagram berikut menggambarkan proses permintaan terjemahan.

Diagram arsitektur.

Berikut urutan peristiwa cara kerja aplikasi tutorial:

  1. Buka halaman web untuk melihat daftar terjemahan sebelumnya, yang disimpan di Firestore.
  2. Minta terjemahan teks dengan memasukkan formulir HTML.
  3. Permintaan terjemahan dipublikasikan ke Pub/Sub.
  4. Fungsi Cloud Run yang berlangganan topik Pub/Sub tersebut akan dipicu.
  5. Fungsi Cloud Run menggunakan Cloud Translation untuk menerjemahkan teks.
  6. Fungsi Cloud Run menyimpan hasilnya di Firestore.

Tutorial ini ditujukan bagi siapa saja yang tertarik untuk mempelajari pemrosesan di latar belakang dengan Google Cloud. Anda tidak memerlukan pengalaman sebelumnya dengan Pub/Sub, Firestore, App Engine, atau fungsi Cloud Run. Namun, untuk memahami semua kode, akan sangat membantu jika Anda memiliki pengalaman dengan Python, JavaScript, dan HTML.

Tujuan

  • Memahami dan men-deploy fungsi Cloud Run.
  • Memahami dan men-deploy aplikasi App Engine.
  • Coba aplikasi.

Biaya

Dalam dokumen ini, Anda akan menggunakan komponen Google Cloudyang dapat ditagih berikut:

Untuk membuat perkiraan biaya berdasarkan proyeksi penggunaan Anda, gunakan kalkulator harga.

Pengguna Google Cloud baru mungkin memenuhi syarat untuk mendapatkan uji coba gratis.

Setelah menyelesaikan tugas yang dijelaskan dalam dokumen ini, Anda dapat menghindari penagihan berkelanjutan dengan menghapus resource yang Anda buat. Untuk mengetahui informasi selengkapnya, baca bagian Pembersihan.

Sebelum memulai

  1. Login ke akun Google Cloud Anda. Jika Anda baru menggunakan Google Cloud, buat akun untuk mengevaluasi performa produk kami dalam skenario dunia nyata. Pelanggan baru juga mendapatkan kredit gratis senilai $300 untuk menjalankan, menguji, dan men-deploy workload.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the Firestore, Cloud Run functions, Pub/Sub, and Cloud Translation APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  6. Verify that billing is enabled for your Google Cloud project.

  7. Enable the Firestore, Cloud Run functions, Pub/Sub, and Cloud Translation APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  8. Di konsol Google Cloud , buka aplikasi di Cloud Shell.

    Buka Cloud Shell

    Cloud Shell menyediakan akses command line ke resource cloud langsung dari browser. Buka Cloud Shell di browser Anda dan klik Proceed untuk mendownload kode contoh dan beralih ke direktori aplikasi.

  9. Di Cloud Shell, konfigurasi alat gcloud untuk menggunakan project Google Cloud Anda:
    # Configure gcloud for your project
    gcloud config set project YOUR_PROJECT_ID

Memahami fungsi Cloud Run

  • Fungsi dimulai dengan mengimpor beberapa dependensi seperti Firestore dan Translation.
    import base64
    import hashlib
    import json
    
    from google.cloud import firestore
    from google.cloud import translate_v2 as translate
  • Klien Firestore dan Translation global diinisialisasi sehingga dapat digunakan kembali di antara pemanggilan fungsi. Dengan begitu, Anda tidak perlu menginisialisasi klien baru untuk setiap pemanggilan fungsi, yang akan memperlambat eksekusi.
    # Get client objects once to reuse over multiple invocations.
    xlate = translate.Client()
    db = firestore.Client()
  • Translation API menerjemahkan string ke bahasa yang Anda pilih.
    def translate_string(from_string, to_language):
        """ Translates a string to a specified language.
    
        from_string - the original string before translation
    
        to_language - the language to translate to, as a two-letter code (e.g.,
            'en' for english, 'de' for german)
    
        Returns the translated string and the code for original language
        """
        result = xlate.translate(from_string, target_language=to_language)
        return result['translatedText'], result['detectedSourceLanguage']
  • Fungsi Cloud Run dimulai dengan mengurai pesan Pub/Sub untuk mendapatkan teks yang akan diterjemahkan dan bahasa target yang diinginkan.

    Kemudian, fungsi Cloud Run menerjemahkan teks dan menyimpannya di Firestore, menggunakan transaksi untuk memastikan tidak ada terjemahan duplikat.

    def document_name(message):
        """ Messages are saved in a Firestore database with document IDs generated
            from the original string and destination language. If the exact same
            translation is requested a second time, the result will overwrite the
            prior result.
    
            message - a dictionary with fields named Language and Original, and
                optionally other fields with any names
    
            Returns a unique name that is an allowed Firestore document ID
        """
        key = '{}/{}'.format(message['Language'], message['Original'])
        hashed = hashlib.sha512(key.encode()).digest()
    
        # Note that document IDs should not contain the '/' character
        name = base64.b64encode(hashed, altchars=b'+-').decode('utf-8')
        return name
    
    
    @firestore.transactional
    def update_database(transaction, message):
        name = document_name(message)
        doc_ref = db.collection('translations').document(document_id=name)
    
        try:
            doc_ref.get(transaction=transaction)
        except firestore.NotFound:
            return  # Don't replace an existing translation
    
        transaction.set(doc_ref, message)
    
    
    def translate_message(event, context):
        """ Process a pubsub message requesting a translation
        """
        message_data = base64.b64decode(event['data']).decode('utf-8')
        message = json.loads(message_data)
    
        from_string = message['Original']
        to_language = message['Language']
    
        to_string, from_language = translate_string(from_string, to_language)
    
        message['Translated'] = to_string
        message['OriginalLanguage'] = from_language
    
        transaction = db.transaction()
        update_database(transaction, message)

Men-deploy fungsi Cloud Run

  • Di Cloud Shell, di direktori function, deploy fungsi Cloud Run dengan pemicu Pub/Sub:

    gcloud functions deploy Translate --runtime=python37 \
    --entry-point=translate_message --trigger-topic=translate \
    --set-env-vars GOOGLE_CLOUD_PROJECT=YOUR_GOOGLE_CLOUD_PROJECT

    dengan YOUR_GOOGLE_CLOUD_PROJECT adalah Google Cloud project ID Anda.

Memahami aplikasi

Ada dua komponen utama untuk aplikasi web:

  • Server HTTP Python untuk menangani permintaan web. Server memiliki dua endpoint berikut:
    • /: Mencantumkan semua terjemahan yang ada dan menampilkan formulir yang dapat dikirim pengguna untuk meminta terjemahan baru.
    • /request-translation: Pengiriman formulir dikirim ke endpoint ini, yang memublikasikan permintaan ke Pub/Sub untuk diterjemahkan secara asinkron.
  • Template HTML yang diisi dengan terjemahan yang ada oleh server Python.

Server HTTP

  • Di direktori app, main.py dimulai dengan mengimpor dependensi, membuat aplikasi Flask, menginisialisasi klien Firestore dan Translation, serta menentukan daftar bahasa yang didukung:

    import json
    import os
    
    from flask import Flask, redirect, render_template, request
    from google.cloud import firestore, pubsub
    from markupsafe import escape
    
    
    app = Flask(__name__)
    
    # Get client objects to reuse over multiple invocations
    db = firestore.Client()
    publisher = pubsub.PublisherClient()
    
    # Keep this list of supported languages up to date
    ACCEPTABLE_LANGUAGES = ("de", "en", "es", "fr", "ja", "sw")
  • Handler indeks (/) mendapatkan semua terjemahan yang ada dari Firestore dan mengisi template HTML dengan daftar:

    @app.route("/", methods=["GET"])
    def index():
        """The home page has a list of prior translations and a form to
        ask for a new translation.
        """
    
        doc_list = []
        docs = db.collection("translations").stream()
        for doc in docs:
            doc_list.append(doc.to_dict())
    
        return render_template("index.html", translations=doc_list)
    
    
  • Terjemahan baru diminta dengan mengirimkan formulir HTML. Penanganan terjemahan permintaan, yang terdaftar di /request-translation, mengurai pengiriman formulir, memvalidasi permintaan, dan memublikasikan pesan ke Pub/Sub:

    @app.route("/request-translation", methods=["POST"])
    def translate():
        """Handle a request to translate a string (form field 'v') to a given
        language (form field 'lang'), by sending a PubSub message to a topic.
        """
        source_string = request.form.get("v", "")
        to_language = escape(request.form.get("lang", ""))
    
        if source_string == "":
            return "Invalid request, you must provide a value.", 400
    
        if to_language not in ACCEPTABLE_LANGUAGES:
            return f"Unsupported language: {to_language}", 400
    
        message = {
            "Original": source_string,
            "Language": to_language,
            "Translated": "",
            "OriginalLanguage": "",
        }
    
        topic_name = (
            f"projects/{os.getenv('GOOGLE_CLOUD_PROJECT')}/topics/translate"
        )
        publisher.publish(
            topic=topic_name, data=json.dumps(message).encode("utf-8")
        )
        return redirect("/")
    
    

Template HTML

Template HTML adalah dasar untuk halaman HTML yang ditampilkan kepada pengguna sehingga mereka dapat melihat terjemahan sebelumnya dan meminta terjemahan baru. Template diisi oleh server HTTP dengan daftar terjemahan yang ada.

  • Elemen <head> template HTML mencakup metadata, lembar gaya, dan JavaScript untuk halaman:
    <html>
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Translations</title>
    
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
        <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
        <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script>
            $(document).ready(function() {
                $("#translate-form").submit(function(e) {
                    e.preventDefault();
                    // Get value, make sure it's not empty.
                    if ($("#v").val() == "") {
                        return;
                    }
                    $.ajax({
                        type: "POST",
                        url: "/request-translation",
                        data: $(this).serialize(),
                        success: function(data) {
                            // Show snackbar.
                            console.log(data);
                            var notification = document.querySelector('.mdl-js-snackbar');
                            $("#snackbar").removeClass("mdl-color--red-100");
                            $("#snackbar").addClass("mdl-color--green-100");
                            notification.MaterialSnackbar.showSnackbar({
                                message: 'Translation requested'
                            });
                        },
                        error: function(data) {
                            // Show snackbar.
                            console.log("Error requesting translation");
                            var notification = document.querySelector('.mdl-js-snackbar');
                            $("#snackbar").removeClass("mdl-color--green-100");
                            $("#snackbar").addClass("mdl-color--red-100");
                            notification.MaterialSnackbar.showSnackbar({
                                message: 'Translation request failed'
                            });
                        }
                    });
                });
            });
        </script>
        <style>
            .lang {
                width: 50px;
            }
            .translate-form {
                display: inline;
            }
        </style>
    </head>

    Halaman menarik aset CSS dan JavaScript Material Design Lite (MDL). MDL memungkinkan Anda menambahkan tampilan dan nuansa Desain Material ke situs Anda.

    Halaman ini menggunakan JQuery untuk menunggu hingga dokumen selesai dimuat dan menetapkan pengendali pengiriman formulir. Setiap kali formulir terjemahan permintaan dikirimkan, halaman akan melakukan validasi formulir minimal untuk memeriksa apakah nilai tidak kosong, lalu mengirimkan permintaan asinkron ke endpoint /request-translation.

    Terakhir, snackbar MDL akan muncul untuk menunjukkan apakah permintaan berhasil atau mengalami error.

  • Isi HTML halaman menggunakan Tata letak MDL dan beberapa komponen MDL untuk menampilkan daftar terjemahan dan formulir untuk meminta terjemahan tambahan:
    <body>
        <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
            <header class="mdl-layout__header">
                <div class="mdl-layout__header-row">
                    <!-- Title -->
                    <span class="mdl-layout-title">Translate with Background Processing</span>
                </div>
            </header>
            <main class="mdl-layout__content">
                <div class="page-content">
                    <div class="mdl-grid">
                    <div class="mdl-cell mdl-cell--1-col"></div>
                        <div class="mdl-cell mdl-cell--3-col">
                            <form id="translate-form" class="translate-form">
                                <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
                                    <input class="mdl-textfield__input" type="text" id="v" name="v">
                                    <label class="mdl-textfield__label" for="v">Text to translate...</label>
                                </div>
                                <select class="mdl-textfield__input lang" name="lang">
                                    <option value="de">de</option>
                                    <option value="en">en</option>
                                    <option value="es">es</option>
                                    <option value="fr">fr</option>
                                    <option value="ja">ja</option>
                                    <option value="sw">sw</option>
                                </select>
                                <button class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent" type="submit"
                                    name="submit">Submit</button>
                            </form>
                        </div>
                        <div class="mdl-cell mdl-cell--8-col">
                            <table class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">
                                <thead>
                                    <tr>
                                        <th class="mdl-data-table__cell--non-numeric"><strong>Original</strong></th>
                                        <th class="mdl-data-table__cell--non-numeric"><strong>Translation</strong></th>
                                    </tr>
                                </thead>
                                <tbody>
                                {% for translation in translations %}
                                    <tr>
                                        <td class="mdl-data-table__cell--non-numeric">
                                            <span class="mdl-chip mdl-color--primary">
                                                <span class="mdl-chip__text mdl-color-text--white">{{ translation['OriginalLanguage'] }} </span>
                                            </span>
                                        {{ translation['Original'] }}
                                        </td>
                                        <td class="mdl-data-table__cell--non-numeric">
                                            <span class="mdl-chip mdl-color--accent">
                                                <span class="mdl-chip__text mdl-color-text--white">{{ translation['Language'] }} </span>
                                            </span>
                                            {{ translation['Translated'] }}
                                        </td>
                                    </tr>
                                {% endfor %}
                                </tbody>
                            </table>
                            <br/>
                            <button class="mdl-button mdl-js-button mdl-button--raised" type="button" onClick="window.location.reload();">
                                Refresh
                            </button>
                        </div>
                    </div>
                </div>
                <div aria-live="assertive" aria-atomic="true" aria-relevant="text" class="mdl-snackbar mdl-js-snackbar" id="snackbar">
                    <div class="mdl-snackbar__text mdl-color-text--black"></div>
                    <button type="button" class="mdl-snackbar__action"></button>
                </div>
            </main>
        </div>
    </body>
    
    </html>

Men-deploy aplikasi web

Anda dapat menggunakan lingkungan standar App Engine untuk membangun dan men-deploy aplikasi yang berjalan secara andal dalam beban berat dan dengan data dalam jumlah besar.

Tutorial ini menggunakan lingkungan standar App Engine untuk men-deploy frontend HTTP.

app.yaml mengonfigurasi aplikasi App Engine:

runtime: python312
  • Dari direktori yang sama dengan file app.yaml, deploy aplikasi Anda ke lingkungan standar App Engine:
    gcloud app deploy

Menguji aplikasi

Setelah men-deploy fungsi Cloud Run dan aplikasi App Engine, coba minta terjemahan.

  1. Untuk melihat aplikasi di browser,masukkan URL berikut:

    https://PROJECT_ID.REGION_ID.r.appspot.com

    Ganti kode berikut:

    Ada halaman dengan daftar terjemahan kosong dan formulir untuk meminta terjemahan baru.

  2. Di kolom Teks yang akan diterjemahkan, masukkan beberapa teks yang akan diterjemahkan, misalnya, Hello, World.
  3. Pilih bahasa dari daftar drop-down yang ingin Anda gunakan untuk menerjemahkan teks.
  4. Klik Kirim.
  5. Untuk memuat ulang halaman, klik Muat ulang . Ada baris baru dalam daftar terjemahan. Jika Anda tidak melihat terjemahan, tunggu beberapa detik lagi, lalu coba lagi. Jika Anda masih tidak melihat terjemahan, lihat bagian berikutnya tentang men-debug aplikasi.

Men-debug aplikasi

Jika Anda tidak dapat terhubung ke aplikasi App Engine atau tidak melihat terjemahan baru, periksa hal-hal berikut:

  1. Periksa apakah perintah deployment gcloud berhasil diselesaikan dan tidak menghasilkan error. Jika terjadi error, perbaiki, lalu coba men-deploy fungsi Cloud Run dan aplikasi App Engine lagi.
  2. Di konsol Google Cloud , buka halaman Logs Viewer.

    Buka halaman Logs Viewer
    1. Di menu drop-down Recently selected resources, klik GAE Application, lalu klik All module_id. Anda akan melihat daftar permintaan dari saat Anda mengunjungi aplikasi. Jika Anda tidak melihat daftar permintaan, pastikan Anda memilih Semua module_id dari daftar drop-down. Jika Anda melihat pesan error yang dicetak ke Google Cloud konsol, periksa apakah kode aplikasi Anda cocok dengan kode di bagian tentang memahami aplikasi.
    2. Di menu drop-down Recently selected resources, klik Cloud Function, lalu klik All function name. Anda akan melihat fungsi yang tercantum untuk setiap terjemahan yang diminta. Jika tidak, periksa apakah fungsi Cloud Run dan aplikasi App Engine menggunakan topik Pub/Sub yang sama:
      • Di file background/main.py, periksa apakah topic_name adalah "translate".
      • Saat Anda men-deploy fungsi Cloud Run, pastikan untuk menyertakan flag --trigger-topic=translate.

Pembersihan

Agar tidak perlu membayar biaya pada akun Google Cloud Anda untuk resource yang digunakan dalam tutorial ini, hapus project yang berisi resource tersebut, atau simpan project dan hapus setiap resource.

Menghapus Google Cloud project

  1. Di Konsol Google Cloud , buka halaman Manage resources.

    Buka Kelola resource

  2. Pada daftar project, pilih project yang ingin Anda hapus, lalu klik Delete.
  3. Pada dialog, ketik project ID, lalu klik Shut down untuk menghapus project.

Menghapus instance App Engine

  1. Di konsol Google Cloud , buka halaman Versions untuk App Engine.

    Buka Versi

  2. Pilih kotak centang untuk versi aplikasi non-default yang ingin Anda hapus.
  3. Untuk menghapus versi aplikasi, klik Hapus.

Menghapus fungsi Cloud Run

  • Hapus fungsi Cloud Run yang Anda buat dalam tutorial ini:
    gcloud functions delete Translate

Langkah berikutnya