币安Binance Websocket客户端后台运行一段时间后停止接收事件求助
问题根因
- 币安用户WebSocket的
listenKey默认有效期仅24小时,未主动续约会自动失效,导致连接断开无法接收事件 ThreadedWebsocketManager默认无自动重连逻辑,WebSocket因网络波动、服务端断开等问题掉线后不会自动恢复,出现进程存活但无数据接收的假死状态- 后台运行时Python默认开启输出全缓冲,你使用
print打印的调试信息和错误都滞留在缓冲区中,无法写入日志文件,导致你看不到错误提示 - 长连接未开启TCP保活,长时间无事件传输时链路被中间节点切断,上层无感知
修复方案
第一步:替换打印为日志模块,禁用输出缓冲
用logging模块同时输出到文件和控制台,运行脚本时加-u参数禁用Python全局缓冲,确保所有日志可追溯:
# 新增日志配置 import logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", handlers=[ logging.FileHandler("copy_trading.log", encoding="utf-8"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__)
后台启动命令改为:nohup python3 -u your_script_name.py &
第二步:增加listenKey续约+自动重连逻辑
每个线程单独做健康检查,检测到连接失效就自动重建连接,同时每30分钟续约一次listenKey,避免过期。
修复后完整代码
import csv import math import threading import time from datetime import datetime import logging from binance import ThreadedWebsocketManager, Client from binance.exceptions import BinanceAPIException # 日志配置 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", handlers=[ logging.FileHandler("copy_trading.log", encoding="utf-8"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) masters = [ [ 'api1', 'secret1', 'test1' ], [ 'api2', 'secret2', 'test2' ], ] def CopyOrder(order, client, name): # 原有跟单逻辑不变 try: # 你的跟单代码 logger.info(f"账户{name}同步订单成功:{order['i']}") except Exception as e: logger.error(f"账户{name}同步订单失败:{str(e)}", exc_info=True) def main(api_key: str, api_secret: str, name: str): while True: # 断线自动重连死循环 twm = None listen_key = None try: logger.info(f"开始监听账户:{name}") cl = Client(api_key=api_key, api_secret=api_secret, testnet=False) # 初始化websocket管理器,开启超时检测 twm = ThreadedWebsocketManager( api_key=api_key, api_secret=api_secret, testnet=False, socket_timeout=60, ping_timeout=30 ) # 获取并存储listenKey用于后续续约 listen_key = cl.stream_get_listen_key() twm.start() def handle_socket_message(msg): if msg.get('e') == 'executionReport': CopyOrder(order=msg, client=cl, name=name) def handle_error(msg): logger.error(f"账户{name}WebSocket出错:{str(msg)}") raise Exception("WebSocket错误触发重连") # 启动用户数据流,绑定错误回调 twm.start_user_socket( callback=handle_socket_message, error_callback=handle_error ) # 启动续约线程,每30分钟续约一次listenKey def renew_listen_key(): while twm and twm.is_alive(): try: cl.stream_keepalive(listen_key) logger.info(f"账户{name}listenKey续约成功") time.sleep(1800) # 30分钟续约一次 except Exception as e: logger.error(f"账户{name}listenKey续约失败:{str(e)}") break threading.Thread(target=renew_listen_key, daemon=True).start() twm.join() except Exception as e: logger.error(f"账户{name}监听异常,10秒后重试:{str(e)}", exc_info=True) finally: # 清理旧连接 if twm: try: twm.stop() except: pass time.sleep(10) if __name__ == "__main__": for master in masters: thread = threading.Thread(target=main, args=(master[0], master[1], master[2]), daemon=True) thread.start() # 主线程保持运行 while True: time.sleep(3600)
额外优化建议
- 可以增加监控逻辑,每1小时主动查询一次主账户最近订单,对比跟单记录,避免漏单
- 不要把API密钥硬编码在代码里,可以放到配置文件或者环境变量中读取
- 限制同一时间的重连频率,避免被币安接口限流
内容的提问来源于stack exchange,提问作者Hassan Ibraheem
相关产品推荐
相关产品推荐

