Pub/Sub 메시지 쓰기 및 응답

리전 ID

REGION_ID는 앱을 만들 때 선택한 리전을 기준으로 Google에서 할당하는 축약된 코드입니다. 일부 리전 ID는 일반적으로 사용되는 국가 및 주/도 코드와 비슷하게 표시될 수 있지만 코드는 국가 또는 주/도와 일치하지 않습니다. 2020년 2월 이후에 생성된 앱의 경우 REGION_ID.r이 App Engine URL에 포함됩니다. 이 날짜 이전에 만든 기존 앱의 경우 URL에서 리전 ID는 선택사항입니다.

리전 ID에 대해 자세히 알아보세요.

Pub/Sub는 애플리케이션 사이에서 안정적인 다대다 비동기 메시징 기능을 제공합니다. 게시자 애플리케이션은 메시지를 주제로 보낼 수 있으며 다른 애플리케이션은 주제를 구독하여 메시지를 수신할 수 있습니다.

이 문서에서는 Cloud 클라이언트 라이브러리를 사용하여 Java 8 앱에서 Pub/Sub 메시지를 전송 및 수신하는 방법을 설명합니다.

기본 요건

  • App Engine에서 자바 8용 'Hello, World!'의 안내를 따라 환경과 프로젝트를 설정하고 App Engine 자바 8 앱의 구조를 알아봅니다.
  • 이 문서에 설명된 샘플 애플리케이션을 실행할 때 프로젝트 ID가 필요하므로 기록합니다.

샘플 앱 클론

샘플 앱을 로컬 머신에 복사한 후 pubsub 디렉터리로 이동합니다.

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

주제 및 구독 만들기

주제 및 구독을 만듭니다. 이때 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

YOUR_TOKEN을 무작위의 비밀 토큰으로 바꿉니다. 내보내기 엔드포인트에서 이 토큰을 사용해 요청을 확인합니다.

인증을 통해 Pub/Sub를 사용하려면 다른 구독을 만듭니다.

# 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'
`YOUR-SERVICE-ACCOUNT-EMAIL`을 [서비스 계정](/appengine/docs/flexible/configure-service-accounts) 이메일로 바꿉니다. ### 환경 변수 설정 {:edit_appyaml} `appengine-web.xml` 파일을 수정하여 주제 및 확인 토큰의 환경 변수를 설정합니다.
<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>
## 코드 검토 샘플 앱은 [Cloud 클라이언트 라이브러리](https://googleapis.dev/java/google-cloud-clients/latest/index.html){: class="external"}를 사용합니다. 샘플 앱은 `appengine-web.xml` 파일에서 설정한 값을 사용하여 환경 변수를 구성합니다. 푸시 요청 핸들러는 이러한 값을 사용하여 요청이 Pub/Sub에서 수신되었으며 신뢰할 수 있는 소스가 보낸 것인지 확인합니다. String pubsubVerificationToken = System.getenv("PUBSUB_VERIFICATION_TOKEN"); 샘플 앱은 메시지를 저장할 Cloud Datastore 데이터베이스 인스턴스를 유지합니다. `PubSubPush` 서블릿은 푸시된 메시지를 수신하여 `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();
  }
}
`PubSubPublish` 서블릿은 App Engine 웹 앱과 상호작용하여 새 메시지를 게시하고 수신된 메시지를 표시합니다.
/*
 * 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;
  }
}
## 로컬에서 샘플 실행 {: #run_the_sample_locally} 로컬에서 실행할 때 Google Cloud CLI를 사용하여 Google Cloud API 사용에 대한 인증을 제공할 수 있습니다. [기본 요건](#prerequisites)의 설명대로 환경을 설정했으면 이 인증을 제공하는 `gcloud init` 명령어가 이미 실행되고 있습니다. mvn clean package 그런 다음 애플리케이션을 시작하기 전에 환경 변수를 설정합니다. export PUBSUB_VERIFICATION_TOKEN=[your-verification-token] export PUBSUB_TOPIC=[your-topic] mvn appengine:run ### 푸시 알림 시뮬레이션 {: simulate_push_notifications} 애플리케이션은 로컬에서 메시지를 보낼 수 있지만 로컬에서 푸시 메시지를 받지는 못합니다. 하지만 로컬 푸시 알림 엔드포인트에 HTTP 요청을 전송하면 푸시 메시지를 시뮬레이션할 수 있습니다. 샘플에는 `sample_message.json` 파일이 포함되어 있습니다. `curl` 또는 [`httpie`](https://github.com/jkbrzt/httpie){: class="external"} 클라이언트를 사용해 HTTP `POST` 요청을 전송할 수 있습니다. curl -H "Content-Type: application/json" -i --data @sample_message.json "localhost:8080/pubsub/push?token=[your-token]" 또는 http POST ":8080/pubsub/push?token=[your-token]" < sample_message.json 응답: HTTP/1.1 200 OK Date: Wed, 26 Apr 2017 00:03:28 GMT Content-Length: 0 Server: Jetty(9.3.8.v20160314) 요청이 완료되면 `localhost:8080`을 새로 고친 후 수신 메시지 목록에서 해당 메시지를 확인할 수 있습니다. ## App Engine에서 실행 {: #run_on_app_engine} To deploy the demo app to App Engine by using the `gcloud` 명령줄 도구를 사용하여 App Engine에 데모 앱을 배포하려면 `pom.xml`이 있는 디렉터리에서 다음 명령어를 실행합니다.
mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID

PROJECT_ID를 Google Cloud 프로젝트의 ID로 바꿉니다. pom.xml 파일에 이미 프로젝트 ID가 지정된 경우 실행할 명령어에 -Dapp.deploy.projectId 속성을 포함하지 않아도 됩니다.

이제 https://PROJECT_ID.REGION_ID.r.appspot.com에서 애플리케이션에 액세스할 수 있습니다. 양식을 사용하여 메시지를 제출할 수 있지만 애플리케이션에서 어떤 인스턴스가 알림을 수신하는지 보장하지는 못합니다. 여러 메시지를 전송하고 페이지를 새로 고쳐 수신된 메시지를 확인할 수 있습니다.