Wildcards
- RabbitMQ
- Kafka
You may need to listen variable event names that have the same structure, in that case you have the
method handleDynamicEvents
in the HandlerRegistry
, so you can specify a pattern with '*' wildcard, it does not
creates a binding in the broker, but allows that you do it dynamically through a DynamicRegistry
class.
You can also create binding with '#' wildcard, it is used to listen multiple words, for example animals.#
will listen
to animals.dog
, animals.dog.bark
, animals.cat
, animals.cat.meow
, etc.
DynamicRegistry API
public interface DynamicRegistry {
Mono<Void> startListeningEvent(String eventName);
Mono<Void> stopListeningEvent(String eventName);
//... other definitions for queries
}
To start listening a new event dynamically at runtime, you should inject and call a method of the DynamicReggistry, for example:
@Component
@AllArgsConstructor
public class DynamicSubscriber {
private final DynamicRegistry registry;
public Mono<Void> listenNewEvent(String eventName) {
return registry.startListeningEvent(eventName);
}
}
The conditions for a success dynamic registry functionality are:
- You should handle dynamic events with specific wildcard
@Configuration
public class HandlerRegistryConfiguration {
@Bean
public HandlerRegistry handlerRegistry(EventsHandler events) {
return HandlerRegistry.register()
.handleDynamicEvents("purchase.*", events::handleEventA, Object.class/*change for proper model*/);
}
}
- Start a listener dynamically through
registry.startListeningEvent("purchase.cancelled");
You also can listen with * wildcard or # wildcard, the * wildcard is for a single word and # wildcard is for multiple words, for example:
animals.*
will listen to animals.dog
, animals.cat
, animals.bird
, etc.
animals.#
will listen to animals.dog
, animals.dog.bark
, animals.cat
, animals.cat.meow
, etc.
@Configuration
public class HandlerRegistryConfiguration {
@Bean
public HandlerRegistry handlerRegistry(EventsHandler events) {
return HandlerRegistry.register()
.listenEvent("animals.*", events::handleEventA, Object.class/*change for proper model*/)
.listenEvent("pets.#.any", events::handleEventA, Object.class/*change for proper model*/);
}
}
This last approach is useful when you have a dynamic event name, for example, you can have a purchase.cancelled
event,
but you can also have a purchase.cancelled.2021
event, so you can listen to all of them with purchase.*
or purchase.#
respectively.
Priorities
The handlers with wildcards have the lowest priority, so if you have a specific handler for an event name, it will be called before the wildcard handler.
The wildcard handler will be called if there is no specific handler for the event name. And the wildcard matches the pattern.
General conditions for handler priority are:
- fixed words has priority over wildcard
- wildcard with * has priority over wildcard with #
- wildcard with # has the lowest priority
The next code will help you to avoid unexpected behaviors, which indicates you the handler that will be called.
public static void main(String[] args) {
Set<String> names = Set.of("prefix.*.*", "prefix.*.#");
String target = "prefix.middle.suffix";
String handler = new KeyMatcher().match(names, target);
System.out.println(handler);
}
Possible issues
Unprocessed events will be notified with an event, so please handle it properly and ensure you don't have wildcards that match all events, because it can cause unexpected behaviors.
For example if you have a handler for my.event.#
and you start listening to my.event
, it will match all events that
start with my.event
, so it will match my.event.any
if your handler has dlq retries or has local retries, when your
handler fails the retry limit, an event with name my.event.any.dlq
will be sent.
And if you have the same binding my.event.#
the event will be handled by the handler, so you will have two different
event structures for the same handler, and it can cause unexpected behaviors.
Example
You can see a real example at samples/async/async-receiver-responder
Comming soon...