ServiceBusSender的keep_alive属性作用及替代自定义Ping实现长连接的可行性问询
提问者问题描述:
我正在尝试让ServiceBusSender的连接持续超过5分钟,不需要手动发送Ping消息。目前我用的是手动Ping的方式:
if time.time()-last_ping_time >= 200: logging.debug(f"trying to ping service bus {QUEUE_DATA['WorkersName']}") trash_message = ServiceBusMessage(b"") trash_message.application_properties = {b"SiteEnv":b"trash"} sender.send_messages(trash_message) last_ping_time=time.time() logging.debug("pinged service bus")
我想去掉这个逻辑,想知道是否可以通过给ServiceBusSender传递keep_alive属性(该属性在Configuration类中默认值为30),设置成1小时来解决问题?
更新1:
我了解到keep_alive本质是底层的Ping机制,但还是不确定:我能不能像下面这样使用它,然后移除自己的Ping逻辑,避免连接断开?
sender=ServiceBusSender._from_connection_string(topic_name="SOME_TOPIC_NAME", conn_str="SOME_CONNECTION_STRING", transport_type=TransportType.AmqpOverWebsocket, keep_alive=200)
更新2:
发现keep_alive只影响AMQP协议,不影响TCP层面...
更新3:
我查看了应用日志,发现Sender和Receiver初始化后长时间空闲才处理第一个请求,但第一个请求完成后15分钟就触发了ConnectionResetError。为什么会这样?如何让Sender和Receiver的连接在整个程序运行期间保持活跃,不需要手动发Ping消息?
日志内容:
2025-01-02 22:07:20,523 - DEBUG - got bool_launch_service_bus True so launching service bus 2025-01-02 22:07:20,523 - DEBUG - Launch time: 02/01/2025 22:07:20 2025-01-02 22:07:21,388 - INFO - RECEIVER: initiation was successful 2025-01-02 22:07:21,400 - INFO - SENDER: initiation was successful 2025-01-02 22:34:30,996 - DEBUG - Received request 2025-01-02 22:34:30,997 - DEBUG - MessageId: id - CorrelationId id: 2025-01-02 22:34:30,997 - DEBUG - Trying to call for CorrelationId: id 2025-01-02 22:34:30,997 - INFO - executing func() 2025-01-02 22:34:31,204 - DEBUG - Finished with CorrelationId: id time: 0.2070004940032959 2025-01-02 22:34:31,694 - DEBUG - full response creation time for id: 0.6970000267028809 2025-01-02 22:34:32,626 - INFO - finished id 2025-01-02 22:35:30,591 - INFO - RECEIVER: message with id: id complete by distributive service 2025-01-02 22:50:38,572 - DEBUG - Transport read failed: ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)
专家解答:
看起来你在为ServiceBusSender的长连接维持问题费神,我来帮你理清关键要点:
1. keep_alive完全可以替代自定义Ping逻辑
你提到的keep_alive参数是SDK内置的AMQP心跳机制,默认30秒,它会在连接空闲时自动发送AMQP心跳帧,不需要你手动构造空消息。把它设置为合适的值(比如200秒或3600秒)后,完全可以移除你的自定义Ping代码——SDK会自动帮你完成保活动作,防止连接因空闲被服务端或中间设备断开。
2. 关于“keep_alive不影响TCP”的补充
这个认知没错,但不用过度焦虑:
- TCP层面的保活一般由操作系统配置,而Azure Service Bus的核心连接逻辑基于AMQP,SDK的AMQP心跳已经能让防火墙、代理等中间设备识别到连接的活跃性,绝大多数场景下这就足够避免TCP连接被判定为空闲断开。
- 如果你的运行环境有极其严格的TCP空闲超时规则,才需要额外配置系统级TCP keepalive,但这种情况非常少见,优先依赖SDK的AMQP keep_alive即可。
3. 针对日志中ConnectionResetError的解决建议
从日志来看,连接在第一次请求后15分钟左右断开,结合初始化后长时间空闲的情况,给你几个排查方向:
- 升级SDK版本:旧版本的Azure Service Bus SDK可能存在keep_alive机制的bug,建议升级到最新稳定版,确保心跳逻辑正常执行。
- 调整keep_alive间隔:当使用
AmqpOverWebsocket时,部分代理/防火墙对WebSocket连接的空闲超时更敏感,与其设置1小时的长间隔,不如先尝试180-300秒(3-5分钟),更稳妥地避免被判定为空闲连接。 - 复用Sender/Receiver实例:确保你的ServiceBusSender和ServiceBusReceiver是全局复用的单实例,不要频繁创建销毁——频繁重建连接反而更容易触发断开问题。
总结
完全可以通过配置keep_alive参数替代自定义Ping逻辑,只要确保SDK版本正确、参数设置合理、实例全局复用,就能让连接在整个程序运行期间保持活跃,不需要手动发送空消息。
备注:内容来源于stack exchange,提问作者Ivan

