kevy4.0

实时

推给所有
正在听的人

聊天、在线状态、通知、一个会自己更新的仪表盘。一次发布,多个订阅者,不用轮询。而在浏览器版本里,同一套发布订阅在两个标签页之间也能用,完全不需要服务端。

为什么合适——以及哪里不合适

发布订阅是发完就不管的。一条消息只会送给那一刻正在订阅的人;晚一秒才连上来的人永远看不到它,而且没有确认。对于一次在线心跳、一个实时计数器,这正好合适;对于任何你丢了会心疼的东西,这正好不合适。

如果丢一条消息是要紧的,就改用 stream——见队列。stream 保留历史,支持消费组,还能让掉过线的客户端把落下的补回来。发布订阅是便宜的那一个;便宜本身就是这笔交易的内容。

怎么做

频道,以及一次订阅一整族频道的模式匹配。
# subscriber
SUBSCRIBE room:42
PSUBSCRIBE room:*          # every room, one connection

# publisher — returns how many subscribers received it
PUBLISH room:42 '{"user":"ada","text":"hello"}'
-> (integer) 3

# presence: the TTL does the expiry, the client refreshes every 10 s
SET presence:ada online EX 30

# who is here. on a large keyspace prefer a set:
SADD online ada
SMEMBERS online
SREM online ada

同一件事,在浏览器标签页里

同源的两个标签页,没有服务端,也没有 WebSocket。桥是一个 BroadcastChannel,过滤发生在引擎内部。
import { open } from "@goliajp/kevy";

const db = await open({ persist: { name: "app" } });

// tab A
db.subscribe("room:42", (payload, channel) => {
  render(JSON.parse(new TextDecoder().decode(payload)));
});

// tab B — tab A receives it
db.publish("room:42", JSON.stringify({ user: "ada", text: "hello" }));
代价是什么

跟不上的订阅者会被丢掉,而不是被无限缓冲。如果一个客户端跟不上,它的消息会被丢弃,而不是让服务端的内存无上限地涨下去——这是有意的选择,在你依赖投递之前,应该先知道它。这里没有确认,也没有重放。只要这两样里有一样要紧,你要的就是 stream,不是频道。发布订阅指南把边界写得很具体。

接下来