HoRain云--Serverless架构实现WebSocket长连接
本文介绍了在Serverless架构中实现WebSocket长连接的技术方案。核心采用"连接与业务逻辑分离"的模式:由APIGateway管理连接生命周期,Lambda处理业务逻辑,DynamoDB存储连接状态。详细说明了AWS三大组件(APIGateway、Lambda、DynamoDB)的协同工作流程,包括连接建立、消息处理和断开连接的完整生命周期管理。该方案具有自动扩缩容

🎬 HoRain 云小助手:个人主页
⛺️生活的理想,就是为了理想的生活!
⛳️ 推荐
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
目录
优势(为什么要在 Serverless 中使用 WebSocket?)

很好,这是一个非常贴合现代架构的问题。在 Serverless 架构中实现 WebSocket 长连接,其核心理念是 「连接与业务逻辑分离」。
传统架构中,一个服务器实例会同时维护连接和处理业务。而 Serverless 模式下,连接管理由专门的托管服务(如 API Gateway)负责,业务逻辑则由无状态、瞬态的 FaaS(如 Lambda)处理。
核心架构模式:连接与逻辑分离
这个模式主要包含三个关键部分,其交互流程如下图所示:
flowchart TD
Client[WebSocket 客户端] <--> |持久连接| AG[API Gateway<br>管理连接生命周期]
AG --> |路由事件| Lambda[Lambda 函数<br>处理业务逻辑]
Lambda --> |“postToConnection”| AG
DynamoDB[(DynamoDB<br>存储ConnectionId)] <-.-> Lambda
-
托管连接层:专门负责维持与客户端的 WebSocket 长连接。
-
无状态计算层:负责处理所有业务逻辑。
-
持久化存储层:负责存储连接状态(ConnectionId)与应用程序状态。
下面我们以 AWS 的云服务为例,详细拆解其实现方案,其他云厂商(如 Azure、Google Cloud)原理类似。
详细实现步骤(以 AWS 为例)
1. 组件选择
-
WebSocket 连接管理:Amazon API Gateway。它原生支持 WebSocket 协议,负责连接的建立、维持和断开,并将消息事件路由到后端。
-
业务逻辑计算:AWS Lambda。处理来自 API Gateway 的消息、业务逻辑,并负责向客户端发送消息。
-
连接状态存储:Amazon DynamoDB。一个快速、全托管的 NoSQL 数据库,用于存储每个连接的
connectionId及其上下文信息(如用户ID、房间号等)。
2. 工作流程与生命周期
API Gateway 将 WebSocket 的生命周期定义为几个特定的路由:
-
$connect: 客户端建立连接时触发。 -
$disconnect: 客户端断开连接时触发。 -
$default: 当消息不匹配任何自定义路由时触发。 -
自定义路由(如sendmessage,joinroom): 根据消息内容路由到特定的处理逻辑。
完整流程如下:
-
连接建立
-
客户端连接到 WebSocket URL(
wss://your-api-id.execute-api.region.amazonaws.com/production)。 -
API Gateway 触发
$connect 路由,并调用绑定的 Lambda 函数。 -
Lambda 函数的工作: 将关键的连接信息(主要是 API Gateway 提供的唯一
connectionId)保存到 DynamoDB 表中。也可以在此进行身份验证(例如,验证连接 URL 中的查询字符串或 Header)。
-
-
消息处理
-
客户端发送一条 JSON 消息:
{ "action": "sendmessage", "data": "Hello" }。 -
API Gateway 根据消息中的
action字段进行路由。例如,action: sendmessage会路由到名为sendmessage的路由。 -
sendmessage路由触发对应的 Lambda 函数。 -
Lambda 函数的工作:
-
从 DynamoDB 中查询相关连接的
connectionId(例如,获取同一个聊天室的所有用户的connectionId)。 -
执行业务逻辑(如验证消息、保存到数据库等)。
-
通过 API Gateway 管理 API 向一个或多个客户端发送消息。
-
-
-
向客户端发送消息(服务器推送)
这是最关键的一步。Lambda 函数本身不保持连接,它如何发送消息?
-
Lambda 函数使用 AWS SDK 调用
ApiGatewayManagementApi.postToConnection()方法。 -
该方法需要两个参数:
-
ConnectionId: 目标客户端的连接 ID。 -
Data: 要发送的消息内容。
-
-
API Gateway 服务接收到这个请求后,会找到对应的持久连接,将消息推送给客户端。
-
-
连接断开
-
客户端断开连接。
-
API Gateway 触发
$disconnect 路由,调用绑定的 Lambda 函数。 -
Lambda 函数的工作: 从 DynamoDB 中删除该连接的
connectionId,并进行清理操作(如通知其他用户该用户已离线)。
-
优势与挑战
优势(为什么要在 Serverless 中使用 WebSocket?)
-
真正的弹性伸缩: API Gateway 和 Lambda 可以自动处理从零到数百万的并发连接,无需预置或管理服务器。你无需担心「我的服务器能撑住多少连接」。
-
成本效益: 传统架构需要始终运行服务器来维持连接,即使没有消息传递也在产生费用。Serverless WebSocket 的计费通常基于「连接分钟数」和「消息数量」,在空闲时段成本极低。
-
无运维负担: 无需打补丁、更新操作系统或管理集群。云服务商负责底层基础设施的可用性和可靠性。
挑战与注意事项
-
状态管理: ConnectionId 是核心。你必须将其持久化到外部存储(如 DynamoDB)。存储设计至关重要(例如,如何根据房间号快速查询所有 ConnectionId)。
-
冷启动延迟: 当 Lambda 函数冷启动时,消息处理可能会产生几百毫秒的延迟。对于实时性要求极高的场景(如快节奏游戏),这可能是个问题。可以通过预置并发等方式缓解。
-
API Gateway 限制: 注意服务本身的限制,例如每个连接的最长存活时间(目前是2小时),以及消息大小限制(32KB)。需要实现连接定期重连逻辑。
-
错误处理: 调用
postToConnection时,如果connectionId已失效(客户端已断开但数据库未及时清理),会返回 410 状态码。Lambda 函数需要捕获这个错误并从数据库中删除无效的connectionId。
简单代码示例(发送消息的 Lambda 函数)
// 发送消息的 Lambda 函数(Node.js)
const AWS = require('aws-sdk');
// 注意:endpoint 需要是你的 WebSocket API 的地址
const apiGatewayManagementApi = new AWS.ApiGatewayManagementApi({
endpoint: 'https://abc123.execute-api.us-east-1.amazonaws.com/production'
});
const dynamodb = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event) => {
const connectionId = event.requestContext.connectionId;
const body = JSON.parse(event.body);
const message = body.data;
// 1. 从 DynamoDB 获取需要接收此消息的所有 connectionId(例如,同房间用户)
const roomUsers = await dynamodb.query({
TableName: 'ConnectionsTable',
IndexName: 'RoomIndex', // 假设有全局二级索引按房间号查询
KeyConditionExpression: 'roomId = :roomId',
ExpressionAttributeValues: { ':roomId': body.roomId }
}).promise();
// 2. 构建要发送的消息
const postData = JSON.stringify({
type: 'message',
user: connectionId,
data: message,
timestamp: Date.now()
});
// 3. 向所有目标 connectionId 发送消息
const postCalls = roomUsers.Items.map(async (item) => {
try {
await apiGatewayManagementApi.postToConnection({
ConnectionId: item.connectionId,
Data: postData
}).promise();
} catch (err) {
if (err.statusCode === 410) { // 连接已断开
console.log(`Found stale connection, deleting ${item.connectionId}`);
await dynamodb.delete({
TableName: 'ConnectionsTable',
Key: { connectionId: item.connectionId }
}).promise();
} else {
throw err;
}
}
});
// 等待所有发送请求完成
await Promise.all(postCalls);
return { statusCode: 200 };
};
总结
在 Serverless 架构中实现 WebSocket 的核心是:
-
角色分离: 让 API Gateway 做它擅长的连接管理,让 Lambda 做它擅长的业务逻辑处理。
-
ConnectionId 是生命线: 通过 DynamoDB 等持久化存储来关联连接与用户/房间的关系。
-
使用管理 API 进行推送: Lambda 函数通过调用云服务商提供的特定 API(如
postToConnection)来实现服务器推送。
这种架构完美体现了 Serverless 的优势,让你能以极低的运维成本和良好的可伸缩性,构建出功能强大的实时应用。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙
更多推荐



所有评论(0)