🔗 Webhook 模块
Webhook 模块为聊天系统提供异步通知机制,允许服务器主动向客户端推送重要事件和状态更新。这对于长时间运行的任务、断开连接场景以及实时通知至关重要。
📋 概述
Webhook 模块的核心功能:
- 异步通知: 服务器主动推送事件到客户端
- 长任务监控: 跟踪长时间运行任务的状态变化
- 断线重连: 处理连接中断后的状态同步
- 安全验证: 确保 Webhook 请求的真实性和安全性
🚀 快速开始
基础配置
ts
import { scp } from '@your-org/scp-sdk';
// 配置 Webhook 接收端点
const webhookConfig = {
endpoint: 'https://your-app.com/api/webhooks/chat',
secret: 'your-webhook-secret-key',
events: ['task_completed', 'message_received', 'session_updated'],
retryPolicy: {
maxRetries: 3,
retryDelay: 1000
}
};
// 注册 Webhook
await scp.chat.webhook.register(webhookConfig);接收 Webhook 事件
ts
// Express.js 示例
app.post('/api/webhooks/chat', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body;
// 验证签名
if (!verifyWebhookSignature(payload, signature, webhookConfig.secret)) {
return res.status(401).send('Invalid signature');
}
// 处理事件
const event = JSON.parse(payload);
handleWebhookEvent(event);
res.status(200).send('OK');
});📡 使用场景
1. 长时间运行任务通知
ts
// 场景:AI 模型训练任务
const trainingTask = {
taskId: 'training_001',
type: 'model_training',
estimatedDuration: 3600000, // 1小时
webhookEvents: [
'task_started',
'task_progress',
'task_completed',
'task_failed'
]
};
// 启动任务时注册 Webhook
await scp.chat.startTask(trainingTask);
// Webhook 事件处理
function handleWebhookEvent(event) {
switch (event.type) {
case 'task_started':
updateUI({
status: 'running',
message: '模型训练已开始',
startTime: event.timestamp
});
break;
case 'task_progress':
updateUI({
progress: event.data.progress,
message: `训练进度: ${event.data.progress}%`,
eta: event.data.estimatedCompletion
});
break;
case 'task_completed':
updateUI({
status: 'completed',
message: '模型训练完成',
result: event.data.result,
duration: event.data.duration
});
break;
case 'task_failed':
updateUI({
status: 'failed',
message: '模型训练失败',
error: event.data.error
});
break;
}
}2. 断线重连场景
ts
// 处理连接中断后的状态同步
class ChatSessionManager {
private sessionId: string;
private lastSyncTime: number;
async handleReconnection() {
// 请求错过的事件
const missedEvents = await scp.chat.webhook.getMissedEvents({
sessionId: this.sessionId,
since: this.lastSyncTime
});
// 处理错过的事件
for (const event of missedEvents) {
this.handleWebhookEvent(event);
}
// 更新同步时间
this.lastSyncTime = Date.now();
}
private handleWebhookEvent(event: WebhookEvent) {
switch (event.type) {
case 'message_received':
this.addMessageToChat(event.data.message);
break;
case 'session_updated':
this.updateSessionState(event.data.session);
break;
case 'user_joined':
this.addUserToSession(event.data.user);
break;
case 'user_left':
this.removeUserFromSession(event.data.userId);
break;
}
}
}3. 实时协作通知
ts
// 多用户协作场景
const collaborationEvents = {
'document_edited': (event) => {
showNotification({
type: 'info',
message: `${event.data.user.name} 正在编辑文档`,
document: event.data.document
});
},
'user_typing': (event) => {
showTypingIndicator({
userId: event.data.userId,
userName: event.data.userName,
sessionId: event.data.sessionId
});
},
'message_reaction': (event) => {
updateMessageReactions({
messageId: event.data.messageId,
reaction: event.data.reaction,
user: event.data.user
});
}
};🔒 安全机制
1. 签名验证
ts
import crypto from 'crypto';
// 生成 Webhook 签名
function generateWebhookSignature(payload: string, secret: string): string {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
return `sha256=${hmac.digest('hex')}`;
}
// 验证 Webhook 签名
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = generateWebhookSignature(payload, secret);
// 使用时间安全的比较方法
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// 中间件示例
function webhookVerificationMiddleware(secret: string) {
return (req, res, next) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body;
if (!signature) {
return res.status(401).json({ error: 'Missing signature' });
}
if (!verifyWebhookSignature(payload, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
next();
};
}2. IP 白名单
ts
// IP 白名单配置
const allowedIPs = [
'192.168.1.0/24',
'10.0.0.0/8',
'172.16.0.0/12'
];
function isIPAllowed(clientIP: string): boolean {
return allowedIPs.some(range => {
if (range.includes('/')) {
// CIDR 范围检查
return isIPInCIDR(
clientIP, range);
} else {
// 精确 IP 匹配
return clientIP === range;
}
});
}
// IP 白名单中间件
function ipWhitelistMiddleware(req, res, next) {
const clientIP = req.ip || req.connection.remoteAddress;
if (!isIPAllowed(clientIP)) {
return res.status(403).json({
error: 'IP not allowed',
ip: clientIP
});
}
next();
}3. 时间戳验证
ts
// 防重放攻击
function verifyTimestamp(timestamp: number, toleranceMs: number = 300000): boolean {
const now = Date.now();
const diff = Math.abs(now - timestamp);
return diff <= toleranceMs; // 5分钟容差
}
// 完整的安全验证中间件
function secureWebhookMiddleware(config: {
secret: string;
allowedIPs?: string[];
timestampTolerance?: number;
}) {
return (req, res, next) => {
try {
// 1. IP 白名单检查
if (config.allowedIPs) {
const clientIP = req.ip || req.connection.remoteAddress;
if (!isIPAllowed(clientIP)) {
return res.status(403).json({ error: 'IP not allowed' });
}
}
// 2. 签名验证
const signature = req.headers['x-webhook-signature'];
if (!signature || !verifyWebhookSignature(req.body, signature, config.secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// 3. 时间戳验证
const timestamp = parseInt(req.headers['x-webhook-timestamp'] as string);
if (!verifyTimestamp(timestamp, config.timestampTolerance)) {
return res.status(401).json({ error: 'Invalid timestamp' });
}
next();
} catch (error) {
res.status(400).json({ error: 'Webhook validation failed' });
}
};
}📊 事件类型
1. 任务相关事件
ts
interface TaskEvent {
type: 'task_started' | 'task_progress' | 'task_completed' | 'task_failed';
taskId: string;
timestamp: number;
data: {
progress?: number;
result?: any;
error?: string;
estimatedCompletion?: number;
duration?: number;
};
}
// 事件处理器
const taskEventHandlers = {
task_started: (event: TaskEvent) => {
console.log(`任务 ${event.taskId} 已开始`);
updateTaskStatus(event.taskId, 'running');
},
task_progress: (event: TaskEvent) => {
console.log(`任务 ${event.taskId} 进度: ${event.data.progress}%`);
updateTaskProgress(event.taskId, event.data.progress);
},
task_completed: (event: TaskEvent) => {
console.log(`任务 ${event.taskId} 已完成`);
updateTaskStatus(event.taskId, 'completed');
showTaskResult(event.taskId, event.data.result);
},
task_failed: (event: TaskEvent) => {
console.error(`任务 ${event.taskId} 失败: ${event.data.error}`);
updateTaskStatus(event.taskId, 'failed');
showTaskError(event.taskId, event.data.error);
}
};2. 聊天相关事件
ts
interface ChatEvent {
type: 'message_received' | 'message_updated' | 'message_deleted' | 'typing_indicator';
sessionId: string;
userId?: string;
timestamp: number;
data: {
message?: ChatMessage;
messageId?: string;
isTyping?: boolean;
};
}
// 聊天事件处理
const chatEventHandlers = {
message_received: (event: ChatEvent) => {
addMessageToChat(event.sessionId, event.data.message);
playNotificationSound();
},
message_updated: (event: ChatEvent) => {
updateMessage(event.data.messageId, event.data.message);
},
message_deleted: (event: ChatEvent) => {
removeMessage(event.data.messageId);
},
typing_indicator: (event: ChatEvent) => {
showTypingIndicator(event.sessionId, event.userId, event.data.isTyping);
}
};3. 系统相关事件
ts
interface SystemEvent {
type: 'system_maintenance' | 'service_update' | 'rate_limit_warning';
timestamp: number;
data: {
message: string;
severity: 'info' | 'warning' | 'error';
maintenanceWindow?: {
start: number;
end: number;
};
rateLimit?: {
current: number;
limit: number;
resetTime: number;
};
};
}🔄 重试机制
1. 指数退避重试
ts
class WebhookRetryManager {
private retryQueue: Map<string, RetryItem> = new Map();
async sendWebhook(
url: string,
payload: any,
options: {
maxRetries?: number;
initialDelay?: number;
maxDelay?: number;
backoffMultiplier?: number;
} = {}
) {
const {
maxRetries = 3,
initialDelay = 1000,
maxDelay = 30000,
backoffMultiplier = 2
} = options;
let attempt = 0;
let delay = initialDelay;
while (attempt <= maxRetries) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': generateWebhookSignature(JSON.stringify(payload), this.secret),
'X-Webhook-Timestamp': Date.now().toString()
},
body: JSON.stringify(payload)
});
if (response.ok) {
console.log(`Webhook 发送成功: ${url}`);
return response;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} catch (error) {
attempt++;
if (attempt > maxRetries) {
console.error(`Webhook 发送失败,已达到最大重试次数: ${error.message}`);
throw error;
}
console.warn(`Webhook 发送失败,${delay}ms 后重试 (${attempt}/${maxRetries}): ${error.message}`);
await this.sleep(delay);
delay = Math.min(delay * backoffMultiplier, maxDelay);
}
}
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}2. 死信队列处理
ts
class DeadLetterQueue {
private failedWebhooks: FailedWebhook[] = [];
addFailedWebhook(webhook: FailedWeb
hook) {
this.failedWebhooks.push({
...webhook,
failedAt: Date.now(),
retryCount: webhook.retryCount || 0
});
}
async processDeadLetterQueue() {
const now = Date.now();
const retryableWebhooks = this.failedWebhooks.filter(webhook => {
// 24小时后重试
return now - webhook.failedAt > 24 * 60 * 60 * 1000 && webhook.retryCount < 5;
});
for (const webhook of retryableWebhooks) {
try {
await this.retryWebhook(webhook);
// 成功后从死信队列移除
this.removeFromDeadLetterQueue(webhook.id);
} catch (error) {
// 更新重试次数
webhook.retryCount++;
if (webhook.retryCount >= 5) {
console.error(`Webhook ${webhook.id} 已达到最大重试次数,永久失败`);
this.removeFromDeadLetterQueue(webhook.id);
}
}
}
}
}🛠️ 实际应用示例
1. Next.js 应用集成
ts
// pages/api/webhooks/chat.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { verifyWebhookSignature } from '@/lib/webhook-utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
// 验证签名
const signature = req.headers['x-webhook-signature'] as string;
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.WEBHOOK_SECRET!
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// 处理事件
const event = req.body;
await handleWebhookEvent(event);
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
async function handleWebhookEvent(event: any) {
switch (event.type) {
case 'task_completed':
await notifyTaskCompletion(event);
break;
case 'message_received':
await broadcastNewMessage(event);
break;
default:
console.log(`未处理的事件类型: ${event.type}`);
}
}2. Vue.js 应用集成
vue
<template>
<div class="webhook-status">
<div v-if="webhookConnected" class="status-connected">
🟢 Webhook 已连接
</div>
<div v-else class="status-disconnected">
🔴 Webhook 未连接
</div>
<div class="recent-events">
<h3>最近事件</h3>
<div v-for="event in recentEvents" :key="event.id" class="event-item">
<span class="event-type">{{ event.type }}</span>
<span class="event-time">{{ formatTime(event.timestamp) }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { scp } from '@your-org/scp-sdk';
const webhookConnected = ref(false);
const recentEvents = ref([]);
let eventSource: EventSource | null = null;
onMounted(async () => {
// 注册 Webhook
await registerWebhook();
// 建立 Server-Sent Events 连接用于实时更新
eventSource = new EventSource('/api/webhook-events');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
handleRealtimeEvent(data);
};
});
onUnmounted(() => {
if (eventSource) {
eventSource.close();
}
});
async function registerWebhook() {
try {
await scp.chat.webhook.register({
endpoint: `${window.location.origin}/api/webhooks/chat`,
secret: process.env.VUE_APP_WEBHOOK_SECRET,
events: ['task_completed', 'message_received', 'session_updated']
});
webhookConnected.value = true;
} catch (error) {
console.error('Webhook 注册失败:', error);
webhookConnected.value = false;
}
}
function handleRealtimeEvent(event: any) {
recentEvents.value.unshift(event);
if (recentEvents.value.length > 10) {
recentEvents.value.pop();
}
}
function formatTime(timestamp: number) {
return new Date(timestamp).toLocaleTimeString();
}
</script>3. React 应用集成
tsx
import React, { useState, useEffect, useCallback } from 'react';
import { scp } from '@your-org/scp-sdk';
interface WebhookEvent {
id: string;
type: string;
timestamp: number;
data: any;
}
export const WebhookManager: React.FC = () => {
const [isConnected, setIsConnected] = useState(false);
const [events, setEvents] = useState<WebhookEvent[]>([]);
const [error, setError] = useState<string | null>(null);
const handleWebhookEvent = useCallback((event: WebhookEvent) => {
setEvents(prev => [event, ...prev.slice(0, 9)]);
// 根据事件类型执行相应操作
switch (event.type) {
case 'task_completed':
showNotification('任务完成', event.data.message);
break;
case 'message_received':
updateChatUI(event.data.message);
break;
case 'system_maintenance':
showMaintenanceAlert(event.data);
break;
}
}, []);
useEffect(() => {
const initWebhook = async () => {
try {
await scp.chat.webhook.register({
endpoint: `${window.location.origin}/api/webhooks/chat`,
secret: process.env.REACT_APP_WEBHOOK_SECRET!,
events: ['*'], // 监听所有事件
onEvent: handleWebhookEvent
});
setIsConnected(true);
setError(null);
} catch (err) {
setError(err instanceof Error ? err.message : '未知错误');
setIsConnected(false);
}
};
initWebhook();
return () => {
scp.chat.webhook.unregister();
};
}, [handleWebhookEvent]);
return (
<div className="webhook-manager">
<div className={`status ${isConnected ? 'connected' : 'disconnected'}`}>
{isConnected ? '🟢 已连接' : '🔴 未连接'}
{error && <span className="error">: {error}</span>}
</div>
<div className="events-list">
<h3>最近事件</h3>
{events.map(event => (
<div key={event.id} className="event-item">
<span className="event-type">{event.type}</span>
<span className="event-time">
{new Date(event.timestamp).toLocaleTimeString()}
</span>
<span className="event-data">
{JSON.stringify(event.data, null, 2)}
</span>
</div>
))}
</div>
</div>
);
};🔒 安全最佳实践
1. 密钥管理
ts
// 环境变量配置
const webhookConfig = {
// ✅ 好的做法:使用环境变量
secret: process.env.WEBHOOK_SECRET,
// ❌ 不好的做法:硬编码密钥
// secret: 'my-secret-key'
};
// 密钥轮换
class WebhookSecretManager {
private currentSecret: string;
private previousSecret: string | null = null;
constructor(secret: string) {
this.currentSecret = secret;
}
// 轮换密钥
rotateSecret(newSecret: string) {
this.previousSecret = this.currentSecret;
this.currentSecret = newSecret;
}
// 验证签名(支持新旧密钥)
verifySignature(payload: string, signature: string): boolean {
// 先尝试当前密钥
if (verifyWebhookSignature(payload, signature, this.currentSecret)) {
return true;
}
// 如果失败且存在旧密钥,尝试旧密钥
if (this.previousSecret &&
verifyWebhookSignature(payload, signature, this.previousSecret)) {
return true;
}
return false;
}
}2. 速率限制
ts
class WebhookRateLimiter {
private requests: Map<string, number[]> = new Map();
isAllowed(clientIP: string, maxRequests: number = 100, windowMs: number = 60000): boolean {
const now = Date.now();
const windowStart = now - windowMs;
// 获取客户端请求历史
let clientRequests = this.requests.get(clientIP) || [];
// 清理过期请求
clientRequests = clientRequests.filter(timestamp => timestamp > windowStart);
// 检查是否超过限制
if (clientRequests.length >= maxRequests) {
return false;
}
// 记录新请求
clientRequests.push(now);
this.requests.set(clientIP, clientRequests);
return true;
}
// 清理过期数据
cleanup() {
const now = Date.now();
const oneHourAgo = now - 60 * 60 * 1000;
for (const [clientIP, requests] of this.requests.entries()) {
const validRequests = requests.filter(timestamp => timestamp > oneHourAgo);
if (validRequests.length === 0) {
this.requests.delete(clientIP);
} else {
this.requests.set(clientIP, validRequests);
}
}
}
}
// 使用中间件
const rateLimiter = new WebhookRateLimiter();
function rateLimitMiddleware(req, res, next) {
const clientIP = req.ip || req.connection.remoteAddress;
if (!rateLimiter.isAllowed(clientIP)) {
return res.status(429).json({
error: 'Too many requests',
retryAfter: 60
});
}
next();
}3. 请求日志和监控
ts
class WebhookLogger {
private logs: WebhookLog[] = [];
logRequest(req: any, res: any, processingTime: number) {
const log: WebhookLog = {
timestamp: Date.now(),
clientIP: req.ip || req.connection.remoteAddress,
userAgent: req.headers['user-agent'],
method: req.method,
url: req.url,
statusCode: res.statusCode,
processingTime,
payloadSize: req.headers['content-length'] || 0,
signature: req.headers['x-webhook-signature'] ? 'present' : 'missing'
};
this.logs.push(log);
// 保持最近1000条日志
if (this.logs.length > 1000) {
this.logs.shift();
}
// 异常请求告警
if (res.statusCode >= 400) {
this.alertOnSuspiciousActivity(log);
}
}
private alertOnSuspiciousActivity(log: WebhookLog) {
// 检测可能的攻击模式
const recentFailures = this.logs.filter(l =>
l.clientIP === log.clientIP &&
l.statusCode >= 400 &&
Date.now() - l.timestamp < 60000 // 最近1分钟
);
if (recentFailures.length > 10) {
console.warn(`可能的攻击检测: IP ${log.clientIP} 在1分钟内失败请求 ${recentFailures.length} 次`);
// 这里可以集成告警系统
this.sendAlert({
type: 'suspicious_activity',
clientIP: log.clientIP,
failureCount: recentFailures.length,
timeWindow: '1分钟'
});
}
}
private sendAlert(alert: any) {
// 集成告警系统(如邮件、Slack、钉钉等)
console.log('🚨 安全告警:', alert);
}
}🐛 故障排除
常见问题
Q1: Webhook 请求失败,返回 401 错误
可能原因:
- 签名验证失败
- 时间戳过期
- 密钥配置错误
解决方案:
ts
// 调试签名验证
function debugSignatureVerification(payload: string, signature: string, secret: string) {
console.log('调试信息:');
console.log('Payload:', payload);
console.log('Received signature:', signature);
const expectedSignature = generateWebhookSignature(payload, secret);
console.log('Expected signature:', expectedSignature);
const isValid = signature === expectedSignature;
console.log('Signature valid:', isValid);
if (!isValid) {
console.log('可能的问题:');
console.log('1. 检查密钥是否正确');
console.log('2. 检查 payload 是否被修改');
console.log('3. 检查签名算法是否一致');
}
return isValid;
}Q2: Webhook 接收到重复事件
解决方案:
ts
class WebhookDeduplicator {
private processedEvents: Set<string> = new Set();
private eventTTL: number = 5 * 60 * 1000; // 5分钟
isDuplicate(eventId: string):
boolean {
return this.processedEvents.has(eventId);
}
markAsProcessed(eventId: string) {
this.processedEvents.add(eventId);
// 定期清理过期事件ID
setTimeout(() => {
this.processedEvents.delete(eventId);
}, this.eventTTL);
}
processEvent(event: any) {
if (this.isDuplicate(event.id)) {
console.log(`跳过重复事件: ${event.id}`);
return false;
}
this.markAsProcessed(event.id);
return true;
}
}Q3: Webhook 处理超时
解决方案:
ts
// 设置处理超时
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) => {
setTimeout(() => reject(new Error(`处理超时 (${timeoutMs}ms)`)), timeoutMs);
})
]);
}
// 异步处理 Webhook
async function handleWebhookAsync(event: any) {
try {
// 快速响应客户端
res.status(200).send('OK');
// 异步处理事件
await withTimeout(processWebhookEvent(event), 30000);
} catch (error) {
console.error('Webhook 处理失败:', error);
// 记录到错误日志或重试队列
}
}Q4: 网络连接问题
解决方案:
ts
// 健康检查端点
app.get('/api/webhooks/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: Date.now(),
version: process.env.APP_VERSION
});
});
// 连接测试工具
class WebhookConnectivityTester {
async testConnection(webhookUrl: string): Promise<boolean> {
try {
const response = await fetch(`${webhookUrl}/health`, {
method: 'GET',
timeout: 5000
});
return response.ok;
} catch (error) {
console.error(`连接测试失败: ${error.message}`);
return false;
}
}
async testWebhookDelivery(webhookUrl: string, secret: string): Promise<boolean> {
const testPayload = {
type: 'test_event',
timestamp: Date.now(),
data: { message: 'This is a test webhook' }
};
try {
const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': generateWebhookSignature(JSON.stringify(testPayload), secret)
},
body: JSON.stringify(testPayload),
timeout: 10000
});
return response.ok;
} catch (error) {
console.error(`Webhook 投递测试失败: ${error.message}`);
return false;
}
}
}📊 监控和分析
性能监控
ts
class WebhookMetrics {
private metrics = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
averageProcessingTime: 0,
processingTimes: [] as number[]
};
recordRequest(processingTime: number, success: boolean) {
this.metrics.totalRequests++;
if (success) {
this.metrics.successfulRequests++;
} else {
this.metrics.failedRequests++;
}
// 记录处理时间
this.metrics.processingTimes.push(processingTime);
// 保持最近1000次请求的数据
if (this.metrics.processingTimes.length > 1000) {
this.metrics.processingTimes.shift();
}
// 计算平均处理时间
this.metrics.averageProcessingTime =
this.metrics.processingTimes.reduce((a, b) => a + b, 0) /
this.metrics.processingTimes.length;
}
getMetrics() {
const successRate = this.metrics.totalRequests > 0 ?
(this.metrics.successfulRequests / this.metrics.totalRequests) * 100 : 0;
return {
...this.metrics,
successRate,
p95ProcessingTime: this.calculatePercentile(95),
p99ProcessingTime: this.calculatePercentile(99)
};
}
private calculatePercentile(percentile: number): number {
if (this.metrics.processingTimes.length === 0) return 0;
const sorted = [...this.metrics.processingTimes].sort((a, b) => a - b);
const index = Math.ceil((percentile / 100) * sorted.length) - 1;
return sorted[index];
}
}
// 监控端点
app.get('/api/webhooks/metrics', (req, res) => {
const metrics = webhookMetrics.getMetrics();
res.json(metrics);
});📖 参考资源
相关协议和标准
- A2A Protocol: 异步通信和流处理
- Webhook 安全最佳实践: OWASP Webhook 安全指南
- HTTP 签名规范: RFC 9421
相关文档
🎯 总结
Webhook 模块为聊天系统提供了强大的异步通知能力,通过合理的设计和实现,可以:
- 提升用户体验: 实时推送重要事件和状态更新
- 增强系统可靠性: 处理长时间运行任务和断线重连场景
- 保证安全性: 通过签名验证、IP 白名单等机制确保安全
- 优化性能: 通过重试机制、速率限制等提升系统稳定性
关键要点:
- 始终验证 Webhook 请求的真实性
- 实现幂等性处理避免重复执行
- 设置合理的超时和重试机制
- 监控和日志记录用于故障排除
- 遵循安全最佳实践保护系统安全
通过遵循这些最佳实践,你可以构建出可靠、安全且高效的 Webhook 系统。