Como gravar e responder a mensagens do Pub/Sub

ID da região

O REGION_ID é um código abreviado que o Google atribui com base na região que você selecionou ao criar o aplicativo. O código não corresponde a um país ou estado, ainda que alguns IDs de região sejam semelhantes aos códigos de país e estado geralmente usados. Para apps criados após fevereiro de 2020, o REGION_ID.r está incluído nos URLs do App Engine. Para apps existentes criados antes dessa data, o ID da região é opcional no URL.

Saiba mais sobre IDs de região.

O Pub/Sub mensagens assíncronas confiáveis de muitos para muitos entre os aplicativos. Os aplicativos do publisher podem enviar mensagens para um tópico e outros aplicativos podem assinar esse tópico para receber as mensagens.

Este documento descreve como usar a biblioteca de cliente do Cloud para enviar e receber mensagens do Pub/Sub em um aplicativo Java.

Pré-requisitos

  • Siga as instruções em "Olá, mundo!" para Java 8 no App Engine para configurar seu ambiente e projeto e entender como os aplicativos Java 8 do App Engine estão estruturados.
  • Anote e guarde o ID do projeto. Você precisará dele para executar o aplicativo de amostra descrito neste documento.

Como clonar o app de amostra

Copie os aplicativos de amostra para sua máquina local e navegue até o diretório pubsub:

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

Como criar um tópico e uma assinatura

Crie um tópico e uma assinatura. Isso inclui especificar o endpoint que receberá as solicitações do 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

Substitua YOUR_TOKEN por um token aleatório secreto. Ele é usado pelo endpoint do push para verificar as solicitações.

Para usar o Pub/Sub com autenticação, crie outra assinatura:

# 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'
Substitua `YOUR-SERVICE-ACCOUNT-EMAIL` pelo e-mail da [conta de serviço](/appengine/docs/flexible/configure-service-accounts). ### Como configurar variáveis de ambiente {: edit_appyaml} Edite o arquivo ` appengine-web.xml ` para definir as variáveis de ambiente para seu tópico e token de verificação:
<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>
## Revisão de código O aplicativo de exemplo usa a [Biblioteca de Cliente do Cloud](https://googleapis.dev/java/google-cloud-clients/latest/index.html){: class="external"}. O aplicativo de amostra usa os valores definidos no arquivo appengine-web.xml para configurar variáveis de ambiente. Esses valores são usados pelo gerenciador da solicitação de push para confirmar que a solicitação veio do Pub/Sub e foi originada de uma fonte confiável: String pubsubVerificationToken = System.getenv("PUBSUB_VERIFICATION_TOKEN");{101 } O aplicativo de exemplo mantém uma instância de banco de dados do Cloud Datastore para armazenar mensagens. O servlet PubSubPush recebe mensagens enviadas e as adiciona à instância do banco de dados 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();
  }
}
O servlet PubSubPublish interage com o aplicativo da Web do App Engine para publicar novas mensagens e exibir as mensagens recebidas:
/*
 * 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;
  }
}
## Como executar a amostra localmente {: #run_the_sample_locally} Ao executar localmente, você pode usar a Google Cloud CLI para fornecer autenticação e usar as APIs do Google Cloud. Se o ambiente foi configurado conforme descrito em [Pré-requisitos](#pré-requisitos), você já terá executado o comando "gcloud init", que fornece essa autenticação. mvn clean package Em seguida, defina as variáveis de ambiente antes de iniciar o aplicativo: export PUBSUB_VERIFICATION_TOKEN=[your-verification-token] export PUBSUB_TOPIC=[your-topic] mvn appengine:run ### Simulação de notificações push {: simulate_push_notifications} O aplicativo pode enviar mensagens localmente, mas não receber mensagens push localmente. É possível simular uma mensagem de push com uma solicitação HTTP para o endpoint da notificação local. A amostra inclui o arquivo `sample_message.json`. É possível usar `curl` ou um cliente [`httpie`](https://github.com) /jkbrzt/httpie){: class="external"} para enviar uma solicitação HTTP `POST`: curl -H "Content-Type: application/json" -i --data @sample_message.json "localhost:8080/pubsub/push?token=[your-token]" Ou http POST ":8080/pubsub/push?token=[your-token]" < sample_message.json Resposta: HTTP/1.1 200 OK Data : Quarta-feira, 26 de abril de 2017 00:03:28 GMT Duração do conteúdo: 0 Servidor: Jetty(9.3.8.v20160314) Depois que a solicitação for concluída, atualize "localhost:8080" e veja a mensagem na lista de mensagens recebidas. ## Como executar no App Engine {: #run_on_app_engine} Para implantar o aplicativo de demonstração no App Engine com a ferramenta de linha de comando `gcloud`, execute o seguinte comando no diretório em que O arquivo pom.xml está localizado:
mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID

Substitua PROJECT_ID pelo ID do projeto do Google Cloud. Se o arquivo pom.xmlespecificar o ID do projeto, não será necessário incluir a propriedade -Dapp.deploy.projectId no comando executado.

Agora, é possível acessar o aplicativo em https://PROJECT_ID.REGION_ID.r.appspot.com. Use o formulário para enviar mensagens. No entanto, não há garantias sobre qual instância do seu aplicativo receberá a notificação. É possível enviar várias mensagens e atualizar a página para ver a mensagem recebida.