Go + React 实现 Server-Sent Events (SSE) 详细教程
第一部分:Go 后端实现
1. 创建项目目录结构
2. 初始化Go模块
3. 创建主程序文件 main.go
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
// 设置路由
http.HandleFunc("/events", sseHandler)
http.HandleFunc("/", homeHandler)
// 启动服务器
fmt.Println("Server running on :8080")
http.ListenAndServe(":8080", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, `
<!DOCTYPE html>
<html>
<head>
<title>SSE Demo</title>
</head>
<body>
<h1>Go SSE Server</h1>
<p>This is the backend server. Open React app to see SSE in action.</p>
</body>
</html>
`)
}
func sseHandler(w http.ResponseWriter, r *http.Request) {
// 设置SSE所需的响应头
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*") // 允许跨域
// 获取flusher接口
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
return
}
// 创建关闭通知通道
notify := r.Context().Done()
// 计数器
eventID := 1
// 发送事件循环
for {
select {
case <-notify:
// 客户端断开连接
fmt.Println("Client disconnected")
return
default:
// 构造消息
message := fmt.Sprintf("Server time: %s (Event ID: %d)",
time.Now().Format("2006-01-02 15:04:05"), eventID)
// 发送事件 (格式很重要!)
fmt.Fprintf(w, "id: %d\n", eventID)
fmt.Fprintf(w, "data: %s\n\n", message)
flusher.Flush()
eventID++
time.Sleep(1 * time.Second) // 每秒发送一次
}
}
}
4. 运行Go服务器
现在你的Go服务器已经在 http://localhost:8080
运行,SSE端点位于 http://localhost:8080/events
第二部分:React 前端实现
1. 创建React应用
2. 创建SSE组件
在 src
文件夹下创建 SSEComponent.js
:
import React, { useState, useEffect } from 'react';
function SSEComponent() {
const [messages, setMessages] = useState([]);
const [isConnected, setIsConnected] = useState(false);
const [eventSource, setEventSource] = useState(null);
const connectSSE = () => {
if (eventSource) return; // 避免重复连接
// 创建新的EventSource连接
const source = new EventSource('http://localhost:8080/events');
setEventSource(source);
setIsConnected(true);
// 处理消息事件
source.onmessage = (event) => {
setMessages(prev => [...prev, event.data]);
};
// 处理错误事件
source.onerror = (error) => {
console.error('SSE Error:', error);
disconnectSSE();
};
};
const disconnectSSE = () => {
if (eventSource) {
eventSource.close();
setEventSource(null);
setIsConnected(false);
}
};
// 组件卸载时断开连接
useEffect(() => {
return () => {
disconnectSSE();
};
}, []);
return (
<div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
<h2>SSE Client</h2>
<div style={{ marginBottom: '20px' }}>
<button
onClick={connectSSE}
disabled={isConnected}
style={{
padding: '8px 16px',
marginRight: '10px',
backgroundColor: isConnected ? '#ccc' : '#4CAF50',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: isConnected ? 'not-allowed' : 'pointer'
}}
>
{isConnected ? 'Connected' : 'Connect'}
</button>
<button
onClick={disconnectSSE}
disabled={!isConnected}
style={{
padding: '8px 16px',
backgroundColor: !isConnected ? '#ccc' : '#f44336',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: !isConnected ? 'not-allowed' : 'pointer'
}}
>
Disconnect
</button>
</div>
<div style={{
height: '300px',
overflowY: 'auto',
border: '1px solid #ddd',
padding: '10px',
backgroundColor: '#f9f9f9'
}}>
{messages.length === 0 ? (
<p>No messages yet. Click Connect to start receiving events.</p>
) : (
messages.map((msg, index) => (
<p key={index} style={{
padding: '5px',
borderBottom: '1px solid #eee',
margin: '5px 0'
}}>
{msg}
</p>
))
)}
</div>
</div>
);
}
export default SSEComponent;
3. 修改App.js
替换 src/App.js
内容:
import './App.css';
import SSEComponent from './SSEComponent';
function App() {
return (
<div className="App">
<header className="App-header">
<h1>React SSE Demo</h1>
<SSEComponent />
</header>
</div>
);
}
export default App;
4. 修改App.css
可以添加一些基本样式到 src/App.css
:
.App {
text-align: center;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
}
.App-header {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
5. 启动React应用
React应用将在 http://localhost:3000
自动打开
第三部分:测试与验证
1. 测试步骤
确保Go后端正在运行 (
http://localhost:8080
)React应用正在运行 (
http://localhost:3000
)在React页面点击"Connect"按钮
你应该每秒看到一条新消息
点击"Disconnect"停止接收消息
2. 验证跨域
由于前端(3000端口
)和后端(8080端口
)不同源,我们已经在Go后端添加了CORS头:
3. 错误处理
如果连接失败,检查控制台错误
确保两个服务器都在运行
检查浏览器控制台的网络请求
第四部分:扩展功能
1. 添加更多事件类型
修改Go后端的sseHandler
:
修改React组件的connectSSE
:
2. 添加重连逻辑
在React组件中添加:
第五部分:部署说明
1. 生产环境配置
修改React的
.env
文件:更新EventSource连接:
2. 构建React应用
3. 部署Go应用
总结
通过这个完整的教程,你已经学会了:
如何用Go创建SSE服务器
如何用React连接SSE流
如何处理连接和断开
如何显示实时消息
基本的错误处理和样式设计
这个实现包含了SSE的核心功能,你可以基于此扩展更复杂的功能,如:
用户认证
不同事件类型
更复杂的UI
生产环境部署配置
评论