Библиотека Ticketing для Java / Kotlin
GitHub Maven CentralОдин TicketBroker предоставляет сразу три способа вызова — корутины Kotlin через acquire, асинхронный Java через acquireAsync (CompletableFuture), блокирующий Java через acquireBlocking. Какой бы вы ни использовали, это один и тот же брокер и одно и то же соединение.
Репозиторий
Пример
Базовый пример
Создайте один broker при запуске приложения и используйте совместно. wait=0 — одна немедленная попытка без queue, максимум 255 с; lease — 1–250 с. Последний minimum-work budget обязателен и может быть нулём. Доли секунды округляются вверх; значения вне диапазона и budget больше нормализованной lease отклоняются до отправки, без clamp.
Kotlin (coroutines):
val broker = TicketBroker.connect("127.0.0.1:5225")
broker.waitReady(Duration.ofSeconds(5))
val ticket = broker.acquire("key", Duration.ofSeconds(5), Duration.ofSeconds(30), Duration.ofSeconds(2))
val token = ticket.token
// In the same DB transaction: verify/update token high-water and perform the business write.
ticket.release()Java (blocking):
TicketBroker broker = TicketBroker.connect("127.0.0.1:5225");
broker.waitReadyBlocking(Duration.ofSeconds(5));
try (Ticket ticket = broker.acquireBlocking(
"key", Duration.ofSeconds(5), Duration.ofSeconds(30), Duration.ofSeconds(2))) {
long token = ticket.getToken();
// In the same DB transaction: verify/update token high-water and perform the business write.
}Автоматический close/drop — ограниченный best-effort release. Если важен результат, используйте явный release API. Token обязан fence защищённый DB write в той же transaction.
Полезно знать (поведение)
- Если мог быть отправлен хотя бы один request byte, потеря окончательного ответа даёт Indeterminate.
Aне отправляется автоматически с тем же owner; caller не входит в critical section. - Cancel до send — unsent; после possible-send закрывает session. Если grant token параллельно распознан, выполняется ограниченный компенсационный exact-token release.
M, каждыйEи malformed/oversized/unknown response — session-fatal. Неопределённые possible-send acquire становятся Indeterminate.B— окончательный capacity reject и возвращается сразу. Внутреннего retry нет; caller может после application backoff начать новый acquire с новым owner.- Явный/компенсационный release повторяет exact token только абсолютные 5 секунд от call/enqueue.
Rуспех,Nотсутствует/не текущий; нет окончательного ответа — ошибка, не предполагаемый успех. - Ticket выдаётся лишь при положительном консервативном остатке времени, достаточном для work budget. Работа свыше 250 с unsupported до отправки.
- DB fencing по token обязателен: в той же transaction отклонить
token <= stored_high_water, обновить high-water и выполнить business write до commit/rollback и release.
Параметры безопасности (токен · TLS)
Все опции необязательны. token должен совпадать с client_tokens сервера, а TLS имеет четыре режима: выкл / системное хранилище доверия / указанный CA / пропуск проверки (только для тестов).
val broker = TicketBroker.builder()
.addrs("10.0.0.1:5225", "10.0.0.2:5225", "10.0.0.3:5225")
.token("123")
.tls(TlsMode.SystemRoots)
// .tls(TlsMode.Ca("ca.crt"))
// .tls(TlsMode.InsecureSkipVerify)
.connect()В Java используйте статические фабрики TlsMode.systemRoots() / TlsMode.ca("ca.crt") / TlsMode.insecureSkipVerify().
Настроить токен, TLS и конфигурацию кластера на стороне сервера можно на странице Развёртывание сервера Ticketing.
Виртуальные потоки Spring
Если Spring MVC работает на виртуальных потоках, используйте вызовы *Blocking — и в Java, и в Kotlin. Не-suspend контроллер не может вызвать acquire, поэтому в Kotlin acquireBlocking тоже является обычным путём. Они не проходят через корутины и паркуются только на ответе, так что несущий поток остаётся свободным.
@RestController
class OrderController(private val broker: TicketBroker) {
@PostMapping("/orders/{id}")
fun place(@PathVariable id: String): String {
broker.acquireBlocking("order-$id", Duration.ofSeconds(5), Duration.ofSeconds(30)).use {
// критическая секция
}
return "ok"
}
}Фоновое освобождение за close() (try-with-resources / use) по умолчанию выполняется на исполнителе виртуальных потоков, поэтому освобождения не выстраиваются в очередь за пулом фиксированного размера. Передайте свой исполнитель, чтобы использовать спринговый.
TicketBroker.builder()
.addrs("127.0.0.1:5225")
.executor(applicationTaskExecutor)
.connect()С WebFlux или корутинными контроллерами продолжайте использовать вызовы suspend.