Como acionar o envio por push no Pub/Sub

É possível usar o Pub/Sub para enviar mensagens ao endpoint do serviço do Cloud Run, em que as mensagens são entregues posteriormente aos contêineres como solicitações HTTP. Não é possível usar assinaturas de pull do Pub/Sub porque o Cloud Run aloca CPU apenas durante o processamento de uma solicitação.

É necessário processar a mensagem e retornar uma resposta quando terminar.

Ao utilizar as contas de serviço e as permissões do IAM, use o Pub/Sub com segurança e privacidade junto ao Cloud Run, sem precisar expor publicamente o serviço do Cloud Run. Somente a assinatura do Pub/Sub que você configurou pode invocar seu serviço.

Os possíveis casos de uso incluem:

Nesta página, mostramos como ativar o serviço para processar com segurança as mensagens enviadas de uma assinatura de um Pub/Sub no mesmo projeto do Google Cloud.

Para integrar seu serviço ao Pub/Sub:

  • Crie um tópico do Pub/Sub.
  • Adicione o código no serviço do Cloud Run para responder às mensagens do Pub/Sub enviadas para o tópico que você criou.
  • Crie uma conta de serviço com as permissões necessárias.
  • Crie uma assinatura do Pub/Sub e associe-a à conta de serviço. Essa assinatura enviará ao seu serviço qualquer mensagem publicada no tópico.

Antes de começar

Se ainda não tiver feito isso, configure o ambiente conforme descrito na página de configuração do Cloud Run. Você precisará usar a linha de comando gcloud e um projeto do Google Cloud em que implantará o serviço do Cloud Run.

Como adicionar código para processar mensagens do Pub/Sub

Seu serviço precisa extrair a mensagem da solicitação e retornar um código de sucesso esperado. A seguir estão snippets para idiomas selecionados (ao seu critério) que mostram como fazer isso para uma simples mensagem Hello World:

Node.js

app.post('/', (req, res) => {
  if (!req.body) {
    const msg = 'no Pub/Sub message received';
    console.error(`error: ${msg}`);
    res.status(400).send(`Bad Request: ${msg}`);
    return;
  }
  if (!req.body.message) {
    const msg = 'invalid Pub/Sub message format';
    console.error(`error: ${msg}`);
    res.status(400).send(`Bad Request: ${msg}`);
    return;
  }

  const pubSubMessage = req.body.message;
  const name = pubSubMessage.data
    ? Buffer.from(pubSubMessage.data, 'base64').toString().trim()
    : 'World';

  console.log(`Hello ${name}!`);
  res.status(204).send();
});

Python

@app.route("/", methods=["POST"])
def index():
    envelope = request.get_json()
    if not envelope:
        msg = "no Pub/Sub message received"
        print(f"error: {msg}")
        return f"Bad Request: {msg}", 400

    if not isinstance(envelope, dict) or "message" not in envelope:
        msg = "invalid Pub/Sub message format"
        print(f"error: {msg}")
        return f"Bad Request: {msg}", 400

    pubsub_message = envelope["message"]

    name = "World"
    if isinstance(pubsub_message, dict) and "data" in pubsub_message:
        name = base64.b64decode(pubsub_message["data"]).decode("utf-8").strip()

    print(f"Hello {name}!")

    return ("", 204)

Go


// PubSubMessage is the payload of a Pub/Sub event.
// See the documentation for more details:
// https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage
type PubSubMessage struct {
	Message struct {
		Data []byte `json:"data,omitempty"`
		ID   string `json:"id"`
	} `json:"message"`
	Subscription string `json:"subscription"`
}

// HelloPubSub receives and processes a Pub/Sub push message.
func HelloPubSub(w http.ResponseWriter, r *http.Request) {
	var m PubSubMessage
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("ioutil.ReadAll: %v", err)
		http.Error(w, "Bad Request", http.StatusBadRequest)
		return
	}
	if err := json.Unmarshal(body, &m); err != nil {
		log.Printf("json.Unmarshal: %v", err)
		http.Error(w, "Bad Request", http.StatusBadRequest)
		return
	}

	name := string(m.Message.Data)
	if name == "" {
		name = "World"
	}
	log.Printf("Hello %s!", name)
}

Java

import java.util.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

// PubsubController consumes a Pub/Sub message.
@RestController
public class PubSubController {
  @RequestMapping(value = "/", method = RequestMethod.POST)
  public ResponseEntity receiveMessage(@RequestBody Body body) {
    // Get PubSub message from request body.
    Body.Message message = body.getMessage();
    if (message == null) {
      String msg = "Bad Request: invalid Pub/Sub message format";
      System.out.println(msg);
      return new ResponseEntity(msg, HttpStatus.BAD_REQUEST);
    }

    String data = message.getData();
    String target =
        !StringUtils.isEmpty(data) ? new String(Base64.getDecoder().decode(data)) : "World";
    String msg = "Hello " + target + "!";

    System.out.println(msg);
    return new ResponseEntity(msg, HttpStatus.OK);
  }
}

É necessário codificar o serviço para retornar um código de resposta HTTP preciso. Códigos de êxito, como HTTP 200 ou 204, confirmam o processamento completo da mensagem do Pub/Sub. Códigos de erro, como HTTP 400 ou 500, indicam que a mensagem será repetida, conforme descrito em Como receber mensagens usando o Push.

Criar uma conta de serviço para assinatura

Você precisa criar uma conta de serviço para associar à sua assinatura do Pub/Sub e conceder a ela a permissão para invocar o serviço do Cloud Run. As mensagens do Pub/Sub enviadas para o serviço do Cloud Run terão a identidade dessa conta de serviço.

Use uma conta de serviço atual para representar a identidade da assinatura de Pub/Sub ou criar uma nova.

Para criar uma nova conta de serviço e conceder a ela permissão para invocar o serviço do Cloud Run:

Console

  1. No Console do Cloud, acesse a página Criar conta de serviço.

    Acesse Criar conta de serviço

  2. Selecione um projeto.

  3. Insira um nome de conta de serviço a ser exibido no Console do Cloud.

    O Console do Cloud gerará um ID de conta de serviço com base nesse nome. Edite o ID se for necessário. Não será possível alterar o ID depois.

  4. Opcional: digite uma descrição da conta de serviço.

  5. Clique em Criar.

  6. Clique no campo Selecionar um papel.

  7. Em Todos os papéis, selecione Cloud Run > Cloud Run Invoker.

  8. Clique em Concluído.

Linha de comando

  1. Crie a conta de serviço:

    gcloud iam service-accounts create SERVICE-ACCOUNT_NAME \
       --display-name "DISPLAYED-SERVICE-ACCOUNT_NAME"

    Substitua:

    • SERVICE-ACCOUNT_NAME por um nome em letras minúsculas exclusivo no projeto do Google Cloud, por exemplo, my-invoker-service-account-name.
    • DISPLAYED-SERVICE-ACCOUNT-NAME pelo nome que você quer exibir para essa conta de serviço, por exemplo, no console, My Invoker Service Account.
  2. Para o Cloud Run, conceda à conta de serviço permissão para invocar o serviço:

    gcloud run services add-iam-policy-binding SERVICE \
       --member=serviceAccount:SERVICE-ACCOUNT_NAME@PROJECT-ID.iam.gserviceaccount.com \
       --role=roles/run.invoker

    Substitua:

    • SERVICE pelo nome do serviço que você quer que seja invocado pelo Pub/Sub;
    • SERVICE-ACCOUNT_NAME pelo nome da conta de serviço;
    • PROJECT-ID pelo ID de projeto do Google Cloud;

Como criar um tópico do Pub/Sub

As solicitações para seu serviço são acionadas por mensagens publicadas em um tópico do Pub/Sub. Portanto, você precisará criar um tópico:

Console

  1. Acesse a página de tópicos do Pub/Sub no Console do Cloud.

    Página de tópicos do Pub/Sub

  2. Clique em Criar um tópico.

  3. Digite um Nome exclusivo para seu tópico, como "MyTopic", por exemplo.

Linha de comando

gcloud pubsub topics create TOPIC-NAME

Substitua TOPIC-NAME por um nome de tópico exclusivo no projeto do Google Cloud.

Criar uma assinatura push e associá-la à conta de serviço

Depois de criar o tópico do Pub/Sub, você precisa assinar o serviço para receber mensagens enviadas para um tópico e associar a assinatura à conta de serviço criada para o serviço. É possível usar o Console do Cloud ou a linha de comando gcloud:

Console

  1. Acesse a página de tópicos do Cloud Pub/Sub.

    Página de tópicos do Pub/Sub

  2. Clique no tópico em que você quer se inscrever.

  3. Clique em Criar assinatura para exibir o formulário de inscrição:

    formulário de inscrição

    No formulário, siga estas etapas:

    1. Especifique o tipo de envio push.
    2. Para a URL de Endpoints, especifique a URL do seu serviço, que é exibida na página de detalhes do serviço.
    3. Na lista suspensa Conta de serviço, selecione a conta de serviço que você criou com as permissões necessárias.
    4. Defina a validade da assinatura e o prazo de confirmação conforme pretendido.
    5. Clique em Criar.
  4. A assinatura foi concluída. As mensagens postadas no tópico agora serão enviadas para seu serviço.

Linha de comando

  1. Permita que o Pub/Sub crie tokens de autenticação no seu projeto:

    gcloud projects add-iam-policy-binding PROJECT-ID \
         --member=serviceAccount:service-PROJECT-NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \
         --role=roles/iam.serviceAccountTokenCreator

    Substitua:

    • PROJECT-ID pelo ID do projeto do Google Cloud;
    • PROJECT-NUMBER pelo número do projeto do Google Cloud.

      O ID e o número do projeto estão listados no painel Informações do projeto no Console do Cloud do seu projeto.

  2. Crie uma assinatura do Pub/Sub com a conta de serviço criada com as permissões necessárias:

    gcloud beta pubsub subscriptions create SUBSCRIPTION-ID --topic TOPIC-NAME \
       --push-endpoint=SERVICE-URL/ \
       --push-auth-service-account=SERVICE-ACCOUNT-NAME@PROJECT-ID.iam.gserviceaccount.com

    Replace

    • TOPIC-NAME pelo tópico que você criou anteriormente.
    • SERVICE-URL pelo URL de HTTPS fornecido quando você implantou o serviço. Para encontrá-lo, use o comando gcloud run services describe, especificando o nome do serviço: procure a linha de retorno iniciada por domain;
    • PROJECT-ID pelo ID de projeto do Google Cloud;

    A sinalização --push-auth-service-account ativa a funcionalidade de push do Pub/Sub para Autenticação e autorização.

  3. A assinatura foi concluída. As mensagens postadas no tópico agora serão enviadas para seu serviço. É possível enviar uma mensagem de teste para o tópico usando o comando:

    gcloud pubsub topics publish TOPIC --message "hello"

    Substitua TOPIC pelo nome do tópico que você criou.

A seguir