基于TypeScript的SSE客户端封装库实现,包含了心跳检测、自动重连、错误处理和事件管理等功能:
type SSEEventHandler = (event: MessageEvent) => void;
type SSEErrorHandler = (error: ErrorEvent) => void;
interface SSEOptions {
url: string;
headers?: Record<string, string>;
queryParams?: Record<string, string>;
heartbeatInterval?: number; // 心跳间隔(ms),默认15000ms
timeoutThreshold?: number; // 超时阈值(ms),默认30000ms
retryInterval?: number; // 初始重连间隔(ms),默认2000ms
maxRetryInterval?: number; // 最大重连间隔(ms),默认30000ms
backoffFactor?: number; // 退避因子,默认2
maxRetries?: number; // 最大重试次数,默认Infinity
}
export class EventSourceClient {
private eventSource?: EventSource;
private options: SSEOptions;
private eventHandlers: Map<string, SSEEventHandler[]> = new Map();
private errorHandlers: SSEErrorHandler[] = [];
private retryCount = 0;
private retryTimer?: NodeJS.Timeout;
private heartbeatTimer?: NodeJS.Timeout;
private lastHeartbeat: number = Date.now();
private isConnected: boolean = false;
constructor(options: SSEOptions) {
this.options = {
heartbeatInterval: 15000,
timeoutThreshold: 30000,
retryInterval: 2000,
maxRetryInterval: 30000,
backoffFactor: 2,
maxRetries: Infinity,
...options
};
}
// 连接到SSE服务器
connect(): void {
if (this.eventSource) {
this.close();
}
const url = this.buildUrl();
console.log(`Connecting to SSE: ${url}`);
try {
this.eventSource = new EventSource(url);
// 监听原生事件
this.eventSource.onopen = () => this.handleOpen();
this.eventSource.onmessage = (event) => this.handleMessage(event);
this.eventSource.onerror = (error) => this.handleError(error);
// 重置重试计数
this.retryCount = 0;
} catch (error) {
console.error('Failed to create EventSource:', error);
this.scheduleReconnect();
}
}
// 关闭连接
close(): void {
this.clearTimers();
if (this.eventSource) {
this.eventSource.close();
this.eventSource = undefined;
}
this.isConnected = false;
console.log('SSE connection closed');
}
// 添加事件监听器
on(eventName: string, handler: SSEEventHandler): void {
if (!this.eventHandlers.has(eventName)) {
this.eventHandlers.set(eventName, []);
}
this.eventHandlers.get(eventName)?.push(handler);
}
// 移除事件监听器
off(eventName: string, handler?: SSEEventHandler): void {
if (!this.eventHandlers.has(eventName)) return;
if (handler) {
const handlers = this.eventHandlers.get(eventName) || [];
this.eventHandlers.set(
eventName,
handlers.filter(h => h !== handler)
);
} else {
this.eventHandlers.delete(eventName);
}
}
// 添加错误监听器
onError(handler: SSEErrorHandler): void {
this.errorHandlers.push(handler);
}
// 移除错误监听器
offError(handler: SSEErrorHandler): void {
this.errorHandlers = this.errorHandlers.filter(h => h !== handler);
}
// 获取连接状态
get connectionStatus(): {
isConnected: boolean;
retryCount: number;
lastHeartbeat: number;
} {
return {
isConnected: this.isConnected,
retryCount: this.retryCount,
lastHeartbeat: this.lastHeartbeat
};
}
// 构建请求URL(包含查询参数)
private buildUrl(): string {
const { url, queryParams } = this.options;
if (!queryParams || Object.keys(queryParams).length === 0) {
return url;
}
const params = new URLSearchParams();
Object.entries(queryParams).forEach(([key, value]) => {
params.append(key, value);
});
return `${url}?${params.toString()}`;
}
// 处理连接建立
private handleOpen(): void {
this.isConnected = true;
this.lastHeartbeat = Date.now();
this.startHeartbeatCheck();
console.log('SSE connection established');
this.emit('open', { type: 'open' } as MessageEvent);
}
// 处理接收到的消息
private handleMessage(event: MessageEvent): void {
this.lastHeartbeat = Date.now();
// 处理心跳消息(如果服务端使用特定格式)
if (event.data.trim() === ': heartbeat') {
console.debug('Heartbeat received');
return;
}
// 处理普通消息
const eventName = event.type === 'message' ? 'message' : event.type;
this.emit(eventName, event);
}
// 处理错误
private handleError(error: ErrorEvent): void {
console.error('SSE error:', error);
this.isConnected = false;
// 触发错误事件
this.errorHandlers.forEach(handler => handler(error));
// 关闭当前连接并计划重连
this.close();
this.scheduleReconnect();
}
// 触发事件
private emit(eventName: string, event: MessageEvent): void {
const handlers = this.eventHandlers.get(eventName) || [];
handlers.forEach(handler => handler(event));
}
// 启动心跳检测
private startHeartbeatCheck(): void {
this.clearTimers();
this.heartbeatTimer = setInterval(() => {
const now = Date.now();
const elapsed = now - this.lastHeartbeat;
if (elapsed > this.options.timeoutThreshold!) {
console.warn(`Heartbeat timeout: ${elapsed}ms`);
this.handleError({
type: 'error',
message: 'Heartbeat timeout',
error: new Error('Heartbeat timeout'),
currentTarget: null,
target: null
} as ErrorEvent);
}
}, 5000); // 每5秒检查一次
}
// 安排重连(带指数退避)
private scheduleReconnect(): void {
if (this.retryCount >= this.options.maxRetries!) {
console.error('Max retries exceeded. Stopping reconnection attempts.');
this.emit('max-retries-exceeded', { type: 'max-retries-exceeded' } as MessageEvent);
return;
}
// 计算退避时间:base * factor^retryCount
const delay = Math.min(
this.options.retryInterval! * Math.pow(this.options.backoffFactor!, this.retryCount),
this.options.maxRetryInterval!
);
console.log(`Scheduling reconnection attempt ${this.retryCount + 1} in ${delay}ms`);
this.retryTimer = setTimeout(() => {
this.retryCount++;
this.connect();
}, delay);
}
// 清除所有定时器
private clearTimers(): void {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = undefined;
}
if (this.retryTimer) {
clearTimeout(this.retryTimer);
this.retryTimer = undefined;
}
}
}
使用示例
// 创建SSE客户端实例
const sseClient = new EventSourceClient({
url: 'https://api.example.com/stream',
queryParams: {
token: 'your-auth-token'
},
heartbeatInterval: 15000,
timeoutThreshold: 30000
});
// 监听消息事件
sseClient.on('message', (event) => {
console.log('Received message:', event.data);
});
// 监听自定义事件
sseClient.on('notification', (event) => {
console.log('New notification:', JSON.parse(event.data));
});
// 监听错误
sseClient.onError((error) => {
console.error('SSE error:', error);
});
// 监听连接状态
sseClient.on('open', () => {
console.log('SSE connection opened');
});
// 启动连接
sseClient.connect();
// 一段时间后关闭连接
setTimeout(() => {
sseClient.close();
}, 3600000); // 1小时后关闭
主要功能特点
-
自动重连机制
- 指数退避算法,避免频繁重试
- 可配置最大重试次数和间隔时间
- 自动恢复连接状态
-
心跳检测
- 定时检查连接活性
- 超时自动触发重连
- 与服务端心跳消息配合
-
事件管理
- 支持自定义事件类型
- 多监听器管理
- 事件注册/注销接口
-
错误处理
- 错误事件统一处理
- 连接状态管理
- 错误重试机制
-
配置灵活性
- 可配置心跳间隔和超时阈值
- 支持自定义请求头和查询参数
- 可定制重连策略
这个封装库可以直接在浏览器环境中使用,也可以在Node.js环境中配合eventsource包使用。它提供了健壮的SSE连接管理,能够处理网络波动、服务端重启等各种异常情况,确保消息的可靠接收。

1850

被折叠的 条评论
为什么被折叠?



