使用模擬器在本機上測試應用程式

如要在本機上開發及測試您的應用程式,您可以使用 Pub/Sub 模擬器,提供實際的 Pub/Sub 服務本機模擬。您可以使用 Google Cloud CLI 執行 Pub/Sub 模擬器。

如要透過模擬器執行應用程式,請先啟動模擬器並設定環境變數。應用程式必須與模擬器通訊,而非正式版 Pub/Sub 服務。建立的資源和發布至模擬器的訊息會在模擬器工作階段的整個生命週期內維持。

事前準備

使用 Pub/Sub 模擬器前,請先完成下列先決條件:

安裝模擬器

透過命令提示字元安裝模擬器:

gcloud components install pubsub-emulator
gcloud components update

將模擬器設為容器映像檔

如要以容器形式安裝及執行模擬器,請下載並安裝 gCloud Docker 映像檔

啟動模擬器

在命令提示字元中叫用 pubsub start,以啟動模擬器。執行指令前,請將 PUBSUB_PROJECT_ID 替換為有效的Google Cloud 專案 ID 字串。由於 Pub/Sub Emulator 會在本機執行,因此字串不必代表實際的 Google Cloud 專案。

gcloud beta emulators pubsub start --project=PUBSUB_PROJECT_ID [options]

如需旗標的完整清單,請參閱 gcloud beta emulators pubsub start

啟動模擬器後,您會看到類似以下的訊息:

...
[pubsub] This is the Pub/Sub fake.
[pubsub] Implementation may be incomplete or differ from the real system.
...
[pubsub] INFO: Server started, listening on 8085

這則訊息表示 Pub/Sub 伺服器會在本機電腦的模擬器端點執行,而非 Google Cloud 端點。所有作業都會在本機上執行,包括:

  • 建立主題或訂閱項目
  • 發布中
  • 訂閱中

設定環境變數

啟動模擬器之後,您必須設定環境變數,讓應用程式連線到模擬器,而非 Pub/Sub。在您用來執行應用程式的相同機器中設定這些環境變數。

每次啟動模擬器時,您都必須設定環境變數。環境變數取決於動態指派的通訊埠編號,這些編號可能會在您重新啟動模擬器時變更。

自動設定變數

如果您的應用程式與模擬器在相同機器上執行,可自動設定環境變數:

Linux / macOS

使用替代指令執行 env-init

$(gcloud beta emulators pubsub env-init)

Windows

使用 env-init 的輸出結果,建立並執行批次檔案:

gcloud beta emulators pubsub env-init > set_vars.cmd && set_vars.cmd

您的應用程式現在會連線至 Pub/Sub 模擬器。

手動設定變數

如果您的應用程式與模擬器在不同機器上執行,可手動設定環境變數:

  1. 執行 env-init 指令:

     gcloud beta emulators pubsub env-init

  2. 在執行應用程式的機器上,依照 env-init 指令的輸出結果,設定 PUBSUB_EMULATOR_HOST 環境變數與值。這項設定會將應用程式連結至模擬器。您可以選擇針對要用於模擬器的專案設定 PUBSUB_PROJECT_ID 環境變數。

    Linux / macOS
    export PUBSUB_EMULATOR_HOST=[::1]:8432
    export PUBSUB_PROJECT_ID=my-project-id
    Windows
    set PUBSUB_EMULATOR_HOST=[::1]:8432
    set PUBSUB_PROJECT_ID=my-project-id

您的應用程式現在會連線至 Pub/Sub 模擬器。

注意:如果您使用的是 Python App Engine 標準 本機開發伺服器,則必須在指令列上傳遞這個環境變數,如下所示:

dev_appserver.py app.yaml --env_var PUBSUB_EMULATOR_HOST=${PUBSUB_EMULATOR_HOST}

dev_appserver.py 已納入您的 [PATH_TO_CLOUD_SDK]/google-cloud-sdk/bin/dev_appserver.py

使用模擬器

如要使用模擬器,您必須使用 Cloud 用戶端程式庫建構應用程式。模擬器不支援 Google Cloud 控制台或 gcloud pubsub 指令。

以下範例說明如何使用模擬器和使用 Python Cloud 用戶端程式庫的應用程式執行各種作業。這些作業的範例包括如何建立主題、發布訊息和讀取訊息。

在您設定模擬器環境變數的機器中完成下列步驟:

  1. 複製完整的 Python 存放區,由 GitHub 取得 Pub/Sub Python 範例。

  2. 在複製的存放區中,前往 samples/snippets 目錄。您將在這個目錄中完成其餘步驟。

  3. samples/snippets 目錄中,安裝執行範例所需的依附元件:

    pip install -r requirements.txt
    
  4. 建立主題:

     python publisher.py PUBSUB_PROJECT_ID create TOPIC_ID
    
  5. (選用) 如果您沒有用於測試模擬器中推播訂閱的本機推播端點,請完成下列步驟,在 http://[::1]:3000/messages 上建立一個。

    1. 安裝 JSON Server
      npm install -g json-server
      
    2. 啟動 JSON 伺服器。
      json-server --port 3000 --watch db.json
      
      ,其中 db.json 包含以下範例程式碼:
      {
         "messages": []
      }
      
    3. 請在下一個步驟中記下 PUSH_ENDPOINThttp://[::1]:3000/messages
  6. 為主題建立訂閱:

    • 建立提取訂閱:

      python subscriber.py PUBSUB_PROJECT_ID create TOPIC_ID SUBSCRIPTION_ID
      
    • 建立推送訂閱項目:

      python subscriber.py PUBSUB_PROJECT_ID create-push TOPIC_ID SUBSCRIPTION_ID \
      PUSH_ENDPOINT
      
  7. 將訊息發佈至主題:

     python publisher.py PUBSUB_PROJECT_ID publish TOPIC_ID
    
  8. 讀取發佈到主題的訊息:

    • 從提取訂閱項目中擷取訊息:

      python subscriber.py PUBSUB_PROJECT_ID receive SUBSCRIPTION_ID
      
    • 觀察傳送至本機發送端的訊息。例如,訊息如下所示:

      {
        "messages": [
            {
                "subscription": "projects/PUBSUB_PROJECT_ID/subscriptions/SUBSCRIPTION_ID",
                "message": {
                    "data": "TWVzc2FnZSBudW1iZXIgMQ==",
                    "messageId": "10",
                    "attributes": {}
                },
                "id": 1
            },
            ...
        ]
      }
      

存取環境變數

在除了 Java 和 C# 以外的所有語言中,如果您已遵循「設定環境變數」一節所述設定 PUBSUB_EMULATOR_HOST,Pub/Sub 用戶端程式庫就會自動呼叫在本機執行個體 (而不是 Pub/Sub) 中執行的 API。

但是,C# 與 Java 用戶端程式庫需要您修改程式碼才能使用模擬器:

C#

在嘗試這個範例之前,請先按照 Pub/Sub 快速入門:使用用戶端程式庫中的操作說明設定 C#。詳情請參閱 Pub/Sub C# API 參考說明文件

如要向 Pub/Sub 進行驗證,請設定應用程式預設憑證。詳情請參閱「為本機開發環境設定驗證機制」。


using Google.Api.Gax;
using Google.Cloud.PubSub.V1;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class EmulatorSupportSample
{
    public async Task WithEmulatorAsync(string projectId, string topicId, string subscriptionId)
    {
        // Use EmulatorDetection.EmulatorOrProduction to create service clients that will
        // that will connect to the PubSub emulator if the PUBSUB_EMULATOR_HOST environment
        // variable is set, but will otherwise connect to the production environment.

        // Create the PublisherServiceApiClient using the PublisherServiceApiClientBuilder
        // and setting the EmulatorDection property.
        PublisherServiceApiClient publisherService = await new PublisherServiceApiClientBuilder
        {
            EmulatorDetection = EmulatorDetection.EmulatorOrProduction
        }.BuildAsync();

        // Use the client as you'd normally do, to create a topic in this example.
        TopicName topicName = new TopicName(projectId, topicId);
        publisherService.CreateTopic(topicName);

        // Create the SubscriberServiceApiClient using the SubscriberServiceApiClientBuilder
        // and setting the EmulatorDection property.
        SubscriberServiceApiClient subscriberService = await new SubscriberServiceApiClientBuilder
        {
            EmulatorDetection = EmulatorDetection.EmulatorOrProduction
        }.BuildAsync();

        // Use the client as you'd normally do, to create a subscription in this example.
        SubscriptionName subscriptionName = new SubscriptionName(projectId, subscriptionId);
        subscriberService.CreateSubscription(subscriptionName, topicName, pushConfig: null, ackDeadlineSeconds: 60);

        // Create the PublisherClient using PublisherClientBuilder to set the EmulatorDetection property.
        PublisherClient publisher = await new PublisherClientBuilder
        {
            TopicName = topicName,
            EmulatorDetection = EmulatorDetection.EmulatorOrProduction
        }.BuildAsync();
        // Use the client as you'd normally do, to send a message in this example.
        await publisher.PublishAsync("Hello, Pubsub");
        await publisher.ShutdownAsync(TimeSpan.FromSeconds(15));

        // Create the SubscriberClient using SubscriberClientBuild to set the EmulatorDetection property.
        SubscriberClient subscriber = await new SubscriberClientBuilder
        {
            SubscriptionName = subscriptionName,
            EmulatorDetection = EmulatorDetection.EmulatorOrProduction
        }.BuildAsync();
        List<PubsubMessage> receivedMessages = new List<PubsubMessage>();

        // Use the client as you'd normally do, to listen for messages in this example.
        await subscriber.StartAsync((msg, cancellationToken) =>
        {
            receivedMessages.Add(msg);
            Console.WriteLine($"Received message {msg.MessageId} published at {msg.PublishTime.ToDateTime()}");
            Console.WriteLine($"Text: '{msg.Data.ToStringUtf8()}'");
            // In this example we stop the subscriber when the message is received.
            // You may leave the subscriber running, and it will continue to received published messages
            // if any.
            // This is non-blocking, and the returned Task may be awaited.
            subscriber.StopAsync(TimeSpan.FromSeconds(15));
            // Return Reply.Ack to indicate this message has been handled.
            return Task.FromResult(SubscriberClient.Reply.Ack);
        });
    }
}

Java

在嘗試這個範例之前,請先按照 Pub/Sub 快速入門:使用用戶端程式庫中的操作說明設定 Java。詳情請參閱 Pub/Sub Java API 參考說明文件

如要向 Pub/Sub 進行驗證,請設定應用程式預設憑證。詳情請參閱「為本機開發環境設定驗證機制」。


import com.google.api.core.ApiFuture;
import com.google.api.gax.core.CredentialsProvider;
import com.google.api.gax.core.NoCredentialsProvider;
import com.google.api.gax.grpc.GrpcTransportChannel;
import com.google.api.gax.rpc.FixedTransportChannelProvider;
import com.google.api.gax.rpc.TransportChannelProvider;
import com.google.cloud.pubsub.v1.Publisher;
import com.google.cloud.pubsub.v1.TopicAdminClient;
import com.google.cloud.pubsub.v1.TopicAdminSettings;
import com.google.protobuf.ByteString;
import com.google.pubsub.v1.PubsubMessage;
import com.google.pubsub.v1.Topic;
import com.google.pubsub.v1.TopicName;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public class UsePubSubEmulatorExample {
  public static void main(String... args) throws Exception {
    String hostport = System.getenv("PUBSUB_EMULATOR_HOST");
    ManagedChannel channel = ManagedChannelBuilder.forTarget(hostport).usePlaintext().build();
    try {
      TransportChannelProvider channelProvider =
          FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel));
      CredentialsProvider credentialsProvider = NoCredentialsProvider.create();

      // Set the channel and credentials provider when creating a `TopicAdminClient`.
      // Can be done similarly for a `SubscriptionAdminClient`.
      TopicAdminClient topicAdminClient =
          TopicAdminClient.create(
              TopicAdminSettings.newBuilder()
                  .setTransportChannelProvider(channelProvider)
                  .setCredentialsProvider(credentialsProvider)
                  .build());

      TopicName topicName = TopicName.of("my-project-id", "my-topic-id");
      Topic topic = topicAdminClient.createTopic(topicName);
      System.out.println("Created topic: " + topic.getName());

      // Set the channel and credentials provider when creating a `Publisher`.
      // Can be done similarly for a `Subscriber`.
      Publisher publisher =
          Publisher.newBuilder(topicName)
              .setChannelProvider(channelProvider)
              .setCredentialsProvider(credentialsProvider)
              .build();

      String message = "Hello World!";
      ByteString data = ByteString.copyFromUtf8(message);
      PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build();

      ApiFuture<String> messageIdFuture = publisher.publish(pubsubMessage);
      String messageId = messageIdFuture.get();
      System.out.println("Published message ID: " + messageId);
    } finally {
      channel.shutdown();
    }
  }
}

停止模擬器

如要停止模擬器,請按下 Control+C

停止模擬器後,請執行下列指令,移除 PUBSUB_EMULATOR_HOST 環境變數,讓應用程式連線至 Pub/Sub:

Linux / macOS
unset PUBSUB_EMULATOR_HOST
Windows
set PUBSUB_EMULATOR_HOST=

模擬器指令列引數

如要進一步瞭解 Pub/Sub 模擬器的指令列引數,請參閱 gcloud beta emulators pubsub

支援功能

模擬器支援下列 Pub/Sub 功能:

  • 發布訊息
  • 透過推送和提取訂閱項目接收訊息
  • 排序訊息
  • 重播訊息
  • 將訊息轉寄至無效信件主題
  • 郵件傳送的重試政策
  • Avro 的結構定義支援

已知限制

  • 不支援 UpdateTopicUpdateSnapshot RPC。
  • 不支援 IAM 作業。
  • 不支援可設定的訊息保留功能,所有訊息都會無限期保留。
  • 不支援訂閱到期。訂閱項目不會過期。
  • 不支援篩選。
  • 支援通訊協定緩衝區的結構定義。
  • 可以建立 BigQuery 訂閱項目,但不會將訊息傳送至 BigQuery。
  • 系統不支援針對有順序的訂閱項目搜尋時間戳記。
  • 您可以使用單一訊息轉換 (SMT) 建立主題和訂閱項目,但訊息不會轉換。

如要回報問題,請提交公開 Issue Tracker

後續步驟

  • 如要瞭解如何在 minikube 中使用 Pub/Sub 模擬器,請參閱這篇網誌文章