功能
实时里难的那部分,已经在运行
一个实时功能,是少量产品代码压在大量投递机制之上:谁 subscribe 了,谁有权限,谁还在。PushCaster 就是后半部分。
Channel
四种,而前缀就是约定
一个频道的名字决定了它怎么表现。前缀告诉库:要不要向你的服务器要鉴权、要不要等一份成员列表、要不要加密。
| 前缀 | 谁可以 subscribe | 拿来做什么 | 鉴权 |
|---|---|---|---|
| public | 任何拿到 app key 的人。 | 构建状态、实时比分、公开的流。 | 无 |
| private- | 由你的服务器逐个订阅者决定。 | 任何限定在某个账户或某个资源上的东西。 | 需签名 |
| presence- | 需鉴权,并且会带上成员列表。 | 房间和会话,成员本身就是功能的那类场景。 | 需签名 |
| private-encrypted- | 需鉴权;负载由你的客户端加密。 | 连我们也不该读到的负载。 | 需签名 |
cache- 频道会投递实时事件,但不会重放已保存的状态——参见 已知差异.
鉴权
裁决权仍在你的服务器
private 和 presence 的订阅不是我们批准的。是你的应用批准,并用你的 app 密钥给它的回答签名。我们只验证签名,别的什么也不做。
- 一个端点POST /pusher/auth 的请求和响应形状原样保留。
- 你的规则这个套接字能不能加入这个频道,是只有你能回答的问题。
- presence 里的身份同一次调用会返回成员 id,以及其他成员应当看到的一切。
routes/pusher-auth.js
js
import Pusher from 'pusher'const pusher = new Pusher({ appId: process.env.PUSHCASTER_APP_ID, key: process.env.PUSHCASTER_KEY, secret: process.env.PUSHCASTER_SECRET, host: 'api-eu1.pushcaster.io', useTLS: true,})// Called by the client library whenever it subscribes to a// private-, presence- or private-encrypted- channel.app.post('/pusher/auth', (req, res) => { const { socket_id, channel_name } = req.body const user = req.session.user if (!user || !mayJoin(user, channel_name)) { return res.sendStatus(403) } res.send( pusher.authorizeChannel(socket_id, channel_name, { user_id: String(user.id), user_info: { name: user.name }, }), )})Webhook
套接字层看到的,你的后端听得到
在线人数和成员变化只在连接处可见。Webhook 把它们签名后回传给你——这样第一位观众到达时你就能开工,最后一位离开时就能停下。
| Event | 何时触发 |
|---|---|
| channel_occupied | 第一个订阅者加入了一个空的频道。 |
| channel_vacated | 最后一个订阅者离开了。 |
| member_added | 有人加入了一个 presence 频道。 |
| member_removed | 有人离开了一个 presence 频道。 |
| client_event | 某个客户端直接向一个频道发了事件。 |
| cache_miss | 有人向某个 cache 频道索要了它并不持有的状态。 |
routes/webhooks.js
js
app.post('/pushcaster/webhooks', (req, res) => { const hook = pusher.webhook({ headers: req.headers, rawBody: req.rawBody }) // The signature is computed with your app secret, so an // unsigned or mismatched delivery never reaches your logic. if (!hook.isValid()) return res.sendStatus(401) for (const event of hook.getEvents()) { if (event.name === 'channel_occupied') startFeed(event.channel) if (event.name === 'channel_vacated') stopFeed(event.channel) } res.sendStatus(200)})库
没有 PushCaster SDK,这是有意的
PushCaster 在线上说的就是 Pusher Channels 协议,所以官方库不改一行就能对着它工作。没有我们的包要加,也没有我们的东西会在我们发版时崩掉。
- pusher-js
- Laravel Echo
- pusher/pusher-php-server
- pusher-http-node
- Python
- Go
- Ruby
- Java
- Android
- iOS
- Flutter
- Unity
完整的安装命令: npm i pusher-js.
基础设施
两层,以及一个由你挑选的集群
投递跑在与计费、配置分开的一层上,所以 control plane 出故障只会让仪表盘降级,不会中断投递。
当前在运行
- eu1西欧
- eu2中欧
集群对消息投递是高可用的,对 presence 则不是:如果某个 presence 存储丢失,presence 和在线人数会降级,而 public 与 private 频道仍在继续投递。这件事在这里读到,好过日后自己撞上。
看看一次 publish 是怎么走的