Dynamic Messaging Pattern-Based Pub/Sub with * and ?
In a standard Pub/Sub model, subscribers listen to specific, named channels. However, as systems scale—especially in microservices or IoT environments—subscribing to every single channel manually becomes inefficient.
Redis solves this with Pattern Matching Subscriptions (PSUBSCRIBE), allowing you to listen to multiple channels simultaneously using glob-style wildcards.
The Architecture: SUBSCRIBE vs. PSUBSCRIBE
While SUBSCRIBE requires an exact string match, PSUBSCRIBE (Pattern Subscribe) allows the subscriber to define a template.
| Feature | SUBSCRIBE | PSUBSCRIBE |
| Matching Logic | Literal String (Exact) | Glob-style Pattern |
| Wildcards | None | Supports *, ?, and [] |
| Flexibility | Low (Specific channels only) | High (Dynamic channel groups) |
1. The Multi-Level Wildcard: *
The asterisk (*) is used to match any number of characters. In messaging, this is most commonly used for hierarchical routing.
- Pattern:
orders.* - Matches:
orders.created,orders.shipped,orders.cancelled - Use Case: A "Log Analytics" service that wants to listen to all events within the "orders" domain without knowing every specific event name.
2. The Fixed-Length Wildcard: ?
The question mark (?) matches exactly one character. This is perfect for slotted systems or region codes where the length is strictly defined.
- Pattern:
sensor:??:temp - Matches:
sensor:01:temp,sensor:XY:temp - Does NOT Match:
sensor:1:temp(too short),sensor:100:temp(too long). - Use Case: A monitoring tool that listens to specific hardware racks labeled with 2-digit IDs.
Real-World Implementation in .NET Core
Using the StackExchange.Redis library, we can implement a pattern-based subscriber easily.
The Subscriber (Pattern Listener)
The Publisher
The publisher does not need to know if the subscriber is using a pattern. It simply publishes to a literal channel.
Critical Considerations for Developers
- Multiple Deliveries: If a subscriber listens to both
news.*andnews.sport, and a message is sent tonews.sport, the subscriber will receive the message twice. - Performance: While
PUBLISHis $O(N)$ where $N$ is the number of subscribers, pattern matching adds overhead because Redis must check the published channel against all active patterns. - Naming Convention: Always use a consistent delimiter (like
:or.) to make your patterns predictable (e.g.,app:module:action).
Comparison of Patterns
| Pattern | Published Channel | Result | Reason |
cache:* | cache:user:123 | MATCH | * covers all trailing characters. |
user:?:info | user:1:info | MATCH | ? matches the '1'. |
user:?:info | user:10:info | FAIL | ? expects 1 char, but found 2 ('10'). |
v1.*.logs | v1.api.logs | MATCH | * matches the 'api' segment. |