跳到正文

功能

实时里难的那部分,已经在运行

一个实时功能,是少量产品代码压在大量投递机制之上:谁 subscribe 了,谁有权限,谁还在。PushCaster 就是后半部分。

Channel

四种,而前缀就是约定

一个频道的名字决定了它怎么表现。前缀告诉库:要不要向你的服务器要鉴权、要不要等一份成员列表、要不要加密。

频道的种类、谁可以 subscribe、拿来做什么,以及订阅是怎么鉴权的
前缀谁可以 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 把它们签名后回传给你——这样第一位观众到达时你就能开工,最后一位离开时就能停下。

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 是怎么走的

建一个应用,接上一个客户端。

Support