GH-8056: Add LockRequestHandlerAdvice
Fixes: https://github.com/spring-projects/spring-integration/issues/8056 * Add `LockRequestHandlerAdvice` for exclusive service access against specific `key`.
This commit is contained in:
@@ -10,6 +10,7 @@ In addition to providing the general mechanism to apply AOP advice classes, Spri
|
||||
* `CacheRequestHandlerAdvice` (described in xref:handler-advice/classes.adoc#cache-advice[Caching Advice])
|
||||
* `ReactiveRequestHandlerAdvice` (described in xref:handler-advice/reactive.adoc[Reactive Advice])
|
||||
* `ContextHolderRequestHandlerAdvice` (described in xref:handler-advice/context-holder.adoc[Context Holder Advice])
|
||||
* `LockRequestHandlerAdvice` (described in xref:handler-advice/lock.adoc[Lock Advice])
|
||||
|
||||
|
||||
[[expression-advice]]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
[[lock-advice]]
|
||||
= Lock Advice
|
||||
|
||||
Starting with version 6.5, the `LockRequestHandlerAdvice` has been introduced.
|
||||
This advice evaluates a lock key against request message and performs `LockRegistry.executeLocked()` API.
|
||||
The goal of the advice is to achieve exclusive access to the service invocation according to the `lockKey` context, meaning that different keys may still get concurrent access to the service.
|
||||
|
||||
The `LockRequestHandlerAdvice` requires a xref:distributed-locks.adoc[LockRegistry], and a static, SpEL or function-based lock key callback.
|
||||
If `lockKey` is evaluated to `null`, no locking is held around service call.
|
||||
However, a `discardChannel` can be provided - and such a message with null key will be sent to this channel instead.
|
||||
Also, a `waitLockDuration` option can be provided to use `Lock.tryLock(long, TimeUnit)` API instead of `Lock.lockInterruptibly()`.
|
||||
|
||||
Following is a sample how a `LockRequestHandlerAdvice` can be used:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
LockRegistry lockRegistry() {
|
||||
return new DefaultLockRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
QueueChannel discardChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
LockRequestHandlerAdvice lockRequestHandlerAdvice(LockRegistry lockRegistry, QueueChannel discardChannel) {
|
||||
LockRequestHandlerAdvice lockRequestHandlerAdvice =
|
||||
new LockRequestHandlerAdvice(lockRegistry, (message) -> message.getHeaders().get(LOCK_KEY_HEADER));
|
||||
lockRequestHandlerAdvice.setDiscardChannel(discardChannel);
|
||||
lockRequestHandlerAdvice.setWaitLockDurationExpressionString("'PT1s'");
|
||||
return lockRequestHandlerAdvice;
|
||||
}
|
||||
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
@ServiceActivator(inputChannel = "inputChannel", adviceChain = "lockRequestHandlerAdvice")
|
||||
String handleWithDelay(String payload) throws InterruptedException {
|
||||
int currentCount = this.counter.incrementAndGet();
|
||||
Thread.sleep("longer_process".equals(payload) ? 2000 : 500);
|
||||
try {
|
||||
return payload + "-" + currentCount;
|
||||
}
|
||||
finally {
|
||||
this.counter.decrementAndGet();
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -31,6 +31,12 @@ The `AbstractMessageChannel` beans now throw a special `MessageDispatchingExcept
|
||||
In general, it is a design error to try to produce a message from `afterPropertiesSet()`, `@PostConstruct` or bean definition methods.
|
||||
The `SmartLifecycle.start()` is preferred way for this kind of logic, or better to do that via inbound channel adapters.
|
||||
|
||||
[[x6.5-lock-request-handler-advice]]
|
||||
== The `LockRequestHandlerAdvice`
|
||||
|
||||
A new `LockRequestHandlerAdvice` is introduced to keep the lock for a key based on a request message for message handler invocation.
|
||||
See xref:handler-advice.adoc[] for more information.
|
||||
|
||||
[[x6.5-correlation-changes]]
|
||||
== The `discardIndividuallyOnExpiry` Option For Correlation Handlers
|
||||
|
||||
|
||||
Reference in New Issue
Block a user