ביטול העטיפה של מטען ייעודי (payload) במינויים מסוג Pub/Sub push

כשמפתחים את מערכת Pub/Sub, אפשר להשתמש בפירוק של מטען ייעודי כדי להתחבר למערכות אחרות שלא עומדות בכל דרישות המערכת של הטמעה רגילה של נקודת קצה מסוג Pub/Sub push.

הנה כמה תרחישי שימוש אפשריים בפירוק של מטען ייעודי (payload):

  • אתם לא רוצים לכתוב קוד לניתוח הודעות שספציפי ל-Pub/Sub עבור נקודות הקצה של HTTP push.
  • אתם מעדיפים לקבל את המטא-נתונים של הודעות Pub/Sub ככותרות HTTP במקום המטא-נתונים בגוף ה-HTTP POST.
  • אתם רוצים לשלוח הודעות Pub/Sub ולהחריג את המטא-נתונים של Pub/Sub, למשל כששולחים נתונים ל-API של צד שלישי.

איך מתבצעת פריסת המטען הייעודי (payload)

התכונה 'פריקת מטען ייעודי' מסירה מהודעות Pub/Sub את כל המטא-נתונים של ההודעה, למעט נתוני ההודעה. כששולחים נתוני הודעות גולמיים, המנויים יכולים לעבד את ההודעה בלי להקפיד על דרישות המערכת של Pub/Sub.

  • במקרה של פריסת מטען ייעודי (payload), נתוני ההודעה מועברים ישירות כגוף ה-HTTP.
  • בלי פתיחה של מטען ייעודי (payload),‏ Pub/Sub מעביר אובייקט JSON שמכיל כמה שדות של מטא-נתונים של ההודעה ושדה של נתוני ההודעה. במקרה הזה, צריך לנתח את ה-JSON כדי לאחזר את נתוני ההודעה ואז לבצע פענוח base64.

כתיבת מטא-נתונים

אחרי שמפעילים את האפשרות 'ביטול עטיפה של מטען ייעודי', אפשר להשתמש באפשרות כתיבת מטא-נתונים, שמוסיפה את המטא-נתונים של ההודעה שהוסרו קודם לכותרת הבקשה.

  • הכתיבה של מטא-נתונים מופעלת. מוסיפים מחדש את המטא-נתונים של ההודעה לכותרת הבקשה. בנוסף, נתוני ההודעה הגולמיים מפוענחים ונשלחים.
  • השבתה של כתיבת מטא-נתונים. רק נתוני ההודעה הגולמיים והמפוענחים מועברים.

מטא-נתונים של כתיבה נחשפים דרך Pub/Sub, הארגומנט --push-no-wrapper-write-metadata של Google Cloud CLI והמאפיין NoWrapper של ה-API. כברירת מחדל, הערך הזה הוא null.

לפני שמתחילים

דוגמה להודעות עטופות ולא עטופות

בדוגמאות הבאות מוצג ההבדל בין שליחת הודעת HTTP עטופה לבין שליחת הודעת HTTP לא עטופה. בדוגמאות האלה, נתוני ההודעה מכילים את המחרוזת {"status": "Hello there"}.

בדוגמה הזו, נוצר מינוי עם התכונה payload unwrapping (פריקת מטען ייעודי) מופעלת, והודעה מתפרסמת ב-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. במקרה הזה, האפשרות להסרת העטיפה של המטען הייעודי (payload) לא מופעלת.

פרסום נקודת הקצה (endpoint) של הודעת ה-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-* הכותרות ומאפייני ההודעה לא נכללים.

פרסום נקודת הקצה (endpoint) של הודעת ה-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-* ומאפייני ההודעה נכללים.

פרסום נקודת הקצה (endpoint) של הודעת ה-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"}

הגדרת פריסת מטען ייעודי (payload)

אפשר להפעיל את האפשרות 'שליחת הודעות בדחיפה עם פתיחת המטען הייעודי (payload) של המינוי' באמצעות הדף Subscription Details במסוף Google Cloud , Google Cloud CLI או ספריות הלקוח.

המסוף

  1. נכנסים לדף Subscriptions במסוף Google Cloud .

    הרשמות פתוחות ל-Pub/Sub

  2. לוחצים על יצירת מינוי.

  3. כותבים שם בשדה Subscription ID.

    מידע על מתן שם למינוי זמין במאמר הנחיות למתן שם לנושא או למינוי.

  4. בוחרים נושא מהתפריט הנפתח. המינוי מקבל הודעות מהנושא.

  5. בקטע סוג המסירה, בוחרים באפשרות דחיפה.

  6. כדי להפעיל את חילוץ המטען הייעודי (payload), בוחרים באפשרות הפעלת חילוץ המטען הייעודי.

  7. (אופציונלי) כדי לשמור את המטא-נתונים של ההודעות בכותרת הבקשה, בוחרים באפשרות כתיבת מטא-נתונים. צריך להפעיל את האפשרות הזו כדי להגדיר כותרת Content-Type להודעות.

  8. מציינים כתובת URL של נקודת קצה.

  9. משאירים את כל שאר ערכי ברירת המחדל.

  10. לוחצים על יצירה.

gcloud

כדי להגדיר מינוי עם פריקת מטען (payload) שכולל כותרות HTTP רגילות, מריצים את הפקודה הבאה של gcloud pubsub subscriptions create:

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

מחליפים את מה שכתוב בשדות הבאים:

  • SUBSCRIPTION: השם או המזהה של מינוי המשיכה.
  • TOPIC: מזהה הנושא.
  • PUSH_ENDPOINT: כתובת ה-URL שתשמש כנקודת הקצה של המינוי הזה. לדוגמה, https://myproject.appspot.com/myhandler.
  • --push-no-wrapper: מעביר את נתוני ההודעה ישירות כגוף ה-HTTP.

כדי להגדיר מינוי עם ביטול עטיפה של מטען ייעודי (payload) ולשלוט בשימוש בכותרות 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 נכתבים בכותרות x-goog-pubsub-<KEY>:<VAL> של בקשת ה-HTTP. כותב את מאפייני ההודעה ב-Pub/Sub לכותרות <KEY>:<VAL> של בקשת ה-HTTP.

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

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי הוראות ההגדרה של 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 Pub/Sub ‏ (v2). אם אתם עדיין משתמשים בספרייה v1, כדאי לעיין במדריך להעברה לגרסה v2. כדי לראות רשימה של דוגמאות קוד מגרסה 1, אפשר לעיין ב דוגמאות הקוד שהוצאו משימוש.

לפני שמנסים את הדוגמה הזו, צריך לפעול לפי הוראות ההגדרה של Go במאמר מדריך למתחילים: שימוש בספריות לקוח. מידע נוסף מופיע במאמרי העזרה של Pub/Sub Go API.

import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/pubsub/v2"
	"cloud.google.com/go/pubsub/v2/apiv1/pubsubpb"
)

// createPushNoWrapperSubscription creates a push subscription where messages are delivered in the HTTP body.
func createPushNoWrapperSubscription(w io.Writer, projectID, topic, subscription, endpoint string) error {
	// projectID := "my-project-id"
	// topic := "projects/my-project-id/topics/my-topic"
	// subscription := "projects/my-project/subscriptions/my-sub"
	// 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()
	pbSubscription := &pubsubpb.Subscription{
		Name:               subscription,
		Topic:              topic,
		AckDeadlineSeconds: 10,
		PushConfig: &pubsubpb.PushConfig{
			PushEndpoint: endpoint,
			Wrapper: &pubsubpb.PushConfig_NoWrapper_{
				NoWrapper: &pubsubpb.PushConfig_NoWrapper{
					// Determines if message metadata is added to the HTTP headers of
					// the delivered message.
					WriteMetadata: true,
				},
			},
		},
	}
	sub, err := client.SubscriptionAdminClient.CreateSubscription(ctx, pbSubscription)
	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 reference documentation.

/**
 * 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 reference documentation.

/**
 * 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.`);
}

הגדרת כותרת מסוג תוכן בהודעה

אחרי שמפעילים את האפשרות להסרת העטיפה של מטען הייעודי (payload),‏ Pub/Sub לא מגדיר באופן אוטומטי שדה כותרת של סוג מדיה בבקשה. אם לא מגדירים באופן מפורש שדה כותרת Content-Type, שרת האינטרנט שמטפל בבקשה עשוי להגדיר ערך ברירת מחדל של application/octet-stream או לפרש את הבקשה באופן לא צפוי.

אם אתם צריכים להשתמש בכותרת Content-Type, אתם צריכים להצהיר עליה באופן מפורש בזמן הפרסום של כל הודעה בנפרד. כדי לעשות זאת, אתם צריכים קודם להפעיל את האפשרות כתיבת מטא-נתונים. התוצאה של הפעלת האפשרות כתיבת מטא-נתונים מוצגת בדוגמאות שסופקו.

המאמרים הבאים