Menulis dan Merespons Pesan Pub/Sub

ID region

REGION_ID adalah kode singkat yang ditetapkan Google berdasarkan region yang Anda pilih saat membuat aplikasi. Kode ini tidak sesuai dengan negara atau provinsi, meskipun beberapa ID region mungkin tampak mirip dengan kode negara dan provinsi yang umum digunakan. Untuk aplikasi yang dibuat setelah Februari 2020, REGION_ID.r disertakan dalam URL App Engine. Untuk aplikasi lama yang dibuat sebelum tanggal tersebut, ID region bersifat opsional dalam URL.

Pelajari ID region lebih lanjut.

Pub/Sub menyediakan pesan yang andal, many-to-many, dan asinkron antara aplikasi. Aplikasi penayang dapat mengirim pesan ke topik, dan aplikasi lain dapat berlangganan topik tersebut untuk menerima pesan.

Dokumen ini menjelaskan cara menggunakan Library Klien Cloud untuk mengirim dan menerima pesan Pub/Sub di aplikasi Java 8.

Prasyarat

  • Ikuti petunjuk di "Hello, World!" untuk Java 8 di App Engine guna menyiapkan lingkungan dan project Anda, serta memahami cara struktur aplikasi App Engine Java 8.
  • Tuliskan dan simpan project ID Anda karena Anda akan membutuhkannya untuk menjalankan contoh aplikasi yang dijelaskan dalam dokumen ini.

Membuat clone aplikasi contoh

Salin aplikasi contoh ke komputer lokal Anda, lalu buka direktori pubsub:

git clone https://github.com/GoogleCloudPlatform/java-docs-samples
cd java-docs-samples/appengine-java8/pubsub

Membuat topik dan langganan

Buat topik dan langganan, termasuk menentukan endpoint yang harus dikirimi permintaan oleh server Pub/Sub:

 bv
# Configure the topic
gcloud pubsub topics create YOUR_TOPIC_NAME

# Configure the push subscription
gcloud pubsub subscriptions create YOUR_SUBSCRIPTION_NAME \
    --topic=YOUR_TOPIC_NAME \
    --push-endpoint=https://YOUR_PROJECT_ID.REGION_ID.r.appspot.com/push-handlers/receive_messages?token=YOUR_TOKEN \
    --ack-deadline=10

Ganti YOUR_TOKEN dengan token acak rahasia. Endpoint push menggunakannya untuk memverifikasi permintaan.

Untuk menggunakan Pub/Sub dengan autentikasi, buat langganan lain:

# Configure the push subscription
gcloud pubsub subscriptions create YOUR_SUBSCRIPTION_NAME \
    --topic=YOUR_TOPIC_NAME \
    --push-auth-service-account=YOUR-SERVICE-ACCOUNT-EMAIL\
    --push-auth-token-audience=OPTIONAL_AUDIENCE_OVERRIDE\
    --push-endpoint=https://YOUR_PROJECT_ID.REGION_ID.r.appspot.com/push-handlers/receive_messages?token=YOUR_TOKEN \
    --ack-deadline=10
# Your service agent
# `service-{PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com` needs to have the
# `iam.serviceAccountTokenCreator` role.
PUBSUB_SERVICE_ACCOUNT="service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:${PUBSUB_SERVICE_ACCOUNT}"\
    --role='roles/iam.serviceAccountTokenCreator'

Ganti YOUR-SERVICE-ACCOUNT-EMAIL dengan email akun layanan Anda.

Menyetel variabel lingkungan

Edit file appengine-web.xml guna menetapkan variabel lingkungan untuk topik dan token verifikasi Anda:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
  <threadsafe>true</threadsafe>
  <runtime>java8</runtime>

  <env-variables>
    <env-var name="PUBSUB_TOPIC" value="your-topic" />
    <env-var name="PUBSUB_VERIFICATION_TOKEN" value="your-verification-token" />
  </env-variables>
</appengine-web-app>

Peninjauan kode

Aplikasi contoh menggunakan Library Klien Cloud.

Aplikasi contoh menggunakan nilai yang Anda tetapkan dalam file appengine-web.xml untuk mengonfigurasi variabel lingkungan. Pengendali permintaan push menggunakan nilai ini untuk mengonfirmasi bahwa permintaan berasal dari Pub/Sub dan berasal dari sumber tepercaya:

String pubsubVerificationToken = System.getenv("PUBSUB_VERIFICATION_TOKEN");

Aplikasi contoh mempertahankan instance database Cloud Datastore untuk menyimpan pesan. Servlet PubSubPush menerima pesan yang dikirim dan menambahkannya ke instance database messageRepository:

@WebServlet(value = "/pubsub/push")
public class PubSubPush extends HttpServlet {

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws IOException, ServletException {
    String pubsubVerificationToken = System.getenv("PUBSUB_VERIFICATION_TOKEN");
    // Do not process message if request token does not match pubsubVerificationToken
    if (req.getParameter("token").compareTo(pubsubVerificationToken) != 0) {
      resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
      return;
    }
    // parse message object from "message" field in the request body json
    // decode message data from base64
    Message message = getMessage(req);
    try {
      messageRepository.save(message);
      // 200, 201, 204, 102 status codes are interpreted as success by the Pub/Sub system
      resp.setStatus(102);
    } catch (Exception e) {
      resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }
  }

  private Message getMessage(HttpServletRequest request) throws IOException {
    String requestBody = request.getReader().lines().collect(Collectors.joining("\n"));
    JsonElement jsonRoot = JsonParser.parseString(requestBody).getAsJsonObject();
    String messageStr = jsonRoot.getAsJsonObject().get("message").toString();
    Message message = gson.fromJson(messageStr, Message.class);
    // decode from base64
    String decoded = decode(message.getData());
    message.setData(decoded);
    return message;
  }

  private String decode(String data) {
    return new String(Base64.getDecoder().decode(data));
  }

  private final Gson gson = new Gson();
  private MessageRepository messageRepository;

  PubSubPush(MessageRepository messageRepository) {
    this.messageRepository = messageRepository;
  }

  public PubSubPush() {
    this.messageRepository = MessageRepositoryImpl.getInstance();
  }
}

Servlet PubSubPublish berinteraksi dengan aplikasi web App Engine untuk memublikasikan pesan baru dan menampilkan pesan yang diterima:

/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.appengine.pubsub;

import com.google.cloud.ServiceOptions;
import com.google.cloud.pubsub.v1.Publisher;
import com.google.protobuf.ByteString;
import com.google.pubsub.v1.ProjectTopicName;
import com.google.pubsub.v1.PubsubMessage;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpStatus;

@WebServlet(name = "Publish with PubSub", value = "/pubsub/publish")
public class PubSubPublish extends HttpServlet {

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws IOException, ServletException {
    Publisher publisher = this.publisher;
    try {
      String topicId = System.getenv("PUBSUB_TOPIC");
      // create a publisher on the topic
      if (publisher == null) {
        ProjectTopicName topicName =
            ProjectTopicName.newBuilder()
                .setProject(ServiceOptions.getDefaultProjectId())
                .setTopic(topicId)
                .build();
        publisher = Publisher.newBuilder(topicName).build();
      }
      // construct a pubsub message from the payload
      final String payload = req.getParameter("payload");
      PubsubMessage pubsubMessage =
          PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(payload)).build();

      publisher.publish(pubsubMessage);
      // redirect to home page
      resp.sendRedirect("/");
    } catch (Exception e) {
      resp.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    }
  }

  private Publisher publisher;

  public PubSubPublish() {}

  PubSubPublish(Publisher publisher) {
    this.publisher = publisher;
  }
}

Menjalankan contoh secara lokal

Saat berjalan secara lokal, Anda dapat menggunakan Google Cloud CLI untuk memberikan autentikasi agar dapat menggunakan Google Cloud API. Dengan asumsi Anda menyiapkan lingkungan seperti yang dijelaskan dalam Prasyarat, Anda telah menjalankan perintah gcloud init, yang menyediakan autentikasi ini.

mvn clean package

Kemudian, tetapkan variabel lingkungan sebelum memulai aplikasi Anda:

export PUBSUB_VERIFICATION_TOKEN=[your-verification-token]
export PUBSUB_TOPIC=[your-topic]
mvn appengine:run

Menyimulasikan notifikasi push

Aplikasi dapat mengirim pesan secara lokal, tetapi tidak dapat menerima pesan push secara lokal. Namun, Anda dapat menyimulasikan pesan push dengan membuat permintaan HTTP ke endpoint notifikasi push lokal. Contoh ini menyertakan file sample_message.json.

Anda dapat menggunakan klien curl atau httpie untuk mengirim permintaan POST HTTP:

curl -H "Content-Type: application/json" -i --data @sample_message.json "localhost:8080/pubsub/push?token=[your-token]"

Atau

http POST ":8080/pubsub/push?token=[your-token]" < sample_message.json

Respons:

HTTP/1.1 200 OK
Date: Wed, 26 Apr 2017 00:03:28 GMT
Content-Length: 0
Server: Jetty(9.3.8.v20160314)

Setelah permintaan selesai, Anda dapat memuat ulang localhost:8080 dan melihat pesan dalam daftar pesan yang diterima.

Menjalankan di App Engine

Untuk men-deploy aplikasi demo ke App Engine menggunakan alat command line gcloud, jalankan perintah berikut dari direktori tempat pom.xml Anda berada:

mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID

Ganti PROJECT_ID dengan ID project Google Cloud Anda. Jika file pom.xml sudah menentukan project ID, Anda tidak perlu menyertakan properti -Dapp.deploy.projectId dalam perintah yang dijalankan singkat ini.

Anda kini dapat mengakses aplikasi di https://PROJECT_ID.REGION_ID.r.appspot.com. Anda dapat menggunakan formulir ini untuk mengirim pesan, tetapi tidak ada jaminan terkait instance mana dari aplikasi Anda yang akan menerima notifikasi tersebut. Anda dapat mengirim beberapa pesan dan memuat ulang halaman untuk melihat pesan yang diterima.