Azure SignalR Service无法收发消息问题排查求助
我编写了一套SignalR演示代码,计划在机器A上向Azure SignalR Service发送消息,机器B接收消息。测试时无报错信息,但机器B无法收到任何消息。查看SignalR控制台图表,连接数据显示正常,但消息计数图表无数据。
相关代码
SignalR客户端核心代码
import datetime import time import base64 import hmac import hashlib import jwt,os from configs.init_env import init_env from logging.handlers import RotatingFileHandler from signalrcore.hub_connection_builder import HubConnectionBuilder class SignalRClient(): def __init__(self, submission_id): init_env() self.connection_string = os.getenv("AZURE_SIGNALR_CONNECTION_STRING") if not self.connection_string: raise ValueError("AZURE_SIGNALR_CONNECTION_STRING environment variable is not set or is empty.") # Parse connection string parts = dict(s.split('=', 1) for s in self.connection_string.split(';') if s) self.endpoint = parts.get("Endpoint") self.access_key = parts.get("AccessKey") self.submission_id = submission_id if isinstance(self.submission_id, int): self.submission_id = str(self.submission_id) else: self.submission_id = str(self.submission_id) self.submission_id = self.submission_id[:4] self.hub_name = f"loghub{self.submission_id}" audience_url = f"{self.endpoint}/client/?hub={self.hub_name}" try: self.hub_connection = HubConnectionBuilder() \ .with_url(audience_url, options={ "access_token_factory": self.generate_access_token }) \ .build() self.hub_connection.start() except Exception as e: raise Exception(f'Failed to connect to SignalR: {e}') def generate_access_token(self): """Generate JWT token for Azure SignalR.""" payload = { "aud": f"{self.endpoint}/client/?hub={self.hub_name}", "exp": int(time.time()) + 3600, # Token expiration time (1 hour) "iat": int(time.time()) } token = jwt.encode(payload, self.access_key, algorithm="HS256") return token def emit(self, message_type, message): try: res = self.hub_connection.send(message_type, [message]) return res except Exception as e: raise Exception(f"Failed to send message to SignalR: {e}") def listen(self): """Listen for messages from Azure SignalR and print them.""" print("Listening for messages...") def on_message(message): print(f"Received message: {message}") self.hub_connection.on("ReceiveMessage", on_message)
机器A发送消息测试代码
import unittest from autotest.signalr.signalr import SignalRClient import threading import time class TestSignalr(unittest.TestCase): def setUp(self): self.signalr_client = SignalRClient(100) def test_listen_messages(self): loop = 0 while loop < 30000: time.sleep(1) res = self.signalr_client.emit("Emit_Log", f"Message {loop}") invocation_id = res.invocation_id print(f"Sent message {loop} with invocation id: {invocation_id}") loop += 1 if __name__ == '__main__': unittest.main()
机器B接收消息测试代码
import unittest from autotest.signalr.signalr import SignalRClient import threading import time class TestSignalr(unittest.TestCase): def setUp(self): self.signalr_client = SignalRClient(100) def test_listen_messages(self): self.signalr_client.listen() loop = 0 while loop < 30000: time.sleep(3) print("Main thread is still running...") loop += 1 if __name__ == '__main__': unittest.main()
问题根源与修复方案
1. 消息收发方法名不匹配
机器A调用emit("Emit_Log", ...)发送消息,但机器B监听的是ReceiveMessage方法,两者名称完全不匹配,Azure SignalR无法将消息路由到正确的监听逻辑。
修复:统一两端的方法名,比如都用ReceiveLog:
- 机器A发送改为:
self.signalr_client.emit("ReceiveLog", f"Message {loop}") - 机器B监听改为:
self.hub_connection.on("ReceiveLog", on_message)
2. 缺少消息中转逻辑
Azure SignalR Service是中继服务,并非直接的P2P消息通道。直接通过客户端发送消息时,需要有后端服务(或无服务器函数)负责消息的转发广播。当前代码没有实现这一层中转,导致消息无法从机器A传递到机器B。
可选修复方案:
方案一:使用Azure Functions无服务器中转(推荐)
创建Azure Function并绑定SignalR,编写转发逻辑:
import azure.functions as func app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) @app.route(route="negotiate") @app.signal_r_negotiate(connection_string_setting="AzureSignalRConnectionString") def negotiate(req: func.HttpRequest) -> func.HttpResponse: return func.SignalRNegotiateResponse() @app.signal_r_trigger(arg_name="req", hub_name="loghub100", event="ReceiveLog") def signalr_trigger(req: func.SignalRInvocationContext) -> func.SignalRMessage: # 广播消息给所有连接的客户端 return func.SignalRMessage( target="ReceiveLog", arguments=[req.arguments[0]] )
客户端需修改连接逻辑,通过Function的negotiate接口获取连接信息,而非直接连接Azure SignalR的endpoint。
方案二:搭建后端Hub服务
编写ASP.NET Core SignalR Hub处理消息转发:
using Microsoft.AspNetCore.SignalR; public class LogHub : Hub { public async Task ReceiveLog(string message) { // 广播给所有在线客户端 await Clients.All.SendAsync("ReceiveLog", message); } }
配置后端服务与Azure SignalR集成后,客户端连接到后端Hub,由后端完成消息转发。
3. 补充连接状态监听
在SignalRClient的__init__方法中添加连接断开重连逻辑,确保连接稳定:
def on_closed(): print("Connection closed, attempting to reconnect...") self.hub_connection.start() self.hub_connection.on_close(on_closed)
4. 验证JWT Token有效性
打印生成的JWT Token,检查aud字段是否符合{endpoint}/client/?hub={hub_name}格式,确保权限验证通过。
内容的提问来源于stack exchange,提问作者Steven Guo

