Biblioteca Ticketing para Java / Kotlin
GitHub Maven CentralUn único TicketBroker ofrece tres estilos de llamada juntos — las corrutinas de Kotlin usan acquire, el Java asíncrono usa acquireAsync (CompletableFuture), y el Java bloqueante usa acquireBlocking. Uses el que uses, es el mismo broker y las mismas conexiones.
Repositorio
Ejemplo
Ejemplo básico
Cree un broker al iniciar la aplicación y compártalo. wait=0 es un intento inmediato sin cola, máximo 255 s; lease es 1–250 s. El minimum-work budget final es obligatorio y puede ser cero. Las fracciones de segundo se redondean hacia arriba y los valores fuera de rango o un budget mayor que el lease normalizado se rechazan antes de enviar, nunca se clampan.
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.
}El close/drop automático es un release bounded best-effort. Use la API de release explícito si importa el resultado. El token debe aplicar fencing al write DB protegido dentro de la misma transaction.
Conviene saber (comportamiento)
- Si pudo enviarse un byte, perder la respuesta definitiva produce Indeterminate. No se reenvía
Aautomáticamente con el mismo owner; el caller no entra en la sección crítica. - Cancel antes de send es unsent; tras possible-send cierra la sesión. Si se parseó a la vez un grant token, intenta un release compensatorio exact-token limitado.
M, todos losEy respuestas malformed/oversized/unknown son session-fatal. Acquires possible-send no resueltos quedan Indeterminate.Bes rechazo definitivo de capacity y vuelve inmediatamente. Sin retry interno; el caller puede iniciar acquire nuevo con owner nuevo y backoff de aplicación.- Releases explícitos/compensatorios reintentan exact token solo durante 5 segundos absolutos desde call/enqueue.
Réxito,Nausente/no actual; sin respuesta final es error, nunca éxito supuesto. - Solo se entrega Ticket con tiempo conservador positivo suficiente para work budget. Trabajo superior a 250 s es unsupported antes de enviar.
- El fencing DB con token es obligatorio: en la misma transaction rechace
token <= stored_high_water, actualice high-water y haga business write antes de commit/rollback y release.
Opciones de seguridad (Token · TLS)
Todas las opciones son opcionales. token debe coincidir con client_tokens del servidor, y TLS tiene cuatro modos: desactivado / almacén de confianza del sistema / una CA indicada / omitir verificación (solo para pruebas).
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()En Java, usa las factorías estáticas TlsMode.systemRoots() / TlsMode.ca("ca.crt") / TlsMode.insecureSkipVerify().
Puedes generar la configuración del token, TLS y el clúster en el lado del servidor en la página Despliegue del servidor Ticketing.
Hilos virtuales de Spring
Si ejecutas Spring MVC sobre hilos virtuales, usa las llamadas *Blocking, tanto en Java como en Kotlin. Un controlador no suspendido no puede llamar a acquire, así que acquireBlocking también es la vía normal en Kotlin. No pasan por corrutinas y solo se aparcan en la respuesta, por lo que el hilo portador queda libre.
@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 {
// sección crítica
}
return "ok"
}
}La liberación en segundo plano de close() (try-with-resources / use) se ejecuta por defecto en un executor de hilos virtuales, así que nunca se encola detrás de un pool de tamaño fijo. Pasa tu propio executor para usar el de Spring.
TicketBroker.builder()
.addrs("127.0.0.1:5225")
.executor(applicationTaskExecutor)
.connect()Con WebFlux o controladores de corrutinas, sigue usando las llamadas suspend.