Understanding and Monitoring Realtime Heartbeats
什么是心跳消息? #
🌐 What are heartbeat messages?
心跳消息是实时客户端和服务器之间发送的定期信号,用于验证 WebSocket 连接是否存活并正常运行。客户端会定期(默认:25 秒)在 phoenix 主题上发送事件类型为 heartbeat 的心跳消息,服务器则会以 phx_reply 消息进行响应。
🌐 Heartbeat messages are periodic signals sent between the Realtime client and server to verify that the WebSocket connection is alive and functioning properly. The client sends a heartbeat message on the phoenix topic with the event type heartbeat at regular intervals (default: 25 seconds), and the server responds with a phx_reply message.
这些消息作为保持连接的机制,用来检测那些可能不会立即显现的连接问题,比如无声的网络故障或中间代理的超时。
🌐 These messages serve as a keep-alive mechanism to detect connection issues that might not be immediately apparent, such as silent network failures or intermediary proxy timeouts.
心跳为什么重要 #
🌐 Why heartbeats matter
心跳消息对于维持可靠的实时连接非常重要:
🌐 Heartbeat messages are critical for maintaining reliable real-time connections:
- 连接健康监控:它们可以检测连接何时悄悄失败,而不会触发 WebSocket 关闭事件
- 自动恢复:当心跳超时时,客户端会自动尝试重新连接
- 网络代理兼容性:许多网络代理和负载均衡器会关闭空闲连接;心跳可以保持连接活跃
- 早期问题检测:心跳超时可以在用户遇到消息传递失败之前提醒你连接出现问题
没有心跳,你的应用可能看起来已连接,但实际上无法发送或接收消息。
🌐 Without heartbeats, your application might appear connected while being unable to send or receive messages.
如何处理心跳消息 #
🌐 How to handle heartbeat messages
你可以使用 onHeartbeat 方法来监控心跳生命周期事件:
🌐 You can monitor heartbeat lifecycle events using the onHeartbeat method:
import { createClient } from '@supabase/supabase-js'import { useEffect } from 'react'function MyComponent() { const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) useEffect(() => { supabase.realtime.onHeartbeat((status) => { console.log('Heartbeat status:', status) // status can be: 'sent', 'ok', 'error', 'timeout', or 'disconnected' }) }, [supabase]) return <div>Your app content</div>}或者在客户端初始化时配置回调:
🌐 Or configure the callback during client initialization:
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, { realtime: { heartbeatCallback: (status) => { console.log('Heartbeat status:', status) }, },})排查心跳问题 #
🌐 Troubleshooting heartbeat issues
心跳超时频繁 #
🌐 Frequent heartbeat timeouts
如果你在心跳回调中频繁看到 timeout 状态:
🌐 If you're seeing frequent timeout status in your heartbeat callback:
- 网络不稳定:检查你的网络连接质量
- 防火墙/代理问题:公司防火墙或代理可能会干扰 WebSocket 连接
- 移动应用暂停:在手机上,应用可能会被暂停,这可能会停止心跳计时器
连接超时后未重新连接
客户端会自动尝试使用指数退避重新连接(1秒、2秒、5秒、10秒)。如果你需要手动重新连接:
🌐 The client automatically attempts reconnection with exponential backoff (1s, 2s, 5s, 10s). If you need to manually reconnect:
import { createClient } from '@supabase/supabase-js'import { useEffect, useState } from 'react'function ConnectionStatus() { const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) const [isConnected, setIsConnected] = useState(true) useEffect(() => { supabase.realtime.onHeartbeat((status) => { if (status === 'ok') { setIsConnected(true) } else if (status === 'timeout' || status === 'disconnected') { setIsConnected(false) } }) }, [supabase]) const handleReconnect = () => { supabase.realtime.connect() } return ( <div> <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p> {!isConnected && <button onClick={handleReconnect}>Reconnect</button>} </div> )}自定义心跳间隔 #
🌐 Customizing heartbeat interval
在 React 中创建 Supabase 客户端时,你可以调整心跳频率:
🌐 You can adjust the heartbeat frequency when creating your Supabase client in React:
import { createClient } from '@supabase/supabase-js'import { useMemo } from 'react'function App() { const supabase = useMemo( () => createClient(SUPABASE_URL, SUPABASE_KEY, { realtime: { heartbeatIntervalMs: 15000, // Send heartbeat every 15 seconds (default: 25000) }, }), [] ) return <YourApp supabase={supabase} />}注意:增加间隔可以减少网络流量,但会延迟连接失败的检测。减少间隔可以提高检测速度,但会增加开销。
🌐 Note: Increasing the interval reduces network traffic but delays detection of connection failures. Decreasing it improves detection speed but increases overhead.
Web 工作线程中的心跳错误 #
🌐 Heartbeat errors in Web workers
对于有长时间连接的 React 应用,使用 Web Workers 来防止浏览器标签页被限制:
🌐 For React applications with long-running connections, use Web Workers to prevent browser tab throttling:
import { createClient } from '@supabase/supabase-js'import { useMemo } from 'react'function App() { const supabase = useMemo( () => createClient(SUPABASE_URL, SUPABASE_KEY, { realtime: { worker: true, workerUrl: '/worker.js', // Optional: Place in public folder }, }), [] ) return <YourApp supabase={supabase} />}如果工人无法启动,请检查:
🌐 If the worker fails to start, check:
- 浏览器支持 Web Worker
- Worker 脚本在公共文件夹中,可以访问
- CORS 头允许加载工作脚本
最佳实践 #
🌐 Best practices
向用户显示连接状态 #
🌐 Show connection status to users
用指示器显示连接状态:
🌐 Display connection status with an indicator:
import { createClient } from '@supabase/supabase-js'import { useEffect, useState } from 'react'function ConnectionIndicator() { const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) const [status, setStatus] = useState('healthy') useEffect(() => { supabase.realtime.onHeartbeat((heartbeatStatus) => { if (heartbeatStatus === 'ok') { setStatus('healthy') } else if (heartbeatStatus === 'timeout') { setStatus('poor') } else if (heartbeatStatus === 'disconnected') { setStatus('disconnected') } }) }, [supabase]) return ( <div> <span>Connection: {status}</span> </div> )}当 React Native 应用回到前台时重新连接 #
🌐 Reconnect when React Native app comes to foreground
确保用户打开你的应用时连接是活跃的:
🌐 Ensure connection is active when user opens your app:
import { createClient } from '@supabase/supabase-js'import { useEffect } from 'react'import { AppState } from 'react-native'function App() { const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) useEffect(() => { const subscription = AppState.addEventListener('change', (nextAppState) => { if (nextAppState === 'active') { if (!supabase.realtime.isConnected()) { supabase.realtime.connect() } } }) return () => { subscription.remove() } }, [supabase]) return <YourApp />}监控你整个应用中的连接 #
🌐 Monitor connection in your entire app
在你创建 Supabase 客户端时设置监控:
🌐 Set up monitoring when you create your Supabase client:
import { createClient } from '@supabase/supabase-js'const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, { realtime: { heartbeatCallback: (status) => { if (status === 'ok') { console.log('Connection healthy') } else if (status === 'timeout') { console.warn('Connection slow') } else if (status === 'disconnected') { console.error('Disconnected') } }, },})export default supabase