fix(wecom): improve websocket reconnection and goroutine lifecycle management
This commit is contained in:
parent
ac7e7f5a64
commit
3008a2a38c
1 changed files with 43 additions and 12 deletions
|
|
@ -503,13 +503,14 @@ func (c *WeComWSChannel) connectionManager() {
|
||||||
})
|
})
|
||||||
|
|
||||||
c.reconnects++
|
c.reconnects++
|
||||||
if c.reconnects > c.config.MaxReconnectAttempts {
|
if c.config.MaxReconnectAttempts > 0 && c.reconnects > c.config.MaxReconnectAttempts {
|
||||||
logger.ErrorC("wecom_ws", "Max reconnection attempts reached, giving up")
|
logger.ErrorC("wecom_ws", "Max reconnection attempts reached, but continuing to loop to ensure service alive")
|
||||||
// 触发错误事件
|
// 触发错误事件
|
||||||
c.eventManager.Emit(EventError, EventPayloadError{
|
c.eventManager.Emit(EventError, EventPayloadError{
|
||||||
Error: fmt.Errorf("max reconnection attempts reached"),
|
Error: fmt.Errorf("max reconnection attempts reached"),
|
||||||
})
|
})
|
||||||
return
|
// 不退出,只保留最大延迟
|
||||||
|
c.reconnects = c.config.MaxReconnectAttempts
|
||||||
}
|
}
|
||||||
|
|
||||||
// 指数退避重连
|
// 指数退避重连
|
||||||
|
|
@ -539,14 +540,28 @@ func (c *WeComWSChannel) connectionManager() {
|
||||||
// 连接成功,重置重连计数
|
// 连接成功,重置重连计数
|
||||||
c.reconnects = 0
|
c.reconnects = 0
|
||||||
|
|
||||||
|
// 使用上下文控制这部分的读写协程生命周期
|
||||||
|
connCtx, connCancel := context.WithCancel(c.ctx)
|
||||||
|
|
||||||
// 启动读写协程
|
// 启动读写协程
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2)
|
wg.Add(1)
|
||||||
|
|
||||||
|
// 清空旧的发送队列,防止将上一次遗留的数据发给新建立的连接
|
||||||
|
drainLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.sendCh:
|
||||||
|
default:
|
||||||
|
break drainLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 先启动 writeLoop,确保订阅消息能被发送
|
// 先启动 writeLoop,确保订阅消息能被发送
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
c.writeLoop()
|
defer connCancel()
|
||||||
|
c.writeLoop(connCtx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 等待一小段时间确保 writeLoop 已启动
|
// 等待一小段时间确保 writeLoop 已启动
|
||||||
|
|
@ -558,17 +573,22 @@ func (c *WeComWSChannel) connectionManager() {
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
c.closeConnection()
|
c.closeConnection()
|
||||||
|
connCancel()
|
||||||
|
wg.Wait()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
c.readLoop()
|
defer connCancel()
|
||||||
|
c.readLoop(connCtx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 等待读写协程结束
|
// 等待读写协程结束
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
c.closeConnection()
|
||||||
logger.InfoC("wecom_ws", "Connection closed, will reconnect...")
|
logger.InfoC("wecom_ws", "Connection closed, will reconnect...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -592,7 +612,7 @@ func (c *WeComWSChannel) connect() error {
|
||||||
headers := http.Header{}
|
headers := http.Header{}
|
||||||
|
|
||||||
logger.DebugC("wecom_ws", "Dialing WebSocket...")
|
logger.DebugC("wecom_ws", "Dialing WebSocket...")
|
||||||
conn, resp, err := dialer.Dial(c.config.WSURL, headers)
|
conn, resp, err := dialer.DialContext(c.ctx, c.config.WSURL, headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCF("wecom_ws", "WebSocket dial error", map[string]any{
|
logger.ErrorCF("wecom_ws", "WebSocket dial error", map[string]any{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
|
|
@ -700,12 +720,12 @@ func (c *WeComWSChannel) closeConnection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// readLoop 读取 WebSocket 消息
|
// readLoop 读取 WebSocket 消息
|
||||||
func (c *WeComWSChannel) readLoop() {
|
func (c *WeComWSChannel) readLoop(ctx context.Context) {
|
||||||
defer c.closeConnection()
|
defer c.closeConnection()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
@ -753,13 +773,13 @@ func (c *WeComWSChannel) readLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeLoop 写入 WebSocket 消息
|
// writeLoop 写入 WebSocket 消息
|
||||||
func (c *WeComWSChannel) writeLoop() {
|
func (c *WeComWSChannel) writeLoop(ctx context.Context) {
|
||||||
ticker := time.NewTicker(time.Duration(c.config.HeartbeatInterval) * time.Second)
|
ticker := time.NewTicker(time.Duration(c.config.HeartbeatInterval) * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
|
|
||||||
case msg := <-c.sendCh:
|
case msg := <-c.sendCh:
|
||||||
|
|
@ -768,7 +788,8 @@ func (c *WeComWSChannel) writeLoop() {
|
||||||
c.connMu.RUnlock()
|
c.connMu.RUnlock()
|
||||||
|
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
continue
|
logger.DebugC("wecom_ws", "Connection is nil, exiting writeLoop")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新写超时
|
// 更新写超时
|
||||||
|
|
@ -777,10 +798,20 @@ func (c *WeComWSChannel) writeLoop() {
|
||||||
logger.ErrorCF("wecom_ws", "WebSocket write error", map[string]any{
|
logger.ErrorCF("wecom_ws", "WebSocket write error", map[string]any{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
|
c.closeConnection()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
|
c.connMu.RLock()
|
||||||
|
conn := c.wsConn
|
||||||
|
c.connMu.RUnlock()
|
||||||
|
|
||||||
|
if conn == nil {
|
||||||
|
logger.DebugC("wecom_ws", "Connection is nil, exiting writeLoop on ticker")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 发送心跳
|
// 发送心跳
|
||||||
if err := c.sendPing(); err != nil {
|
if err := c.sendPing(); err != nil {
|
||||||
logger.ErrorCF("wecom_ws", "Ping failed", map[string]any{
|
logger.ErrorCF("wecom_ws", "Ping failed", map[string]any{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue