В настоящее время я работаю с LockRegistryLeaderInitiator:
org.springframework.integration.support.leader.LockRegistryLeaderInitiator
и JdbcLockRegistry:
org.springframework.integration.jdbc.lock.JdbcLockRegistry
, и я вижупроблема, когда узлы, не являющиеся лидерами, делают вызовы базы данных каждые 100 мс.
Я думаю, что могу видеть, что происходит.Инициатор лидера входит в тесную петлю, когда блокировка не удерживается:
while (isRunning()) {
...
// We always try to acquire the lock, in case it expired
boolean acquired = this.lock.tryLock(LockRegistryLeaderInitiator.this.heartBeatMillis,
TimeUnit.MILLISECONDS);
if (!this.locked) {
if (acquired) {
// Success: we are now leader
this.locked = true;
handleGranted();
}
else if (isPublishFailedEvents()) {
publishFailedToAcquire();
}
}
...
}
Время ожидания вызова tryLock(...)
контролирует скорость, с которой этот код повторяется, блокируясь на период одного такта, еслиблокировка не может быть получена.
Проблема возникает в том, как tryLock записан в некоторых реализациях LockRegistry.Возьмите JdbcLockRegistry, например:
while (true) {
try {
while (!(acquired = doLock()) && System.currentTimeMillis() < expire) { //NOSONAR
Thread.sleep(100); //NOSONAR
}
...
}
...
}
Метод tryLock будет вращаться, многократно отправляя запросы к БД каждые 100 мс, пока не истечет время ожидания.Поэтому, хотя инициатор блокировки не удерживает блокировку, он будет повторять вызовы каждые 100 мс до бесконечности.
Мне удалось решить эту проблему с помощью следующего изменения метода LockRegistryLeaderInitiator call()
:
while (isRunning()) {
try {
...
//We always try to acquire the lock, in case it expired
boolean acquired = this.lock.tryLock(); // # Make a single attempt to acquire the lock
if (!this.locked) {
if (acquired) {
// Success: we are now leader
this.locked = true;
handleGranted();
} else if (isRunning()) {
// Wait before trying again.
Thread.sleep(LockRegistryLeaderInitiator.this.heartBeatMillis); // # Make the heartbeat an explicit thread sleep.
}
}
...
}
...
}
Это поведение по своему замыслу или ошибка?
Редактировать: LockRegistryLeaderInitiator имеет два свойства конфигурации для управления реакцией выборов:
/**
* Time in milliseconds to wait in between attempts to re-acquire the lock, once it is
* held. The heartbeat time has to be less than the remote lock expiry period, if
* there is one, otherwise other nodes can steal the lock while we are sleeping here.
* If the remote lock does not expire, or if you know it interrupts the current thread
* when it expires or is broken, then you can extend the heartbeat to Long.MAX_VALUE.
*/
private long heartBeatMillis = DEFAULT_HEART_BEAT_TIME;
/**
* Time in milliseconds to wait in between attempts to acquire the lock, if it is not
* held. The longer this is, the longer the system can be leaderless, if the leader
* dies. If a leader dies without releasing its lock, the system might still have to
* wait for the old lock to expire, but after that it should not have to wait longer
* than the busy wait time to get a new leader. If the remote lock does not expire, or
* if you know it interrupts the current thread when it expires or is broken, then you
* can reduce the busy wait to zero.
*/
private long busyWaitMillis = DEFAULT_BUSY_WAIT_TIME;
... и этоПохоже, что busyWaitMillis должен быть задействован, когда блокировка не удерживается и не может быть получена, но в данном случае она не используется.