In the previous lesson, you successfully built a publisher that sends user signup events to a Pub/Sub topic. You learned how to configure the emulator environment, create topics, and publish structured JSON messages. However, publishing messages is only half of the event-driven equation. Without subscribers to consume and process these messages, your events simply accumulate in the topic without triggering any downstream actions.
This is where subscriptions become essential. A subscription acts as a bridge between your topic and the applications that need to process your events. Think of it as a dedicated delivery route that ensures your messages reach the right consumers. When you create a subscription, you're essentially telling Pub/Sub, "I want to receive messages from this specific topic, and here's how you should deliver them to me."
The relationship between topics and subscriptions is one-to-many, meaning a single topic can have multiple subscriptions. This design enables powerful patterns in which different services can process the same events in different ways. For example, your user signup events might trigger a welcome email service, an analytics service, and a user provisioning service — each with its own subscription to the same topic.
In this lesson, you'll complete your event-driven messaging system by learning how to create and manage subscriptions. You'll discover how to handle subscription creation gracefully, manage multiple clients, and integrate subscription management with your existing publishing code. By the end, you'll have a complete publisher-subscriber pipeline that can handle real-world event processing scenarios.
Just as you needed a PublisherClient
to send messages, you need a SubscriberClient to manage subscriptions and receive messages. The subscriber client works alongside your existing publisher client, and both can coexist in the same application without conflicts.
Setting up the subscriber client follows the same pattern you learned for the publisher, automatically detecting your emulator configuration from the environment variable:
Notice the addition of a third constant, SUB
, which defines your subscription name. The subscription name should be descriptive of what will process the messages — in this case, "signup-processor" clearly indicates that this subscription will handle user signup events.
Creating subscription paths works similarly to topic paths, but uses the subscriber client's subscription_path
method:
This creates a properly formatted subscription identifier like projects/demo-local/subscriptions/signup-processor
. The subscription path uniquely identifies your subscription within the project and will be used for all subscription operations.
You'll also need the topic path for subscription creation, since every subscription must be associated with a specific topic. You can reuse the topic path creation from your publisher code:
This coordination between publisher and subscriber clients demonstrates how Pub/Sub applications often need both types of clients working together, even when their primary function is publishing or subscribing.
Creating subscriptions requires more sophisticated error handling than topic creation because subscriptions have dependencies and more complex failure scenarios. Unlike topics, which can be created idempotently, subscription creation involves checking whether the subscription already exists and handling various error conditions gracefully.
Critical dependency: Subscriptions cannot be created without an existing topic. If you attempt to create a subscription for a non-existent topic, Pub/Sub will raise an error indicating that the topic was not found. This makes the order of operations crucial — topics must be successfully created before their subscriptions.
The most elegant approach is to implement an "ensure subscription" pattern that checks for the subscription's existence before attempting to create it. Note the use of the request
parameter for get_subscription
, which is the recommended style for the latest Pub/Sub client libraries:
This function first attempts to retrieve the subscription using get_subscription
. If the subscription exists, the function completes successfully. If the subscription doesn't exist, the get_subscription
call raises a NotFound
exception, which triggers the creation of a new subscription.
The NotFound
exception is part of Google Cloud's API core exceptions and specifically indicates that the requested resource doesn't exist. This is different from other types of errors, such as network failures or permission issues, making it safe to catch specifically for this use case.
The create_subscription
method requires both the subscription name and the topic it should subscribe to. This establishes the connection between your subscription and the topic containing your user signup events. However, if the topic doesn't exist at this point, you'll encounter an error that can be confusing to debug, especially if your topic creation was silently ignored due to broad exception handling.
Let's examine how all these components work together in a complete solution that combines subscription management with message publishing. This integrated approach demonstrates how real-world Pub/Sub applications coordinate multiple clients and operations, and follows the code style shown above:
The script begins with the familiar emulator configuration from the previous lesson, ensuring your application connects to the local emulator rather than the cloud service. The constants now include all three key identifiers: project, topic, and subscription names.
Both client types are initialized at the module level, demonstrating how Pub/Sub applications often need multiple clients working in coordination. The topic and subscription paths are created using their respective clients, establishing the proper resource identifiers for all subsequent operations.
The topic creation remains unchanged from the previous lesson, using the simple exception-handling approach for idempotent topic creation. This ensures your topic exists before creating subscriptions that depend on it.
The subscription management uses the robust ensure_subscription
function, which handles the existence check and creation logic. This function is called in the main execution block before publishing messages, ensuring the complete infrastructure is in place.
When you run this complete script, you'll see output similar to:
You've successfully built a complete publisher-subscriber messaging pipeline using Google Cloud Pub/Sub. You've learned how to coordinate multiple client types, manage subscription creation with proper error handling, and integrate subscription management with your existing publishing infrastructure.
The key skills you've mastered include setting up subscriber clients alongside publishers, creating subscription paths with proper naming conventions, implementing robust subscription management with the ensure_subscription
pattern, and coordinating multiple Pub/Sub operations in a single application. You've also seen how the NotFound
exception enables intelligent resource management that distinguishes between expected and unexpected error conditions.
Your complete messaging system can now both publish user signup events and ensure that subscriptions exist to process them. This foundation enables sophisticated event-driven architectures in which multiple services can independently process the same events, each with its own subscription and processing logic.
In the upcoming practice exercises, you'll get hands-on experience with subscription variations and management scenarios. You'll create multiple subscriptions for the same topic, handle different event types with dedicated subscriptions, and test message delivery patterns. The next lesson will teach you how to actually pull and process messages from your subscriptions, completing your journey from event publishing through consumption and acknowledgment.
