Kotlin und Java
Eine Bibliothek für beide Sprachen: eine suspend-API für Kotlin und sendBlocking, sendAsync und Builder für Java, auf java.net.http.HttpClient. Ihre einzigen Abhängigkeiten sind die Kotlin-Standardbibliothek und Coroutines.
- Paket
-
app.honk-me:sdkDemnächst - Quellcode
- github.com/honk-me/honk-kotlin · MIT-Lizenz
- Voraussetzungen
- JDK 17 oder neuer.
Das Release auf Maven Central wartet auf den Sonatype-Namespace. Bis dahin baust du die Bibliothek aus dem Repository (./gradlew publishToMavenLocal) oder nutzt cURL.
Installation
Alles liegt im Paket 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> Mit einem Ingest-Schlüssel (honk_…) kann jeder an sein Projekt senden. Er gehört auf Server, in Jobs und in CI-Secrets, nie in einen Browser, eine Mobil- oder Desktop-App.
Ein Ereignis senden
Erstelle eine Honk-Instanz pro Anwendung und schließe sie beim Herunterfahren.
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()); Rezept: eine Kundenanfrage mit Spring Boot oder Ktor
Die Bibliothek hängt nicht von Spring ab; das hier ist normaler Code in einem Spring-Service. Sende erst nach dem Commit der Transaktion, damit der Kunde nie auf das Netzwerk wartet.
@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}")
}
} Die Honk-Skala und Vorfälle
In Java steht Message.longHonk(…) für long, weil das ein Schlüsselwort ist.
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")) Fehler
Alle Exceptions sind unchecked und erweitern HonkException, mit isRetryable und dem Idempotency-Key für die Wiederholung.
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;
} Retries ohne Doppelversand
- Jedes Senden hat einen
Idempotency-Key: deinen oder eine neue UUIDv7. Jeder Retry nutzt denselben Schlüssel, und innerhalb von 24 Stunden beantwortet Honk eine erneut gesendete Anfrage mit der ursprünglichen ID undduplicate: true. Geht eine Antwort verloren, entsteht also nie eine zweite Nachricht. - Wiederholt werden nur Netzwerkfehler, Timeouts,
429und5xx, mit exponentiellem Backoff und vollem Jitter, nie früher als dasRetry-Afterdes Servers. Andere4xx-Antworten werden nie wiederholt: Korrigiere stattdessen die Anfrage. - Jeder Versuch bricht nach 5 Sekunden ab, und nach insgesamt 30 Sekunden ist Schluss. Wäre die nötige Wartezeit länger, etwa bis ein Tageskontingent um Mitternacht zurückgesetzt wird, schlägt der Aufruf sofort fehl und sagt dir, wann du es erneut versuchen kannst.
- Felder werden vor dem Senden geprüft, und alle ungültigen Felder werden auf einmal gemeldet.