INT-4058: Add leader initiator for lock registry
JIRA: https://jira.spring.io/browse/INT-4058 If you hold the lock, you are the leader. This simple idea gets you a long way if there is no "native" leader initiator. E.g. you can use this with a RDBMS with JdbcLockRegistry. Add some docs on leader election under "endpoints" Make thread name for leader initiator unique In case there are multiple instances in the same context we would like to be able to spot them in the logs. INT-4058: Polishing * Code refactoring in the `LockRegistryLeaderInitiator`: SI use 120 line length * Add `Assert.notNull()` for required properties * Add `this.lock.unlock()` and `Thread.sleep(LockRegistryLeaderInitiator.this.busyWaitMillis)` into the `catch` block to let distributed elections to work. Otherwise there is a big chance that we will acquire the lock and become a leader again just after `yield()` * Change `LockContext.toString()` to use simple `String` concatenation which is optimized by compiler to the `StringBuilder`. That let to have some better micro-performance compared with with extra `Formatter` object in case of `String.format()` * Add `JdbcLockRegistryLeaderInitiatorTests` * Fix `yield()` logic based on the `Future.cancel(true)`. Since one `FutureTask.cancel(true)` makes it as `INTERRUPTED` any subsequent `yield()` does not make any effect, therefore our infinite selector loop isn't interrupted one more time. * Reschedule the `LeaderSelector` in the `yield()` after cancel(true). * Rework `catch` in the selector loop just to the `InterruptedException` and `return null;` to stop looping and let reschedule the selector. * Add one more `onRevoked` logic into the `final` of the selector loop to notify that we have lost leadership during `stop()` * Improve `JdbcLockRegistryLeaderInitiatorTests` to ensure that several `yield()` on the same initiator work well. * Add `What's New` note.
This commit is contained in:
@@ -630,4 +630,26 @@ The `SmartLifecycleRoleController` implements `ApplicationListener<AbstractLeade
|
||||
start/stop its configured `SmartLifecycle` objects when leadership is granted/revoked (when some bean publishes
|
||||
`OnGrantedEvent` or `OnRevokedEvent` respectively).
|
||||
|
||||
See <<zk-leadership>> for more information about leadership election and events.
|
||||
[[leadership-event-handling]]
|
||||
=== Leadership Event Handling
|
||||
|
||||
Groups of endpoints can be started/stopped based on leadership being granted or revoked respectively.
|
||||
This is useful in clustered scenarios where shared resources must only be consumed by a single instance.
|
||||
An example of this is a file inbound channel adapter that is polling a shared directory.
|
||||
(See <<file-reading>>).
|
||||
|
||||
To participate in a leader election and be notified when elected leader or when leadership is revoked, an application creates a component in the application context called a "leader initiator". Normally a leader initiator is a `SmartLifecycle` so it starts up (optionally) automatically when the context starts, and then publishes notifications when leadership changes. By convention the user provides a `Candidate` that receives the callbacks and also can revoke the leadership through a `Context` object provided by the framework. User code can also listen for `AbstractLeaderEvents`, and respond accordingly, for instance using a `SmartLifecycleRoleController`.
|
||||
|
||||
There is a basic implementation of a leader initiator based on the `LockRegistry` abstraction. To use it you just need to create an instance as a bean, for example:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public LockRegistryLeaderInitiator leaderInitiator(LockRegistry locks) {
|
||||
return new LockRegistryLeaderInitiator(locks);
|
||||
}
|
||||
----
|
||||
|
||||
If the lock registry is implemented correctly, there will only ever be at most one leader. If the lock registry also provides locks which throw exceptions (ideally `InterruptedException`) when they expire or are broken, then the duration of the leaderless periods can be as short as is allowed by the inherent latency in the lock implementation. By default there is a `busyWaitMillis` property that adds some additional latency to prevent CPU starvation in the (more usual) case that the locks are imperfect and you only know they expired by trying to obtain one again.
|
||||
|
||||
See <<zk-leadership>> for more information about leadership election and events using Zookeeper.
|
||||
|
||||
@@ -48,6 +48,11 @@ See <<integration-graph>> for more information.
|
||||
A new `JdbcLockRegistry` is provided for distributed locks shared through the data base table.
|
||||
See <<jdbc-lock-registry>> for more information.
|
||||
|
||||
==== Leader Initiator for Lock Registry
|
||||
|
||||
A new `LeaderInitiator` implementation is provided based on the `LockRegistry` strategy.
|
||||
See <<leadership-event-handling>> for more information.
|
||||
|
||||
[[x4.3-general]]
|
||||
=== General Changes
|
||||
|
||||
|
||||
@@ -64,10 +64,7 @@ to time, to remove old unused locks from memory.
|
||||
[[zk-leadership]]
|
||||
=== Zookeeper Leadership Event Handling
|
||||
|
||||
Groups of endpoints can be started/stopped based on leadership being granted or revoked respectively.
|
||||
This is useful in clustered scenarios where shared resources must only be consumed by a single instance.
|
||||
An example of this is a file inbound channel adapter that is polling a shared directory.
|
||||
(See <<file-reading>>).
|
||||
To configure an application for leader election using Zookeeper in XML:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
@@ -81,6 +78,8 @@ When leadership is revoked, an `OnRevokedEvent` will be published for the role `
|
||||
will be stopped.
|
||||
See <<endpoint-roles>> for more information.
|
||||
|
||||
In Java configuration you can create an instance of the leader initiator like this:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
|
||||
Reference in New Issue
Block a user