NLB WebSocket 连接问题排查与解决方案
问题背景
需要通过Network Load Balancer (NLB) 支持WebSocket连接,但在配置过程中遇到连接失败问题。
环境信息
- 协议: WebSocket (OCPP 1.6)
- 后端服务端口: 9521
- NLB监听端口: 80
NLB 创建步骤(AWS 控制台)
1. 创建目标组
- 进入 EC2 控制台 → 左侧菜单 目标组 (Target Groups)
- 点击 创建目标组 (Create target group)
配置基本信息:
- 目标类型: 实例 (Instances)
- 目标组名称:
xxx-nlb-tg - 协议: TCP
- 端口: 9521 ⚠️ 注意:必须是后端服务实际监听的端口
- VPC: 选择
vpc - 协议版本: 默认
配置健康检查:
- 健康检查协议: TCP ⚠️ 重要:WebSocket服务器使用TCP检查
- 健康检查间隔: 30秒
- 健康阈值: 3
- 不健康阈值: 3
- 点击 下一步 (Next)
注册目标:
- 选择实例
- 端口: 9521
- 点击 包含为待处理项 (Include as pending below)
- 点击 创建目标组 (Create target group)
2. 创建 Network Load Balancer
- 进入 EC2 控制台 → 左侧菜单 负载均衡器 (Load Balancers)
- 点击 创建负载均衡器 (Create load balancer)
- 选择 Network Load Balancer → 点击 创建 (Create)
基本配置:
- 负载均衡器名称:
test-nlb - 方案: 面向互联网 (Internet-facing)
- IP地址类型: IPv4
- 负载均衡器名称:
网络映射:
- VPC: 选择
vpc-xxxxxx 可用区:
- ✅ us-east-1d → 子网
subnet-xxxx - ✅ us-east-1e → 子网
subnet-0xxxxxf18
- ✅ us-east-1d → 子网
- VPC: 选择
监听器和路由:
- 协议: TCP
- 端口:
80 - 默认操作: 转发至目标组 xxxx-tg`
- 点击 创建负载均衡器 (Create load balancer)
- 记录生成的 DNS名称:
xxx-4xxxxxeast-1.amazonaws.com
3. 启用跨可用区负载均衡 ⚠️ 重要
- 选择刚创建的 NLB
xxxx-nlb - 点击 属性 (Attributes) 标签
- 点击 编辑 (Edit)
- 找到 跨可用区负载均衡 (Cross-zone load balancing)
- 选择 开启 (On)
- 点击 保存更改 (Save changes)
说明: 如果后端实例只部署在一个可用区,必须启用此选项,否则会导致50%的连接失败
4. 配置 Route53 DNS 解析
- 进入 Route 53 控制台 → 托管区域 (Hosted zones)
- 选择域名 xxxxxx
- 点击 创建记录 (Create record)
配置记录:
- 记录名称: xxxx
- 记录类型: CNAME
- 值: xxxxxxx.amazonaws.com`
- TTL: 300秒
- 路由策略: 简单路由
- 点击 创建记录 (Create records)
5. 验证配置
- 进入目标组 xxxnlb-tg` → 目标 (Targets) 标签
- 确认目标状态为 healthy (可能需要等待1-2分钟)
如果状态为 initial 或 unhealthy,检查:
- 端口是否正确
- 健康检查协议是否为 TCP
- 实例安全组是否允许端口
问题排查过程
问题 1: 端口配置错误
现象: 目标健康检查失败,WebSocket连接超时
原因:
- 目标组配置端口为 9520
- 实际后端服务监听端口为 9521
解决方案(控制台操作):
- 进入 EC2 控制台 → 目标组 → 选择
xxx-tg - 点击 目标 (Targets) 标签
- 选中错误端口的目标 → 点击 注销 (Deregister)
- 点击 注册目标 (Register targets)
- 选择实例
i-xxxx - 端口覆盖: 输入
9521 - 点击 包含为待处理项 → 注册待处理目标
问题 2: 健康检查失败
现象: 目标状态一直为 initial,无法变为 healthy
原因:
- 9521端口是纯WebSocket服务器
- HTTP健康检查路径
/asdsadasg返回 404 - 响应:
404 WebSocket Upgrade Failure
解决方案(控制台操作): 修改为TCP健康检查
- 进入 EC2 控制台 → 目标组 → 选择
xxxxnlb-tg - 点击 健康检查 (Health checks) 标签
- 点击 编辑 (Edit)
修改配置:
- 健康检查协议: 选择 TCP
- 删除健康检查路径(TCP不需要)
- 点击 保存更改 (Save changes)
- 等待30-60秒,目标状态应变为 healthy
验证:
- 在目标组的 目标 (Targets) 标签查看状态
- 状态应显示为 healthy 且带绿色图标
问题 3: 跨可用区连接不稳定
现象:
- NLB有两个IP地址 (x.x.x.x, a.a.a.a)
- 其中一个IP连接超时
- 连接成功率约50%
原因:
- NLB部署在2个可用区 (us-east-1d, us-east-1e)
- 后端实例只在 us-east-1d
- 跨可用区负载均衡未启用
- 流量路由到 us-east-1e 时无本地目标
解决方案(控制台操作): 启用跨可用区负载均衡
- 进入 EC2 控制台 → 负载均衡器 → 选择 xxxx-nlb`
- 点击 属性 (Attributes) 标签
- 点击 编辑 (Edit) 按钮
- 找到 跨可用区负载均衡 (Cross-zone load balancing) 部分
- 选择 开启 (On)
- 点击 保存更改 (Save changes)
- 等待1-2分钟配置生效
验证:
- 在负载均衡器的 属性 标签确认 "跨可用区负载均衡" 显示为 已启用
- 多次测试连接,成功率应达到100%
注意: 跨可用区流量会产生数据传输费用 (~$0.01/GB)
问题 4: 域名连接失败 (根本原因)
现象:
- 直接使用NLB DNS可以连接:
ws://xxxxxx.amazonaws.com/dqwdwqd✅ - 使用自定义域名连接失败:
ws://xxxxxxx/dqwdwqd❌ - 错误:
Connection reset by peer
排查过程:
# 1. 验证DNS解析正确
nslookup xxxxxxx
# 输出: 正确解析到 NLB 的两个IP
# 2. 测试直接IP连接(带Host头)
curl -H "Host: xxxxxxx" \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: test" \
-H "Sec-WebSocket-Protocol: ocpp1.6" \
http://x.x.x.x/dasdwq
# 结果: Connection reset by peer
# 3. 测试直接IP连接(不带自定义Host头)
curl -H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: test" \
-H "Sec-WebSocket-Protocol: ocpp1.6" \
http://x.x.x.x/dasdwq
# 结果: HTTP/1.1 101 Web Socket Protocol Handshake ✅根本原因:
后端WebSocket服务器对Host头进行了验证,拒绝了 xxxx 域名的请求
解决方案:
修改后端WebSocket服务器配置,将 xxxxx 添加到允许的域名白名单中
最终配置总结
NLB 配置
- 名称: iot-nlb
- 类型: Network Load Balancer
- 监听器: TCP 80
- 目标组: iot-nlb-tg
- 目标端口: 9521
- 健康检查: TCP (端口9521)
- 跨可用区: 已启用
DNS 配置
- 记录: xxxxx
- 类型: CNAME
- 值: xxxxx.elb.us-east-1.amazonaws.com
- TTL: 300秒
WebSocket 连接要求
- 协议: ws:// (端口80)
- 必需请求头:
Sec-WebSocket-Protocol: ocpp1.6 - Host头: 需要后端服务器允许自定义域名
测试验证命令
1. 检查目标健康状态
aws elbv2 describe-target-health \
--target-group-arn xxxxxx \
--region us-east-1 \
--query 'TargetHealthDescriptions[*].[Target.Id,Target.Port,TargetHealth.State]' \
--output table2. 测试WebSocket握手
curl -i --max-time 3 \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: test" \
-H "Sec-WebSocket-Protocol: ocpp1.6" \
http://xxxxxx.elb.us-east-1.amazonaws.com/asdasds期望输出: HTTP/1.1 101 Web Socket Protocol Handshake
3. 验证DNS解析
dig +short xxxxxxx
nslookup xxxxxxx4. 检查NLB属性
aws elbv2 describe-load-balancer-attributes \
--load-balancer-arn xxxxxxxx \
--region us-east-1 \
--query 'Attributes[?Key==`load_balancing.cross_zone.enabled`]'常见问题与解决方案
Q1: WebSocket连接超时
检查清单:
- 目标健康状态是否为
healthy - 安全组是否允许目标端口入站流量
- 后端服务是否正常运行
- 端口配置是否正确
Q2: 连接成功率不稳定
可能原因:
- 跨可用区负载均衡未启用
- 目标实例分布不均
解决: 启用跨可用区负载均衡或在所有可用区部署实例
Q3: 域名无法连接但直接DNS可以
排查步骤:
- 验证DNS解析是否正确
- 清除本地DNS缓存
- 检查后端服务Host头验证逻辑
- 测试带/不带Host头的连接差异
Q4: 健康检查失败
WebSocket服务器特殊处理:
- 纯WebSocket服务器可能不支持HTTP健康检查
- 使用TCP健康检查替代
- 或在后端实现专门的健康检查端点
总结
本次排查解决了以下关键问题:
- ✅ 端口配置错误 (9520 → 9521)
- ✅ 健康检查协议不匹配 (HTTP → TCP)
- ✅ 跨可用区负载均衡未启用
- ✅ 后端服务Host头验证限制
最终状态: NLB配置正常,WebSocket连接稳定,需要后端服务添加域名白名单支持自定义域名访问。