에뮬레이터로 앱 로컬 테스트

로컬에서 애플리케이션을 개발하고 테스트하려면 프로덕션 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 에뮬레이터는 로컬에서 실행되므로 문자열은 실제 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 서버를 설치합니다.
      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
            },
            ...
        ]
      }
      

환경 변수 액세스

자바와 C#을 제외한 모든 언어에서 PUBSUB_EMULATOR_HOST환경 변수 설정에 나온 대로 설정하면 Cloud Pub/Sub 클라이언트 라이브러리가 Cloud Pub/Sub 대신 로컬 인스턴스에서 실행 중인 API를 자동으로 호출합니다.

하지만 C#과 자바 클라이언트 라이브러리의 경우 에뮬레이터를 사용하려면 코드를 수정해야 합니다.

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 기능을 지원합니다.

  • 메시지 게시
  • push 및 pull 구독에서 메시지 수신
  • 메시지 순서
  • 메시지 재생
  • 데드 레터 주제로 메시지 전달
  • 메시지 전송 재시도 정책
  • Avro에 대한 스키마 지원

알려진 제한사항

  • UpdateTopicUpdateSnapshot RPC는 지원되지 않습니다.
  • IAM 작업은 지원되지 않습니다.
  • 구성 가능한 메시지 보관은 지원되지 않으며 모든 메시지는 무기한으로 유지됩니다.
  • 구독 만료는 지원되지 않습니다. 구독은 만료되지 않습니다.
  • 필터링은 지원되지 않습니다.
  • 프로토콜 버퍼에 대한 스키마 지원
  • BigQuery 구독을 만들 수 있지만 메시지를 BigQuery로 전송하지 않습니다.
  • 주문한 구독의 타임스탬프로 이동하는 기능은 지원되지 않습니다.

문제를 보고하려면 공개 Issue Tracker를 제출합니다.

다음 단계

  • minikube에 Pub/Sub 에뮬레이터를 사용하는 방법은 이 블로그 게시물을 참조하세요.