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:
1import { createClient } from '@supabase/supabase-js'2import { useEffect } from 'react'34function MyComponent() {5 const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)67 useEffect(() => {8 supabase.realtime.onHeartbeat((status) => {9 console.log('Heartbeat status:', status)10 // status can be: 'sent', 'ok', 'error', 'timeout', or 'disconnected'11 })12 }, [supabase])1314 return <div>Your app content</div>15}或者在客户端初始化时配置回调:
🌐 Or configure the callback during client initialization:
1const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {2 realtime: {3 heartbeatCallback: (status) => {4 console.log('Heartbeat status:', status)5 },6 },7})排查心跳问题 #
🌐 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:
1import { createClient } from '@supabase/supabase-js'2import { useEffect, useState } from 'react'34function ConnectionStatus() {5 const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)6 const [isConnected, setIsConnected] = useState(true)78 useEffect(() => {9 supabase.realtime.onHeartbeat((status) => {10 if (status === 'ok') {11 setIsConnected(true)12 } else if (status === 'timeout' || status === 'disconnected') {13 setIsConnected(false)14 }15 })16 }, [supabase])1718 const handleReconnect = () => {19 supabase.realtime.connect()20 }2122 return (23 <div>24 <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>25 {!isConnected && <button onClick={handleReconnect}>Reconnect</button>}26 </div>27 )28}自定义心跳间隔 #
🌐 Customizing heartbeat interval
在 React 中创建 Supabase 客户端时,你可以调整心跳频率:
🌐 You can adjust the heartbeat frequency when creating your Supabase client in React:
1import { createClient } from '@supabase/supabase-js'2import { useMemo } from 'react'34function App() {5 const supabase = useMemo(6 () =>7 createClient(SUPABASE_URL, SUPABASE_KEY, {8 realtime: {9 heartbeatIntervalMs: 15000, // Send heartbeat every 15 seconds (default: 25000)10 },11 }),12 []13 )1415 return <YourApp supabase={supabase} />16}注意:增加间隔可以减少网络流量,但会延迟连接失败的检测。减少间隔可以提高检测速度,但会增加开销。
🌐 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:
1import { createClient } from '@supabase/supabase-js'2import { useMemo } from 'react'34function App() {5 const supabase = useMemo(6 () =>7 createClient(SUPABASE_URL, SUPABASE_KEY, {8 realtime: {9 worker: true,10 workerUrl: '/worker.js', // Optional: Place in public folder11 },12 }),13 []14 )1516 return <YourApp supabase={supabase} />17}如果工人无法启动,请检查:
🌐 If the worker fails to start, check:
- 浏览器支持 Web Worker
- Worker 脚本在公共文件夹中,可以访问
- CORS 头允许加载工作脚本
最佳实践 #
🌐 Best practices
向用户显示连接状态 #
🌐 Show connection status to users
用指示器显示连接状态:
🌐 Display connection status with an indicator:
1import { createClient } from '@supabase/supabase-js'2import { useEffect, useState } from 'react'34function ConnectionIndicator() {5 const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)6 const [status, setStatus] = useState('healthy')78 useEffect(() => {9 supabase.realtime.onHeartbeat((heartbeatStatus) => {10 if (heartbeatStatus === 'ok') {11 setStatus('healthy')12 } else if (heartbeatStatus === 'timeout') {13 setStatus('poor')14 } else if (heartbeatStatus === 'disconnected') {15 setStatus('disconnected')16 }17 })18 }, [supabase])1920 return (21 <div>22 <span>Connection: {status}</span>23 </div>24 )25}当 React Native 应用回到前台时重新连接 #
🌐 Reconnect when React Native app comes to foreground
确保用户打开你的应用时连接是活跃的:
🌐 Ensure connection is active when user opens your app:
1import { createClient } from '@supabase/supabase-js'2import { useEffect } from 'react'3import { AppState } from 'react-native'45function App() {6 const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)78 useEffect(() => {9 const subscription = AppState.addEventListener('change', (nextAppState) => {10 if (nextAppState === 'active') {11 if (!supabase.realtime.isConnected()) {12 supabase.realtime.connect()13 }14 }15 })1617 return () => {18 subscription.remove()19 }20 }, [supabase])2122 return <YourApp />23}监控你整个应用中的连接 #
🌐 Monitor connection in your entire app
在你创建 Supabase 客户端时设置监控:
🌐 Set up monitoring when you create your Supabase client:
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {4 realtime: {5 heartbeatCallback: (status) => {6 if (status === 'ok') {7 console.log('Connection healthy')8 } else if (status === 'timeout') {9 console.warn('Connection slow')10 } else if (status === 'disconnected') {11 console.error('Disconnected')12 }13 },14 },15})1617export default supabase