Pub/Sub 푸시 구독의 페이로드 래핑 해제

Pub/Sub 시스템 빌드 시 페이로드 래핑을 해제하면 표준 Pub/Sub push 엔드포인트 구현의 모든 시스템 요구사항을 준수하지 않는 다른 시스템에 연결할 수 있습니다.

페이로드 래핑 해제의 가능한 사용 사례는 다음과 같습니다.

  • HTTP 푸시 엔드포인트에 Pub/Sub 관련 메시지 파싱 코드를 작성하지 않는 것이 좋습니다.
  • Pub/Sub 메시지 메타데이터를 HTTP POST 본문의 메타데이터 대신 HTTP 헤더로 수신하는 것이 좋습니다.
  • Pub/Sub 메시지를 보내고 Pub/Sub 메타데이터를 제외하려고 합니다(예: 타사 API로 데이터를 전송할 때).

페이로드 래핑 해제 작동 방식

페이로드 래핑 해제는 Pub/Sub 메시지에서 메시지 데이터를 제외한 모든 메시지 메타데이터를 제거하는 기능입니다. 원시 메시지 데이터를 전송하면 구독자는 Pub/Sub의 시스템 요구사항을 준수하지 않고 메시지를 처리할 수 있습니다.

  • 페이로드의 래핑을 해제하면 메시지 데이터가 HTTP 본문으로 직접 전달됩니다.
  • 페이로드 래핑을 해제하지 않으면 Pub/Sub는 여러 메시지 메타데이터 필드와 메시지 데이터 필드가 포함된 JSON 객체를 전달합니다. 이 경우 메시지 데이터를 검색한 다음 base64로 디코딩하려면 JSON을 파싱해야 합니다.

메타데이터 쓰기

페이로드 래핑 해제를 사용 설정한 후에는 이전에 삭제한 메시지 메타데이터를 요청 헤더에 추가하는 메타데이터 쓰기 옵션을 사용할 수 있습니다.

  • 쓰기 메타데이터가 사용 설정되었습니다. 메시지 메타데이터를 요청 헤더에 다시 추가합니다. 디코딩된 원시 메시지 데이터도 전달합니다.
  • 메타데이터 쓰기가 사용 중지되었습니다. 디코딩된 원시 메시지 데이터만 전달합니다.

쓰기 메타데이터는 Pub/Sub, Google Cloud CLI 인수 --push-no-wrapper-write-metadata, API 속성 NoWrapper를 통해 노출됩니다. 기본적으로 이 값은 null입니다.

시작하기 전에

  • Pub/Sub 구독푸시 구독에 대해 알아보기 페이로드 래핑 해제는 푸시 구독에서만 사용할 수 있습니다.
  • 푸시 구독 구성 방법 알아보기

래핑된 메시지와 래핑되지 않은 메시지 예시

다음 예시에서는 래핑된 HTTP 메시지와 래핑되지 않은 HTTP 메시지 전송의 차이점을 보여줍니다. 이 예시의 메시지 데이터에는 {"status": "Hello there"} 문자열이 포함되어 있습니다.

이 예시에서는 페이로드 래핑 해제 기능이 사용 설정된 상태로 구독이 생성되고 mytopic에 메시지가 게시됩니다. 값이 some-key인 순서 키를 사용하고 미디어 유형이 application/json으로 선언됩니다.

gcloud pubsub topics publish mytopic
   --message='{"status": "Hello there"}'
   --ordering-key="some-key"
   --attribute "Content-Type=application/json"

다음 섹션에서는 래핑된 메시지와 래핑되지 않은 메시지의 차이점을 보여줍니다.

래핑된 메시지

다음 예시에서는 래핑된 Pub/Sub 메시지 표준을 보여줍니다. 이 경우 페이로드 래핑 해제가 사용 설정되지 않습니다.

게시 push 엔드포인트 수신

data="{"status": "Hello there"}"
ordering_key="some-key"
attributes=
  {
     {"Content-Type", "application/json"}
  }

Content-Length: 361
Content-Type: application/json
User-Agent: CloudPubSub-Google
Host: subscription-project.uc.r.appspot.com

{
  "message": {
      "attributes": {
          "Content-Type": "application/json"
      },
      "data": "eyJzdGF0dXMiOiAiSGVsbG8gdGhlcmUifQ==", //  Base64 - {"status": "Hello there"}
      "messageId": "2070443601311540",
      "message_id": "2070443601311540",
      "publishTime": "2021-02-26T19:13:55.749Z",
      "publish_time": "2021-02-26T19:13:55.749Z"
  },
  "subscription": "projects/myproject/..."
}

쓰기 메타데이터가 사용 중지된 래핑되지 않은 메시지

다음 예시에서는 메타데이터 쓰기 옵션이 사용 중지된 래핑되지 않은 메시지를 보여줍니다. 이 경우 x-goog-pubsub-* 헤더와 메시지 속성이 포함되지 않습니다.

게시 push 엔드포인트 수신

data="{"status": "Hello there"}"
ordering_key="some-key"
attributes=
  {
     {"Content-Type", "application/json"}
  }

Content-Length: 25
User-Agent: CloudPubSub-Google
Host: subscription-project.uc.r.appspot.com

{"status": "Hello there"}

쓰기 메타데이터가 사용 설정된 래핑되지 않은 메시지

다음 예시에서는 메타데이터 쓰기 옵션이 사용 설정된 래핑되지 않은 메시지를 보여줍니다. 이 경우 x-goog-pubsub-* 헤더와 메시지 속성이 포함됩니다.

게시 push 엔드포인트 수신

data="{"status": "Hello there"}"
ordering_key="some-key"
attributes=
  {
     {"Content-Type", "application/json"}
  }

x-goog-pubsub-subscription-name: "projects/myproject/..."
x-goog-pubsub-message-id: "2070443601311540"
x-goog-pubsub-publish-time: "2021-02-26T19:13:55.749Z"
x-goog-pubsub-ordering-key: "some-key"
Content-Type: application/json
Content-Length: 12
User-Agent: CloudPubSub-Google
Host: subscription-project.uc.r.appspot.com

{"status": "Hello there"}

페이로드 래핑 해제 구성

Google Cloud 콘솔 구독 세부정보 페이지, Google Cloud CLI, 클라이언트 라이브러리를 사용하여 구독에 페이로드 래핑 해제 내보내기 전송을 사용 설정할 수 있습니다.

콘솔

  1. Google Cloud 콘솔에서 구독 페이지로 이동합니다.

    Pub/Sub 구독 열기

  2. 구독 만들기를 클릭합니다.

  3. 구독 ID 필드에 이름을 입력합니다.

    구독 이름 지정 방법은 주제 또는 구독 이름 지정 안내를 참조하세요.

  4. 드롭다운 메뉴에서 하나의 주제를 선택합니다. 구독은 주제에서 메시지를 수신합니다.

  5. 전송 유형에서 푸시를 선택합니다.

  6. 페이로드 래핑 해제를 사용 설정하려면 페이로드 래핑 해제 사용 설정을 선택합니다.

  7. (선택사항) 요청 헤더에 있는 메시지 메타데이터를 보존하려면 메타데이터 쓰기를 선택합니다. 메시지의 콘텐츠 유형 헤더를 설정하려면 이 옵션을 사용 설정해야 합니다.

  8. 엔드포인트 URL을 지정합니다.

  9. 다른 모든 기본값을 유지합니다.

  10. 만들기를 클릭합니다.

gcloud

표준 HTTP 헤더를 포함하는 페이로드 래핑 해제로 구독을 구성하려면 다음 gcloud pubsub subscriptions create 명령어를 실행합니다.

gcloud pubsub subscriptions create SUBSCRIPTION \
  --topic TOPIC \
  --push-endpoint=PUSH_ENDPOINT \
  --push-no-wrapper

다음을 바꿉니다.

  • SUBSCRIPTION: 풀 구독의 이름 또는 ID입니다.
  • TOPIC: 주제의 ID입니다.
  • PUSH_ENDPOINT: 이 구독의 엔드포인트로 사용할 URL입니다. 예를 들면 https://myproject.appspot.com/myhandler입니다.
  • --push-no-wrapper: 메시지 데이터를 HTTP 본문으로 직접 전달합니다.

페이로드 래핑을 해제하여 구독을 구성하고 x-goog-pubsub-* 헤더 사용을 제어하려면 다음 명령어를 실행하세요.

gcloud pubsub subscriptions create SUBSCRIPTION \
  --topic TOPIC \
  --push-endpoint=PUSH_ENDPOINT \
  --push-no-wrapper \
  --push-no-wrapper-write-metadata
  • --push-no-wrapper-write-metadata: true이면 Pub/Sub 메시지 메타데이터를 HTTP 요청의 x-goog-pubsub-<KEY>:<VAL> 헤더에 기록합니다. Pub/Sub 메시지 속성을 HTTP 요청의 <KEY>:<VAL> 헤더에 기록합니다.

Python

이 샘플을 시도하기 전에 빠른 시작: 클라이언트 라이브러리 사용의 Python 설정 안내를 따르세요. 자세한 내용은 Pub/Sub Python API 참고 문서를 참조하세요.

from google.cloud import pubsub_v1

# TODO(developer)
# project_id = "your-project-id"
# topic_id = "your-topic-id"
# subscription_id = "your-subscription-id"
# endpoint = "https://my-test-project.appspot.com/push"

publisher = pubsub_v1.PublisherClient()
subscriber = pubsub_v1.SubscriberClient()
topic_path = publisher.topic_path(project_id, topic_id)
subscription_path = subscriber.subscription_path(project_id, subscription_id)

no_wrapper = pubsub_v1.types.PushConfig.NoWrapper(write_metadata=True)
push_config = pubsub_v1.types.PushConfig(
    push_endpoint=endpoint, no_wrapper=no_wrapper
)

# Wrap the subscriber in a 'with' block to automatically call close() to
# close the underlying gRPC channel when done.
with subscriber:
    subscription = subscriber.create_subscription(
        request={
            "name": subscription_path,
            "topic": topic_path,
            "push_config": push_config,
        }
    )

print(f"Push no wrapper subscription created: {subscription}.")
print(f"Endpoint for subscription is: {endpoint}")
print(f"No wrapper configuration for subscription is: {no_wrapper}")

Java

이 샘플을 시도하기 전에 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 Pub/Sub Java API 참고 문서를 참조하세요.

/*
 * Copyright 2016 Google LLC
 *
 * 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 pubsub;

import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.pubsub.v1.PushConfig;
import com.google.pubsub.v1.PushConfig.NoWrapper;
import com.google.pubsub.v1.Subscription;
import com.google.pubsub.v1.SubscriptionName;
import com.google.pubsub.v1.TopicName;
import java.io.IOException;

public class CreateUnwrappedPushSubscriptionExample {
  public static void main(String... args) throws Exception {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "your-project-id";
    String subscriptionId = "your-subscription-id";
    String topicId = "your-topic-id";
    String pushEndpoint = "https://my-test-project.appspot.com/push";

    createPushSubscriptionExample(projectId, subscriptionId, topicId, pushEndpoint);
  }

  public static void createPushSubscriptionExample(
      String projectId, String subscriptionId, String topicId, String pushEndpoint)
      throws IOException {
    try (SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create()) {
      TopicName topicName = TopicName.of(projectId, topicId);
      SubscriptionName subscriptionName = SubscriptionName.of(projectId, subscriptionId);
      NoWrapper noWrapper =
          NoWrapper.newBuilder()
              // Determines if message metadata is added to the HTTP headers of
              // the delivered message.
              .setWriteMetadata(true)
              .build();
      PushConfig pushConfig =
          PushConfig.newBuilder().setPushEndpoint(pushEndpoint).setNoWrapper(noWrapper).build();

      // Create a push subscription with default acknowledgement deadline of 10 seconds.
      // Messages not successfully acknowledged within 10 seconds will get resent by the server.
      Subscription subscription =
          subscriptionAdminClient.createSubscription(subscriptionName, topicName, pushConfig, 10);
      System.out.println("Created push subscription: " + subscription.getName());
    }
  }
}

C++

이 샘플을 시도하기 전에 빠른 시작: 클라이언트 라이브러리 사용의 C++ 설정 안내를 따르세요. 자세한 내용은 Pub/Sub C++ API 참고 문서를 확인하세요.

namespace pubsub = ::google::cloud::pubsub;
namespace pubsub_admin = ::google::cloud::pubsub_admin;
[](pubsub_admin::SubscriptionAdminClient client,
   std::string const& project_id, std::string const& topic_id,
   std::string const& subscription_id, std::string const& endpoint) {
  google::pubsub::v1::Subscription request;
  request.set_name(
      pubsub::Subscription(project_id, subscription_id).FullName());
  request.set_topic(pubsub::Topic(project_id, topic_id).FullName());
  request.mutable_push_config()->set_push_endpoint(endpoint);
  request.mutable_push_config()->mutable_no_wrapper()->set_write_metadata(
      true);
  auto sub = client.CreateSubscription(request);
  if (sub.status().code() == google::cloud::StatusCode::kAlreadyExists) {
    std::cout << "The subscription already exists\n";
    return;
  }
  if (!sub) throw std::move(sub).status();

  std::cout << "The subscription was successfully created: "
            << sub->DebugString() << "\n";
}

Go

이 샘플을 시도하기 전에 빠른 시작: 클라이언트 라이브러리 사용의 Go 설정 안내를 따르세요. 자세한 내용은 Pub/Sub Go API 참고 문서를 참조하세요.

import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/pubsub"
)

// createPushNoWrapperSubscription creates a push subscription where messages are delivered in the HTTP body.
func createPushNoWrapperSubscription(w io.Writer, projectID, subID string, topic *pubsub.Topic, endpoint string) error {
	// projectID := "my-project-id"
	// subID := "my-sub"
	// topic of type https://godoc.org/cloud.google.com/go/pubsub#Topic
	// endpoint := "https://my-test-project.appspot.com/push"
	ctx := context.Background()
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("pubsub.NewClient: %w", err)
	}
	defer client.Close()

	sub, err := client.CreateSubscription(ctx, subID, pubsub.SubscriptionConfig{
		Topic:       topic,
		AckDeadline: 10 * time.Second,
		PushConfig: pubsub.PushConfig{
			Endpoint: endpoint,
			Wrapper: &pubsub.NoWrapper{
				// Determines if message metadata is added to the HTTP headers of
				// the delivered message.
				WriteMetadata: true,
			},
		},
	})
	if err != nil {
		return fmt.Errorf("CreateSubscription: %w", err)
	}
	fmt.Fprintf(w, "Created push no wrapper subscription: %v\n", sub)
	return nil
}

Node.js

이 샘플을 시도하기 전에 빠른 시작: 클라이언트 라이브러리 사용의 Node.js 설정 안내를 따르세요. 자세한 내용은 Pub/Sub Node.js API 참고 문서를 참조하세요.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const pushEndpoint = 'YOUR_ENDPOINT_URL';
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';

// Imports the Google Cloud client library
const {PubSub} = require('@google-cloud/pubsub');

// Creates a client; cache this for further use
const pubSubClient = new PubSub();

async function createPushSubscriptionNoWrapper(
  pushEndpoint,
  topicNameOrId,
  subscriptionNameOrId
) {
  const options = {
    pushConfig: {
      // Set to an HTTPS endpoint of your choice. If necessary, register
      // (authorize) the domain on which the server is hosted.
      pushEndpoint,
      // When true, writes the Pub/Sub message metadata to
      // `x-goog-pubsub-<KEY>:<VAL>` headers of the HTTP request. Writes the
      // Pub/Sub message attributes to `<KEY>:<VAL>` headers of the HTTP request.
      noWrapper: {
        writeMetadata: true,
      },
    },
  };

  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, options);
  console.log(`Subscription ${subscriptionNameOrId} created.`);
}

Node.js

이 샘플을 시도하기 전에 빠른 시작: 클라이언트 라이브러리 사용의 Node.js 설정 안내를 따르세요. 자세한 내용은 Pub/Sub Node.js API 참고 문서를 참조하세요.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const pushEndpoint = 'YOUR_ENDPOINT_URL';
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';

// Imports the Google Cloud client library
import {PubSub, CreateSubscriptionOptions} from '@google-cloud/pubsub';

// Creates a client; cache this for further use
const pubSubClient = new PubSub();

async function createPushSubscriptionNoWrapper(
  pushEndpoint: string,
  topicNameOrId: string,
  subscriptionNameOrId: string
) {
  const options: CreateSubscriptionOptions = {
    pushConfig: {
      // Set to an HTTPS endpoint of your choice. If necessary, register
      // (authorize) the domain on which the server is hosted.
      pushEndpoint,
      // When true, writes the Pub/Sub message metadata to
      // `x-goog-pubsub-<KEY>:<VAL>` headers of the HTTP request. Writes the
      // Pub/Sub message attributes to `<KEY>:<VAL>` headers of the HTTP request.
      noWrapper: {
        writeMetadata: true,
      },
    },
  };

  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, options);
  console.log(`Subscription ${subscriptionNameOrId} created.`);
}

메시지에 콘텐츠 유형 헤더 설정하기

페이로드 래핑 해제를 사용 설정한 후에는 Pub/Sub가 요청에 미디어 유형 헤더 필드를 자동으로 설정하지 않습니다. Content-Type 헤더 필드를 명시적으로 설정하지 않으면 요청을 처리하는 웹 서버가 application/octet-stream의 기본값을 설정하거나 요청을 예기치 않은 방식으로 해석할 수도 있습니다.

Content-Type 헤더가 필요한 경우 게시 시점에 모든 게시된 메시지에 대해 명시적으로 선언해야 합니다. 이렇게 하려면 먼저 메타데이터 쓰기를 사용 설정해야 합니다. 메타데이터 쓰기를 사용 설정한 결과는 제공된 예시에 나와 있습니다.

다음 단계