#webhook #callback #api-client #non-blocking #imperative #request #received

fish

基于 async/await 的命令式非阻塞回调/钩子客户端 API

3 个版本

0.0.3 2021 年 10 月 26 日
0.0.2 2020 年 11 月 22 日
0.0.1 2020 年 11 月 11 日

#3#received

每月 22 次下载

MIT/Apache

20KB
264

Fish

基于事件交互模式进行回调通信通常被认为是最理想的,但,在许多情况下我们发现这会导致复杂性、错误和不需要的工作。 Fish 为基于回调和钩子的 API 提供了命令式非阻塞运行时交互。

让我们设想一个例子来看看它是如何工作的:假设你是某个配送应用插件的开发者。

应用会通过钩子通知你 (... 通过钩子) 当一个新的配送区域开放时。有一个端点允许你为该区域的新的订单注册一个钩子。

每当收到一个订单时,你可以发送一个请求来接受它。如果请求被批准,配送应用将向你发送一个回调。哦,我忘了提,你是一名经纪人,所以,每当收到订单时,你需要将其传递给你的履行客户,他们将同样在希望履行订单的情况下发送回调。

在每个步骤中,你都有特定于领域的逻辑和状态,这些通常在整个生命周期中被使用。

Fish 中,这种交互看起来像这样

// Step 1: Register webhook for new delivery regions
let orders = server.spawn();

app.send("region", RegisterRegion {
    webhook_url: orders.url(),
    region_id
}).await;

// orders is a Stream. You could concatenate multiple regions' orders together,
// or maybe handle them separately
while let Ok(order) = orders.next().await {
   // Step 2: Let's see if our fulfillment partner is interested in the order
   let fulfillment = server.spawn();

    partner.send("order", NotifyOrder {
        callback: fulfillment.url(),
        ..order
    }).await;

    // The partner will send us back a POST request if they want to fulfill the order,
    // and do-nothing otherwise. We have a time limit to adhere to, so we'll give them
    // 5 seconds to respond
   if let Ok(_) = timeout(Duration::from_secs(5), fulfillment.next().await) {
     // Step 3: OK, we're set, lets let the app know we are interested!
     let granted = server.spawn();
     app.send("order", AcceptOrder {
          callback_url: url,
          ..
     }).await;

     // granted.next() and so on!
   }
}

好的,我们可以进一步展开这个示例。而且严重缺失的是在编排过程中发生的特定于领域的逻辑、状态更新、缓存等等。我们发现以基于事件处理器的方式做这项工作需要巨大的努力。

那么你呢?你是否觉得这很具挑战性?有什么建议吗?

许可证:MIT OR Apache-2.0

考虑一下

这种模式如何与 CI/CD、硬件故障等相结合?例如,假设有 10 个挂起的回调。如果服务器重启,接收器将停止。在一个“基于事件”的系统,服务器将重新启动,一切都会顺利,第三方可能会在服务器宕机时看到 504 错误并重试。

存在不可恢复的状态。

依赖项

~9–19MB
~271K SLoC