Pemrosesan latar belakang

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. Layanan Cloud Run yang berlangganan topik Pub/Sub tersebut akan dipicu.
  5. Layanan Cloud Run menggunakan Cloud Translation untuk menerjemahkan teks.
  6. Layanan Cloud Run menyimpan hasilnya di Firestore.

Tutorial ini ditujukan bagi siapa saja yang tertarik untuk mempelajari pemrosesan di latar belakang dengan Google Cloud. Tidak diperlukan pengalaman sebelumnya dengan Pub/Sub, Firestore, Cloud Run. Namun, untuk memahami semua kode, pengalaman dengan Java dan HTML akan sangat membantu.

Tujuan

  • Pahami dan deploy layanan Cloud Run.
  • 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, 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. Instal Google Cloud CLI.

  6. Jika Anda menggunakan penyedia identitas (IdP) eksternal, Anda harus login ke gcloud CLI dengan identitas gabungan Anda terlebih dahulu.

  7. Untuk melakukan inisialisasi gcloud CLI, jalankan perintah berikut:

    gcloud init
  8. 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

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

  10. Enable the Firestore, 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

  11. Instal Google Cloud CLI.

  12. Jika Anda menggunakan penyedia identitas (IdP) eksternal, Anda harus login ke gcloud CLI dengan identitas gabungan Anda terlebih dahulu.

  13. Untuk melakukan inisialisasi gcloud CLI, jalankan perintah berikut:

    gcloud init
  14. Perbarui komponen gcloud:
    gcloud components update
  15. Siapkan lingkungan pengembangan Anda.

    Buka panduan penyiapan Java

Menyiapkan aplikasi

  1. Di jendela terminal, clone repositori aplikasi contoh ke komputer lokal Anda:

    git clone https://github.com/GoogleCloudPlatform/getting-started-java.git

    Atau, Anda dapat mendownload contoh dalam file ZIP dan mengekstraknya.

  2. Ubah ke direktori yang berisi kode contoh pemrosesan di latar belakang:

    cd getting-started-java/background

Memahami aplikasi

Ada dua komponen utama untuk aplikasi web:

  • Server HTTP Java untuk menangani permintaan web. Server memiliki dua endpoint berikut:
    • /translate
      • GET (dengan menggunakan browser web): Menampilkan 10 permintaan terjemahan yang diproses terbaru yang dikirimkan oleh pengguna.
      • POST (dengan langganan Pub/Sub): Memproses permintaan terjemahan menggunakan Cloud Translation API dan menyimpan hasilnya di Firestore.
    • /create: Formulir untuk mengirimkan permintaan terjemahan baru.
  • Klien layanan yang memproses permintaan terjemahan yang dikirimkan oleh formulir web. Ada tiga klien yang bekerja sama:
    • Pub/Sub: Saat formulir web dikirim oleh pengguna, klien Pub/Sub akan memublikasikan pesan dengan detail permintaan. Langganan yang dibuat dalam tutorial ini akan meneruskan pesan tersebut ke endpoint Cloud Run yang Anda buat untuk melakukan terjemahan.
    • Terjemahan: Klien ini menangani permintaan Pub/Sub dengan melakukan terjemahan.
    • Firestore: Setelah terjemahan selesai, klien ini menyimpan data permintaan beserta terjemahan di Firestore. Klien ini juga membaca permintaan terbaru di endpoint /translate utama.

Memahami kode Cloud Run

  • Aplikasi Cloud Run memiliki dependensi pada Firestore, Translation, dan Pub/Sub.

    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-firestore</artifactId>
      <version>3.13.2</version>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-translate</artifactId>
      <version>2.20.0</version>
    </dependency>
    
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-pubsub</artifactId>
      <version>1.123.17</version>
    </dependency>
  • Klien Firestore, Translation, dan Pub/Sub global diinisialisasi sehingga dapat digunakan kembali di antara pemanggilan. Dengan begitu, Anda tidak perlu menginisialisasi klien baru untuk setiap pemanggilan, yang akan memperlambat eksekusi.

    @WebListener("Creates Firestore and TranslateServlet service clients for reuse between requests.")
    public class BackgroundContextListener implements ServletContextListener {
      @Override
      public void contextDestroyed(javax.servlet.ServletContextEvent event) {}
    
      @Override
      public void contextInitialized(ServletContextEvent event) {
        String firestoreProjectId = System.getenv("FIRESTORE_CLOUD_PROJECT");
        Firestore firestore = (Firestore) event.getServletContext().getAttribute("firestore");
        if (firestore == null) {
          firestore =
              FirestoreOptions.getDefaultInstance().toBuilder()
                  .setProjectId(firestoreProjectId)
                  .build()
                  .getService();
          event.getServletContext().setAttribute("firestore", firestore);
        }
    
        Translate translate = (Translate) event.getServletContext().getAttribute("translate");
        if (translate == null) {
          translate = TranslateOptions.getDefaultInstance().getService();
          event.getServletContext().setAttribute("translate", translate);
        }
    
        String topicId = System.getenv("PUBSUB_TOPIC");
        TopicName topicName = TopicName.of(firestoreProjectId, topicId);
        Publisher publisher = (Publisher) event.getServletContext().getAttribute("publisher");
        if (publisher == null) {
          try {
            publisher = Publisher.newBuilder(topicName).build();
            event.getServletContext().setAttribute("publisher", publisher);
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      }
    }
  • Handler indeks (/) mendapatkan semua terjemahan yang ada dari Firestore dan mengisi template HTML dengan daftar:

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
      Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore");
      CollectionReference translations = firestore.collection("translations");
      QuerySnapshot snapshot;
      try {
        snapshot = translations.limit(10).get().get();
      } catch (InterruptedException | ExecutionException e) {
        throw new ServletException("Exception retrieving documents from Firestore.", e);
      }
      List<TranslateMessage> translateMessages = Lists.newArrayList();
      List<QueryDocumentSnapshot> documents = Lists.newArrayList(snapshot.getDocuments());
      documents.sort(Comparator.comparing(DocumentSnapshot::getCreateTime));
    
      for (DocumentSnapshot document : Lists.reverse(documents)) {
        String encoded = gson.toJson(document.getData());
        TranslateMessage message = gson.fromJson(encoded, TranslateMessage.class);
        message.setData(decode(message.getData()));
        translateMessages.add(message);
      }
      req.setAttribute("messages", translateMessages);
      req.setAttribute("page", "list");
      req.getRequestDispatcher("/base.jsp").forward(req, resp);
    }
  • Terjemahan baru diminta dengan mengirimkan formulir HTML. Penanganan terjemahan permintaan, yang terdaftar di /create, mengurai pengiriman formulir, memvalidasi permintaan, dan memublikasikan pesan ke Pub/Sub:

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
      String text = req.getParameter("data");
      String sourceLang = req.getParameter("sourceLang");
      String targetLang = req.getParameter("targetLang");
    
      Enumeration<String> paramNames = req.getParameterNames();
      while (paramNames.hasMoreElements()) {
        String paramName = paramNames.nextElement();
        logger.warning("Param name: " + paramName + " = " + req.getParameter(paramName));
      }
    
      Publisher publisher = (Publisher) getServletContext().getAttribute("publisher");
    
      PubsubMessage pubsubMessage =
          PubsubMessage.newBuilder()
              .setData(ByteString.copyFromUtf8(text))
              .putAttributes("sourceLang", sourceLang)
              .putAttributes("targetLang", targetLang)
              .build();
    
      try {
        publisher.publish(pubsubMessage).get();
      } catch (InterruptedException | ExecutionException e) {
        throw new ServletException("Exception publishing message to topic.", e);
      }
    
      resp.sendRedirect("/");
    }
  • Langganan Pub/Sub yang Anda buat akan meneruskan permintaan tersebut ke endpoint Cloud Run, yang mem-parsing pesan Pub/Sub untuk mendapatkan teks yang akan diterjemahkan dan bahasa target yang diinginkan. Kemudian, Translation API menerjemahkan string ke bahasa yang Anda pilih.

    String body = req.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
    
    PubSubMessage pubsubMessage = gson.fromJson(body, PubSubMessage.class);
    TranslateMessage message = pubsubMessage.getMessage();
    
    // Use Translate service client to translate the message.
    Translate translate = (Translate) this.getServletContext().getAttribute("translate");
    message.setData(decode(message.getData()));
    Translation translation =
        translate.translate(
            message.getData(),
            Translate.TranslateOption.sourceLanguage(message.getAttributes().getSourceLang()),
            Translate.TranslateOption.targetLanguage(message.getAttributes().getTargetLang()));
  • Aplikasi menyimpan data terjemahan dalam dokumen baru yang dibuatnya di Firestore.

    // Use Firestore service client to store the translation in Firestore.
    Firestore firestore = (Firestore) this.getServletContext().getAttribute("firestore");
    
    CollectionReference translations = firestore.collection("translations");
    
    ApiFuture<WriteResult> setFuture = translations.document().set(message, SetOptions.merge());
    
    setFuture.get();
    resp.getWriter().write(translation.getTranslatedText());

Men-deploy aplikasi Cloud Run

  1. Pilih Nama Topik Pub/Sub dan buat Token Verifikasi Pub/Sub menggunakan uuidgen atau generator UUID online seperti uuidgenerator.net. Token ini akan memastikan bahwa endpoint Cloud Run hanya menerima permintaan dari langganan Pub/Sub yang Anda buat.

    export PUBSUB_TOPIC=background-translate
    export PUBSUB_VERIFICATION_TOKEN=your-verification-token
  2. Buat topik Pub/Sub:

     gcloud pubsub topics create $PUBSUB_TOPIC
    
    • Ganti MY_PROJECT di file pom.xml dengan project ID Google Cloud Anda.
  3. Bangun dan deploy image kode Anda ke GCR (repositori image) dengan plugin Maven Jib.

     mvn clean package jib:build
    
  4. Men-deploy aplikasi ke Cloud Run:

    gcloud run deploy background --image gcr.io/MY_PROJECT/background \
          --platform managed --region us-central1 --memory 512M \
          --update-env-vars PUBSUB_TOPIC=$PUBSUB_TOPIC,PUBSUB_VERIFICATION_TOKEN=$PUBSUB_VERIFICATION_TOKEN

    Dengan MY_PROJECT adalah nama project Google Cloud yang Anda buat. Perintah ini menampilkan endpoint tempat langganan Pub/Sub Anda mengirimkan permintaan terjemahan. Catat endpoint ini, karena Anda akan memerlukannya untuk membuat langganan Pub/Sub, dan Anda akan membuka endpoint di browser untuk meminta terjemahan baru.

Menguji aplikasi

Setelah men-deploy layanan Cloud Run, coba minta terjemahan.

  1. Untuk melihat aplikasi di browser, buka endpoint Cloud Run yang Anda buat sebelumnya.

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

  2. Klik + Minta Terjemahan, isi formulir permintaan, lalu klik Kirim.

  3. Pengiriman otomatis membawa Anda kembali ke jalur /translate, tetapi terjemahan baru mungkin belum muncul. 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 layanan Cloud Run atau tidak melihat terjemahan baru, periksa hal berikut:

  • Periksa apakah perintah gcloud run deploy berhasil diselesaikan dan tidak menghasilkan error. Jika ada error (misalnya, message=Build failed), perbaiki, lalu coba jalankan lagi.

  • Periksa error dalam log:

    1. Di konsol Google Cloud , buka halaman Cloud Run.

      Buka halaman Cloud Run

    2. Klik nama layanan, background.

    3. Klik Logs.

Pembersihan

Menghapus 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 layanan Cloud Run.

  • Hapus layanan Cloud Run yang Anda buat dalam tutorial ini:

    gcloud run services delete --region=$region background

Langkah berikutnya