Kotlin et Java
Une seule bibliothèque pour les deux langages : une API suspend pour Kotlin, et sendBlocking, sendAsync et des builders pour Java, sur java.net.http.HttpClient. Ses seules dépendances sont la bibliothèque standard de Kotlin et les coroutines.
- Paquet
-
app.honk-me:sdkBientôt disponible - Code source
- github.com/honk-me/honk-kotlin · Licence MIT
- Prérequis
- JDK 17 ou plus récent.
La publication sur Maven Central attend l’espace de noms Sonatype. D’ici là, compilez la bibliothèque depuis le dépôt (./gradlew publishToMavenLocal) ou utilisez cURL.
Installation
Tout se trouve dans le paquet 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> Une clé d’ingestion (honk_…) permet à quiconque la détient d’envoyer des messages dans son projet. Gardez-la sur vos serveurs, dans vos tâches et dans les secrets de votre CI, jamais dans un navigateur ni dans une app mobile ou de bureau.
Envoyer un événement
Créez un seul Honk par application et fermez-le à l’arrêt.
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()); Recette : une demande client avec Spring Boot ou Ktor
La bibliothèque ne dépend pas de Spring ; il s’agit simplement de code dans un service Spring. Envoyez après la validation de la transaction : le client n’attend jamais le réseau.
@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}")
}
} L’échelle Honk et les incidents
En Java, Message.longHonk(…) remplace long, qui est un mot-clé.
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")) Erreurs
Toutes les exceptions sont non vérifiées et étendent HonkException, avec isRetryable et la clé d’idempotence à réutiliser pour réessayer.
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;
} Des nouvelles tentatives sans doublon
- Chaque envoi porte une
Idempotency-Key: la vôtre, ou un UUIDv7 généré pour l’occasion. La même clé est réutilisée à chaque nouvelle tentative, et pendant 24 heures Honk répond à une requête rejouée avec l’identifiant d’origine etduplicate: true: une réponse perdue ne crée jamais de second message. - Une nouvelle tentative n’a lieu qu’en cas d’erreur réseau, de délai dépassé, de
429ou de5xx, avec un backoff exponentiel et un jitter complet, jamais avant leRetry-Afterdu serveur. Les autres réponses4xxne donnent lieu à aucune nouvelle tentative : corrigez plutôt la requête. - Chaque tentative expire au bout de 5 secondes, et tout s’arrête au bout de 30 secondes au total. Une attente qui dépasserait cette échéance, comme un quota quotidien remis à zéro à minuit, échoue immédiatement et indique quand réessayer.
- Les champs sont vérifiés localement avant l’envoi, et tous les champs invalides sont signalés en une seule fois.