实时快速入门

学习如何构建multiplayer.dev,这是一个合作应用,它展示了使用实时的广播、Presence和Postgres CDC。

安装supabase-js 客户端#

npm install @supabase/supabase-js

光标位置

广播允许一个客户端发送消息,多个客户端接收消息。广播的消息是短暂的。它们不会被持久化到数据库中,而是直接通过实时服务器转发。这对于发送像光标位置这样的信息是很理想的,因为最小的延迟是很重要的,但持久化则不是。

multiplayer.dev中,客户端的光标位置被发送到房间里的其他客户端。然而,在这个例子中,光标位置将是随机生成的。

你需要从你的项目的API设置中获得公共的anon访问令牌。然后你就可以设置Supabase客户端,并开始发送一个客户端的光标位置到通道room1中的其他客户端。

1const { createClient } = require('@supabase/supabase-js')
2
3const supabase = createClient('https://your-project-ref.supabase.co', 'anon-key', {
4  realtime: {
5    params: {
6      eventsPerSecond: 10,
7    },
8  },
9})
10
11// Channel name can be any string.
12// Create channels with the same name for both the broadcasting and receiving clients.
13const channel = supabase.channel('room1')
14
15// Subscribe registers your client with the server
16channel.subscribe((status) => {
17  if (status === 'SUBSCRIBED') {
18    // now you can start broadcasting cursor positions
19    setInterval(() => {
20      channel.send({
21        type: 'broadcast',
22        event: 'cursor-pos',
23        payload: { x: Math.random(), y: Math.random() },
24      })
25      console.log(status)
26    }, 100)
27  }
28})

info

JavaScript客户端有一个默认的速率限制,即每100毫秒一个实时事件,由eventsPerSecond配置。

另一个客户端可以订阅频道room1并接收游标位置。

1// Supabase client setup
2
3// Listen to broadcast messages.
4supabase
5  .channel('room1')
6  .on('broadcast', { event: 'cursor-pos' }, (payload) => console.log(payload))
7  .subscribe((status) => {
8    if (status === 'SUBSCRIBED') {
9      // your callback function will now be called with the messages broadcast by the other client
10    }
11  })

info

type 必须是 broadcastevent 必须与订阅该频道的客户相匹配。

往返延时

你也可以配置通道,使服务器必须返回一个确认函,表明它收到了 "广播 "信息。如果你想测量往返的延迟,这很有用。

1// Supabase client setup
2
3const channel = supabase.channel('calc-latency', {
4  config: {
5    broadcast: { ack: true },
6  },
7})
8
9channel.subscribe(async (status) => {
10  if (status === 'SUBSCRIBED') {
11    const begin = performance.now()
12
13    await channel.send({
14      type: 'broadcast',
15      event: 'latency',
16      payload: {},
17    })
18
19    const end = performance.now()
20
21    console.log(`Latency is ${end - begin} milliseconds`)
22  }
23})

跟踪和显示哪些用户在线

Presence存储和同步各客户端的共享状态。每当共享状态发生变化时,就会触发sync事件。join事件在新客户加入频道时被触发,leave事件在客户离开时被触发。

每个客户端可以使用通道的track方法来存储共享状态中的对象。每个客户端只能跟踪一个对象,如果track被同一个客户端再次调用,那么新的对象就会覆盖共享状态中先前跟踪的对象。你可以使用一个客户端来跟踪和显示在线的用户。

1// Supabase client setup
2
3const channel = supabase.channel('online-users', {
4  config: {
5    presence: {
6      key: 'user1',
7    },
8  },
9})
10
11channel.on('presence', { event: 'sync' }, () => {
12  console.log('Online users: ', channel.presenceState())
13})
14
15channel.on('presence', { event: 'join' }, ({ newPresences }) => {
16  console.log('New users have joined: ', newPresences)
17})
18
19channel.on('presence', { event: 'leave' }, ({ leftPresences }) => {
20  console.log('Users have left: ', newPresences)
21})
22
23channel.subscribe(async (status) => {
24  if (status === 'SUBSCRIBED') {
25    const status = await channel.track({ online_at: new Date().toISOString() })
26    console.log(status)
27  }
28})

然后,您可以使用其他客户端将其他用户添加到频道的存在状态:

1// Supabase client setup
2
3const channel = supabase.channel('online-users', {
4  config: {
5    presence: {
6      key: 'user2',
7    },
8  },
9})
10
11// Presence event handlers setup
12
13channel.subscribe(async (status) => {
14  if (status === 'SUBSCRIBED') {
15    const status = await channel.track({ online_at: new Date().toISOString() })
16    console.log(status)
17  }
18})

如果一个频道在没有存在密钥的情况下被设置,服务器会生成一个随机的UUID。type必须是presenceevent必须是syncjoin,或leave

插入和接收持久的信息

Postgres Change Data Capture (CDC)使你的客户能够插入、更新或删除数据库记录,并将这些变化发送给客户。创建一个messages表来跟踪用户在特定房间创建的消息。

1create table messages (
2  id serial primary key,
3  message text,
4  user_id text,
5  room_id text,
6  created_at timestamptz default now()
7)
8
9alter table messages enable row level security;
10
11create policy "anon_ins_policy"
12ON messages
13for insert
14to anon
15with check (true);
16
17create policy "anon_sel_policy"
18ON messages
19for select
20to anon
21using (true);

如果它还不存在,创建一个supabase_realtime发布,并将messages表添加到该出发布中。

1begin;
2  -- remove the supabase_realtime publication
3  drop publication if exists supabase_realtime;
4
5  -- re-create the supabase_realtime publication with no tables and only for insert
6  create publication supabase_realtime with (publish = 'insert');
7commit;
8
9-- add a table to the publication
10alter publication supabase_realtime add table messages;

然后你可以让客户端监听特定房间的messages表的变化,并发送和接收持久化的信息。

1// Supabase client setup
2
3const channel = supabase.channel('db-messages')
4
5const roomId = 'room1'
6const userId = 'user1'
7
8channel.on(
9  'postgres_changes',
10  {
11    event: 'INSERT',
12    schema: 'public',
13    table: 'messages',
14    filter: `room_id=eq.${roomId}`,
15  },
16  (payload) => console.log(payload)
17)
18
19channel.subscribe(async (status) => {
20  if (status === 'SUBSCRIBED') {
21    const res = await supabase.from('messages').insert({
22      room_id: roomId,
23      user_id: userId,
24      message: 'Welcome to Realtime!',
25    })
26    console.log(res)
27  }
28})