Kotlin y Java
Una sola biblioteca para los dos lenguajes: una API suspend para Kotlin, y sendBlocking, sendAsync y builders para Java, sobre java.net.http.HttpClient. Sus únicas dependencias son la biblioteca estándar de Kotlin y las corrutinas.
- Paquete
-
app.honk-me:sdkPróximamente - Código fuente
- github.com/honk-me/honk-kotlin · Licencia MIT
- Requisitos
- JDK 17 o posterior.
La publicación en Maven Central está a la espera del namespace de Sonatype. Mientras tanto, compílala desde el repositorio (./gradlew publishToMavenLocal) o usa cURL.
Instalación
Todo está en el paquete app.honkme.
// build.gradle.kts
dependencies { implementation("app.honk-me:sdk:0.1.0") } <!-- pom.xml -->
<dependency>
<groupId>app.honk-me</groupId>
<artifactId>sdk</artifactId>
<version>0.1.0</version>
</dependency> Quien tenga una clave de ingesta (honk_…) puede enviar mensajes a su proyecto. Guárdala en servidores, tareas y secretos de CI, nunca en un navegador ni en una app móvil o de escritorio.
Enviar un evento
Crea un solo Honk por aplicación y ciérralo al apagarla.
import app.honkme.Honk
val honk = Honk.fromEnvironment() // HONK_URL, HONK_KEY (+ HONK_SOURCE, HONK_ENVIRONMENT, HONK_CHANNEL)
honk.beep("Backup finished", "nightly pg_dump took 42 s") // suspend import app.honkme.Honk;
import app.honkme.Message;
Honk honk = Honk.fromEnvironment();
honk.sendBlocking(Message.beep("Backup finished", "nightly pg_dump took 42 s").build()); Receta: una solicitud de cliente con Spring Boot o Ktor
La biblioteca no depende de Spring; esto es código normal en un servicio de Spring. Envía después de confirmar la transacción, para que el cliente nunca espere a la red.
@Configuration
class HonkConfig {
@Bean(destroyMethod = "close")
Honk honk(@Value("${honk.url}") String url, @Value("${honk.key}") String key) {
return Honk.builder().url(url).key(key).build(); // one client, keep-alive
}
}
@Service
class CustomerRequestNotifier {
private static final Logger log = LoggerFactory.getLogger(CustomerRequestNotifier.class);
private final Honk honk;
CustomerRequestNotifier(Honk honk) { this.honk = honk; }
// After the request is saved: never block the customer's request on the network.
@TransactionalEventListener
void on(CustomerRequestCreated event) {
CustomerRequest r = event.request();
Message message = Message.light("New request: " + r.subject(), r.name() + " (" + r.company() + ") asked: " + r.body())
.priority("high") // push right away
.category(Category.CUSTOMERS)
.channel("requests")
.groupKey("requests/" + r.id()) // one group per request
.url("https://shop.example.com/admin/requests/" + r.id()) // https only
.meta("request_id", String.valueOf(r.id()))
.build();
honk.sendAsync(message, "request-" + r.id()) // same request, same key
.exceptionally(e -> { log.warn("honk: {}", e.getMessage()); return null; });
}
} scope.launch {
honk.light("New request: ${r.subject}", "${r.name} (${r.company}) asked: ${r.body.take(2000)}") {
priority(Priority.HIGH)
category(Category.CUSTOMERS)
groupKey("requests/${r.id}")
url("https://shop.example.com/admin/requests/${r.id}")
idempotencyKey("request-${r.id}")
}
} La escala Honk e incidentes
En Java, Message.longHonk(…) sustituye a long, que es una palabra reservada.
honk.loud("Disk 91%", "/var on app-01") { groupKey("disk/app-01/var") }
honk.problem("db/backup", "Backup failed", "pg_dump exited with 1") // a long honk by default
honk.recovery("db/backup", "Backup OK", "pg_dump finished in 41 s") // a beep by default
honk.send(Message("Imported 1 204 rows", severity = Severity.BEEP, channel = "imports")) Errores
Todas las excepciones son no comprobadas y extienden HonkException, con isRetryable y la clave de idempotencia con la que reintentar.
try {
honk.sendBlocking(Message.longHonk("Payment failed", "Stripe declined order 1042").groupKey("payments/stripe").build());
} catch (HonkValidationException e) {
log.error("bug: {}", e.getFields());
} catch (HonkException e) {
if (e.isRetryable()) queue.retryLater(e.getIdempotencyKey(), e.getRetryAfter());
else throw e;
} Reintentos que nunca envían dos veces
- Cada envío lleva una
Idempotency-Key: la tuya o un UUIDv7 nuevo. La misma clave se reutiliza en cada reintento y, durante 24 horas, Honk responde a una repetición con el id original yduplicate: true, así que una respuesta perdida nunca crea un segundo mensaje. - Solo se reintentan los errores de red, los tiempos de espera agotados,
429y5xx, con backoff exponencial y jitter completo, y nunca antes delRetry-Afterdel servidor. Las demás respuestas4xxno se reintentan nunca: hay que corregir la solicitud. - Cada intento se corta a los 5 segundos y todo se detiene a los 30. Una espera que superaría ese plazo, como una cuota diaria que se reinicia a medianoche, falla al momento y te dice cuándo reintentar.
- Los campos se validan antes de enviar, y todos los errores de validación se devuelven juntos.