假设我们有以下机器:
现客户端每隔 10s 向服务器上报心跳,流量路径客户端>CLB>服务器,使用的是 http 连接,keep-alive 设置的 30s 。按理来说连接经过 CLB ,服务器负责的连接是均衡的。
现在,假设故障了 4 台服务器,流量会偏移到一台服务器上,现在重启这 4 台机器,这些连接由于 keep-alive ,不会重新分布。
请问,这种问题怎么解决呢?
![]() |
1
cz5424 58 天前
都用 http 了,http 一般都人为是无状态的,我感觉你是 http 请求了之后,不完成,一直挂着链接
|
![]() |
2
cz5424 58 天前
服务端通知客户端断线,让客户端重连,就均衡了
|
3
dampler OP @cz5424 有两种方案啊,一种是客户端定时断开重连。另外还有一种是服务端主动断开,想问问,服务端主动断开连接的细节是咋样的,老哥有了解吗?我们用的是 go ,gin 框架
|
6
fds 58 天前
我个人其实觉得不是什么大问题。大不了等那 4 台好了,把之前那 1 台重启一下,全部断开连接。问了下 GPT ,说是可以在服务内部根据情况给客户端发送关闭连接的 header 。
func main() { router := gin.Default() router.Use(func(c *gin.Context) { // 可以根据具体情况决定是否关闭连接 c.Header("Connection", "close") c.Next() }) router.GET("/heartbeat", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) router.Run() // 默认在 localhost:8080 } 话说应该怎么格式化代码呢? |
7
fds 58 天前
不过你这种架构我觉得还是应该负载均衡 LB 自己处理这些连接问题,直接断掉连接等重连就行。
|
8
csys 58 天前
如果是 http1.1+keep-alive 做长连接,现有负载均衡应该是能正常工作的,keep-alive 超时 tcp 链接就会被关闭,你新发送的请求就会开启新的链接
客户端请求头上的 keepalive 好像指定的是保持连接的最小时长(这个我不太确定 服务端可以配置 keepalive 的超时时间 |
13
csys 58 天前 via Android
@dampler 可以参考 timeout 中间件,请求头上的 keepalive 不是个强制超时时间,而是给服务端的一个暗示,表示“需要保持 xx 秒的长连接”,具体什么时候 close 取决于双方的设置,如果库或框架没有自动 close ,你直接定时 close 就行
|
14
dampler OP @csys 明白你的意思了,是通过中间件,判断建连的时间和当前时间的差值,超过设定的值就 close 。(我去找找,看看能不能获取建连的时间,这一块我去了解下 Thx )
|
![]() |
15
lasuar 57 天前
1. 首先客户端要支持无感重连
2. 服务端长连接程序能够感知其他节点的启动时间和负载压力,然后根据自己的负载压力决定是否要进行主动连接断开(无感的关键是通过自定义消息通知客户端,而不是简单断连) |
![]() |
16
lasuar 57 天前
3. 决定要进行断连时,选择较老且**空闲**的连接进行断开。
|
17
gongguowei02 57 天前
@dampler #10 应该是直接可以 set 在 headers 里面的,试一下?
|
18
coderxy 57 天前
不要用 clb , 用 alb ,alb 负责与客户端连接,客户端请求 alb 再转发到 ecs 时关闭长连接,这样每次都是新建一个 http 连接了。
|
19
xxs55 57 天前
把长连接 单独一个服务 然后 rpc 调用 其他服务可以不。
|
20
mingyuewandao 57 天前
讲真,我之前也探索过类似的问题,但没有得到很好的答案。对于长链接,如果是 grpc 这种,通常要通过客户端负载均衡来做。如果加了 lb 后,客户端无法感知服务端服务器变化,因此客户端负载均衡肯定行不通。此时应该由 lb 来提供对应能力。如果通过连接经过 lb 已经建立,lb 如何重新调整是个问题,我也没有想到应该如何做。因此问了下 GPT ,以下可做参考:
在这种情况下,服务器故障后重启,由于 HTTP 连接的 keep-alive 特性,现有的连接可能不会立即重新分布到所有服务器上,这可能导致剩余的服务器负载过高。以下是一些可能的解决方案: 1. **重置连接**:在服务器重启后,可以配置负载均衡器( CLB )发送一个重置信号给客户端,通知它们现有的连接已经无效,需要重新建立连接。这可以通过 HTTP 响应状态码如`408 Request Timeout`或`503 Service Unavailable`来实现。 2. **调整 keep-alive 超时**:如果可能,可以在服务器重启前调整 HTTP 连接的 keep-alive 超时时间,使其更短,这样在服务器重启后,客户端会更快地重新建立连接。 3. **使用更智能的负载均衡策略**:一些负载均衡器支持基于服务器当前负载的动态调整流量分配的策略。如果 CLB 支持这种策略,可以在服务器重启后动态调整流量分配。 4. **客户端重连机制**:客户端可以在检测到服务器故障或响应超时时,自动尝试重新连接到其他健康的服务器。 5. **使用会话持久性**:如果 CLB 支持会话持久性( session persistence ),可以配置它在服务器重启后将流量重新分配给所有可用的服务器,而不是只连接到一台服务器。 6. **心跳机制调整**:可以调整客户端的心跳上报机制,使其在检测到服务器故障时,立即断开连接并重新连接到其他服务器。 7. **使用更高级的负载均衡技术**:例如使用基于 IP 的负载均衡,这样即使 TCP 连接保持打开状态,新的请求也可以被分配到不同的服务器。 8. **监控和自动化**:实现监控系统来检测服务器状态和负载情况,一旦检测到故障或负载不均衡,自动化脚本可以介入,进行流量重新分配或触发重连机制。 9. **使用 WebSocket**:如果适用,可以考虑使用 WebSocket 代替 HTTP 长连接,因为 WebSocket 提供了一个全双工通信渠道,可以在服务器端更容易地管理连接状态。 10. **服务发现**:使用服务发现机制,让客户端能够动态地发现可用的服务实例,并在必要时重新连接。 选择哪种解决方案取决于具体的应用场景、现有的技术栈以及可接受的复杂度。在实际应用中,可能需要结合多种策略来确保系统的高可用性和负载均衡。 |
![]() |
21
raviscioniemeche 57 天前
做代理的时候 一般都会禁用 keepalive 头
|
22
qinze113 57 天前
用四层 lb 不用要七层 lb,四层 lb 配置成客户端到 LB 保持长连接既可
|
![]() |
23
zhoudaiyu 57 天前
定时踢掉重连
|
26
dampler OP @raviscioniemeche 好的,这个我也去了解一下啦,禁用和启用的优缺点
|
27
dampler OP @mingyuewandao 很有参考意义,感谢感谢~
|
29
photon006 57 天前
好奇长连接为什么不用 websocket ,全双工通讯比 http 更容易操控客户端
crc 算法可以根据多台服务器 ip 实现负载均衡 https://chatgpt.com/share/1a96aad4-ddb9-4b64-9e75-4674d050c9e7 多年以前网易开源游戏引擎 pomelo 就是这样的: https://github.com/NetEase/pomelo 客户端都会默认自动重连,服务端 kickout 客户端就能恢复均衡,每台服务器连接数也是一目了然。 |
30
dampler OP 大结局啦,已经找到解决方案了,祝各位升职加薪,每天开开心心!!!!
https://tencentcloudcontainerteam.github.io/tke-handbook/best-practice/scale-keepalive-service.html |
32
dampler OP server := &http.Server{
Handler: r, ConnContext: func(ctx context.Context, conn net.Conn) context.Context { // 每个连接创建一个独立的计数器实例 return context.WithValue(ctx, "counter", new(counter)) }, } 使用 connContext 也是可以的 |