监视和排查 Azure SignalR 服务日志问题

本文介绍如何使用 Azure Monitor 功能分析和排查 Azure SignalR 生成的资源日志监视数据的问题。

每种 Azure SignalR 服务的 Azure 门户“概述”页都包含资源使用情况(例如并发连接和消息计数)的简要视图。 此有用信息只是门户中提供的一小部分监视数据。 创建资源后,其中的某些数据会自动收集,并可供分析。

在进行某些配置后,可以启用其他类型的数据收集。 本文介绍如何使用 Azure Monitor 工具配置日志数据收集以及分析收集的数据并对其进行故障排除。

先决条件

若要启用资源日志,需要设置用于存储日志数据的位置,例如 Azure 存储或 Log Analytics。

  • Azure 存储保留策略审核、静态分析或备份的资源日志。
  • Log Analytics是灵活的日志搜索和分析工具,可用于分析 Azure 资源生成的原始日志。

启用资源日志

Azure SignalR 服务支持连接情况日志、消息传送日志和 Http 请求日志。 有关这些类型的日志的更多详细信息,请参阅资源日志类别

资源日志默认已禁用。 若要使用诊断设置启用资源日志,请执行以下步骤:

  1. Azure 门户中的“监视”下,单击“诊断设置”。

    在窗格中导航到诊断设置。

    你将获得诊断设置的完整视图。

    诊断设置的完整视图。

  2. 配置日志源设置。

    1. 在“日志源设置”部分中,表显示为每个日志类型收集行为。
    2. 检查要为所有连接收集的特定日志类型。 否则,只会为诊断客户端收集日志。
  3. 配置日志目标设置。

    1. 在“日志目标设置”部分中,诊断设置的表显示现有诊断设置。 可以选择表中的链接来访问日志目标以查看收集的资源日志。
    2. 在本部分中,选择“配置日志目标设置”按钮以添加、更新或删除诊断设置。
    3. 选择“添加诊断设置”以添加新的诊断设置,或选择“编辑”以修改现有诊断设置。
    4. 设置所需的存档目标。 目前,Azure SignalR 服务支持“存档到存储帐户”和“发送到 Log Analytics”
    5. 选择要存档的日志。 仅 AllLogs 适用于资源日志。 它仅控制是否要存档日志。 若要配置需要在 Azure SignalR 服务中生成哪些日志类型,请在“日志源设置”部分中进行配置。

    “诊断设置”窗格。

    1. 保存新的诊断设置。 新设置将在大约 10 分钟后生效。 之后,日志将发送到配置的存档目标。 有关如何配置日志目标设置的详细信息,请参阅 Azure 资源日志概述

日志存储在“诊断日志”窗格中配置的存储帐户内。 有关存储格式和字段的更多详细信息,请参阅数据存储

查询资源日志

若要查询资源日志,请执行以下步骤:

  1. 在目标 Log Analytics 中选择“日志”

    Log Analytics 菜单项

  2. 输入 SignalRServiceDiagnosticLogs 并选择时间范围。 若要进行高级查询,请参阅 Azure Monitor 中的 Log Analytics 入门

    在 Log Analytics 中查询日志

若要对 Azure SignalR 服务使用示例查询,请执行以下步骤:

  1. 在目标 Log Analytics 中选择“日志”

  2. 选择“查询”选项卡以打开查询资源管理器。

  3. 选择“资源类型”以在资源类型中对示例查询进行分组。

  4. 选择“运行”以运行脚本

    Log Analytics 中的示例查询

注意

存储目标的查询字段名称与 Log Analytics 的字段名称略有不同。 有关存储表与 Log Analytics 表之间的字段名称映射的详细信息,请参阅资源日志表映射

使用资源日志进行故障排除

若要对 Azure SignalR 服务进行故障排除,可以启用服务器端/客户端日志来捕获故障。 当 Azure SignalR 服务公开资源日志时,可以利用资源日志对服务的日志进行故障排除。

遇到连接意外增长或丢失时,可以利用连接日志进行故障排除。 典型问题通常与意外的连接数量更改、连接达到连接限制和授权失败有关。 以下几节介绍如何排除故障。

意外的连接丢失

如果遇到意外的连接丢失,请先在服务、服务器和客户端启用日志。

如果连接断开,资源日志将记录此断开连接事件,operationName 中会显示 ConnectionAbortedConnectionEnded

ConnectionAbortedConnectionEnded 之间的区别在于,ConnectionEnded 是客户端或服务器端触发的预期断开连接。 ConnectionAborted 通常是意外的连接丢失事件,message 中提供了中止原因。

下表列出了中止的原因。

Reason 说明
连接计数达到限制 连接计数达到当前定价层的限制。 考虑纵向扩展服务单元
应用程序服务器关闭了连接 应用服务器触发了中止。 可将其视为预期的中止
连接 ping 超时 通常,这是由网络问题造成的。 考虑从 Internet 检查应用服务器的可用性
服务重新加载,尝试重新连接 Azure SignalR 服务正在重新加载。 Azure SignalR 支持自动重新连接。可以等到已重新连接,或手动重新连接到 Azure SignalR 服务
内部服务器的暂时性错误 Azure SignalR 服务中发生暂时性错误,应可自动恢复
服务器连接已丢失 服务器连接丢失并出现未知错误,请考虑先使用服务/服务器/客户端日志自我进行故障排除。 尝试排除基本问题(例如网络问题、应用服务器端问题等)。 如果问题未解决,请联系我们以获得更多帮助。 有关详细信息,请参阅获取帮助部分。

意外的连接增加

若要排查连接意外增加的问题,首先需要筛选出额外的连接。 可以向测试客户端连接添加唯一的测试用户 ID。 检查资源日志。 如果看到多个客户端连接使用同一测试用户 ID 或 IP,可能表示客户端创建了超过预期数目的连接。 检查客户端。

授权失败

如果针对客户端请求返回了“401 未授权”,请检查资源日志。 如果遇到 Failed to validate audience. Expected Audiences: <valid audience>. Actual Audiences: <actual audience>,则表示访问令牌中的所有受众无效。 尝试使用日志中建议的有效受众。

限制

如果发现无法在 SignalR 客户端与 Azure SignalR 服务之间建立连接,请检查资源日志。 如果在资源日志中遇到 Connection count reaches limit,则表示与 SignalR 服务建立的连接过多,从而达到了连接计数限制。 考虑纵向扩展 SignalR 服务。 如果在资源日志中遇到 Message count reaches limit,则表示使用的是免费层,并且已用完了消息配额。 如果要发送更多消息,请考虑将 SignalR 服务更改为标准层。 有关详细信息,请参阅 Azure SignalR 服务定价

遇到与消息相关的问题时,可以利用消息传送日志进行故障排除。 首先,在服务、服务器和客户端中启用资源日志

注意

对于 ASP.NET Core,请参阅此处以启用服务器和客户端中的日志记录。

对于 ASP.NET,请参阅此处以启用服务器和客户端中的日志记录。

如果你不介意潜在的性能影响,并且没有客户端到服务器方向的消息,请选中 Log Source Settings/Types 中的 Messaging 以启用“全部收集”日志收集行为。 有关此行为的详细信息,请参阅全部收集

否则,取消选中 Messaging 以启用“部分收集”日志收集行为。 此行为需要客户端和服务器中的配置才能启用它。 有关详细信息,请参阅部分收集

消息丢失

如果遇到消息丢失问题,关键在于找到丢失消息的位置。 基本上,使用 Azure SignalR 服务时有三个组件:SignalR 服务、服务器和客户端。 服务器和客户端都连接到 SignalR 服务,但一旦协商完成,它们不会直接相互连接。 因此,需要考虑消息的两个方向,对于每个方向,需要考虑两个路径:

  • 通过 SignalR 服务从客户端到服务器
    • 路径 1:客户端到 SignalR 服务
    • 路径 2:SignalR 服务到服务器
  • 通过 SignalR 服务从服务器到客户端
    • 路径 3:服务器到 SignalR 服务
    • 路径 4:SignalR 服务到服务器

消息路径

对于“全部收集”收集行为:

Azure SignalR 服务仅跟踪“通过 SignalR 服务从服务器到客户端”方向的消息。 服务器中会生成跟踪 ID。 消息将跟踪 ID 传送到 SignalR 服务。

注意

如果要跟踪消息并在应用服务器中发送来自中心外部的消息,则需要启用“全部收集”收集行为来收集未源自诊断客户端的消息的消息的消息日志。 诊断客户端同时适用于收集行为“全部收集”和“部分收集”,但日志的收集优先级更高。 有关详细信息,请参阅“诊断客户端”部分

通过检查登录服务器和服务端,可以轻松地了解消息是否从服务器发送、到达 SignalR 服务以及离开 SignalR 服务。 基本上,通过检查“已接收”和“已发送”消息是否根据消息跟踪 ID 进行匹配,可以判断消息丢失问题在此方向上是出于服务器还是 SignalR 服务。 有关详细信息,请参阅下面的详细信息

对于“部分收集”收集行为:

将客户端标记为诊断客户端后,Azure SignalR 服务跟踪两个方向的消息。

通过检查登录服务器和服务端,可以轻松地了解消息是否已成功通过服务器或 SignalR 服务。 基本上,通过检查“已接收”和“已发送”消息是否根据消息跟踪 ID 进行匹配,可以判断消息丢失问题是出于服务器还是 SignalR 服务。 有关详细信息,请参阅以下详细信息。

消息流的详细信息

对于“通过 SignalR 服务从客户端到服务器”方向,SignalR 服务只考虑源自诊断客户端的调用,即直接在诊断客户端中生成的消息,或者由于间接调用诊断客户端而生成的服务消息。

消息到达路径 1 中的 SignalR 服务后,将在 SignalR 服务中生成跟踪 ID。 SignalR 服务为诊断客户端中的每个消息生成日志 Received a message <MessageTracingId> from client connection <ConnectionId>.。 消息从 SignalR 传递到服务器后,SignalR 服务生成日志消息 Sent a message <MessageTracingId> to server connection <ConnectionId> successfully.。 如果你看到这两个日志,则可以确定消息已通过 SignalR 服务成功传递。

注意

由于 ASP.NET Core SignalR 的限制,来自客户端的消息不包含任何消息级别 ID,但 ASP.NET SignalR 会为每个消息生成调用 ID。 可以使用它映射跟踪 ID。

然后,消息会在路径 2 中传递跟踪 ID 服务器。 消息到达后服务器会即刻生成日志 Received message <messagetracingId> from client connection <connectionId>

消息在服务器中调用中心方法后,将使用新的跟踪 ID 生成新服务消息。 生成服务消息后,服务器会生成登录模板 Start to broadcast/send message <MessageTracingId> ...。 实际日志基于你的方案。 然后消息被传送到路径 3 中的 SignalR 服务。 服务消息离开服务器后,系统会生成一个名为 Succeeded to send message <MessageTracingId> 的日志。

注意

来自客户端的消息的跟踪 ID 无法映射到要发送到 SignalR 服务的服务消息的跟踪 ID。

服务消息到达 SignalR 服务后,生成名为 Received a <MessageType> message <MessageTracingId> from server connection <ConnectionId>. 的日志。 然后,SignalR 服务处理服务消息并传递到目标客户端。 在路径 4 中将消息发送到客户端后,生成日志 Sent a message <MessageTracingId> to client connection <ConnectionId> successfully.

总之,消息传入和传出 SignalR 服务和服务器时,生成消息日志。 可以使用这些日志来验证消息是否在这些组件中丢失。

以下示例是典型的消息丢失问题。

客户端无法接收组中的消息

此问题的典型情况是客户端在发送组消息后加入组。

Class Chat : Hub
{
    public void JoinAndSendGroup(string name, string groupName)
    {
        Groups.AddToGroupAsync(Context.ConnectionId, groupName); // join group
        Clients.Group(groupName).SendAsync("ReveiceGroupMessage", name, "I'm in group"); // send group message
    }
}

例如,有人可能会调用加入组,并使用同一中心方法发送组消息。 问题(AddToGroupAsync 如下)是 async 方法。 由于没有 awaitAddToGroupAsync 无法等待它完成,组消息在 AddToGroupAsync 完成之前发送。 由于网络延迟,以及将客户端加入某些组的过程延迟,加入组操作可能晚于组消息传递完成。 如果是这样,则第一个组消息没有任何客户端作为接收方,因为没有客户端加入该组,于是便发生了消息丢失问题。

如果没有资源日志,则无法确定客户端加入组的时间以及发送组消息的时间。 启用消息传递日志后,即可在 SignalR 服务中比较消息到达时间。 请执行以下步骤进行故障排除:

  1. 在服务器中查找消息日志以确定客户端加入组的时间以及发送组消息的时间。
  2. 从消息日志中获取加入组的消息跟踪 ID A 和组消息的消息跟踪 ID B。
  3. 在日志存档目标中的消息日志中筛选这些消息跟踪 ID,然后比较其到达时间戳。 你会发现哪些消息先到达 SignalR 服务。
  4. 如果消息跟踪 ID A 到达时间晚于 B 到达时间,则必须在客户端加入组之前发送组消息。 需要确保组消息发送之前客户端位于组中。

如果在 SignalR 或服务器中丢失消息,请尝试根据消息跟踪 ID 获取警告日志以发现原因。 如果需要进一步的帮助,请参阅“获取帮助”部分

资源日志收集行为

使用资源日志(尤其是消息传递日志)有两种典型方案。

有人可能会关心每条消息的质量。 例如,他们对于是否成功发送/接收消息十分敏感,或者他们想要记录通过 SignalR 服务传递的每条消息。

同时,其他人可能会关心性能。 他们对于消息的延迟十分敏感,有时他们需要跟踪一些连接中的消息,而不是出于某种原因而跟踪所有连接中的消息。

因此,SignalR 服务提供两种类型的收集行为

  • 全部收集:收集所有连接中的日志
  • 部分收集:收集某些特定连接中的日志

注意

为了区分收集日志与未收集日志的连接,SignalR 服务根据服务器和客户端的诊断客户端配置将某些客户端视为诊断客户端,其中资源日志始终收集,而另一些不是。 有关详细信息,请参阅“部分收集”部分

全部收集

资源日志由所有连接收集。 例如,消息传递日志。 启用此行为后,SignalR 服务向服务器发送通知,开始为每个消息生成跟踪 ID。 跟踪 ID 经由消息携带传送到服务。 服务还会按跟踪 ID 记录消息。

注意

请注意,为了确保 SignalR 服务的性能,SignalR 服务不会等待和分析整条从客户端发送的消息。 因此不会记录客户端消息。 如果客户端标记为诊断客户端,则客户端消息记录在 SignalR 服务中。

配置指南

若要启用此行为,请选中“日志源设置”的“类型”部分中的复选框。

此行为不需要更新服务器端配置。 此配置更改始终自动发送到服务器。

部分收集

资源日志仅由诊断客户端收集。 所有消息都会记录,包括诊断客户端中的客户端消息和连接性事件。

备注

诊断客户端数限制为 100。 如果诊断客户端数超过 100,则超出数量的诊断客户端受 SignalR 服务的限制。 超过一定数量的新客户端无法连接到 SignalR 服务,并引发 System.Net.Http.HttpRequestException,收到消息 Response status code does not indicate success: 429 (Too Many Requests)。 已连接的客户端可正常工作,不受限制策略的影响。

诊断客户端

诊断客户端是一个逻辑概念。 任何客户端都可以是诊断客户端。 服务器控制哪个客户端可以是诊断客户端。 将客户端标记为诊断客户端后,将在此客户端中启用所有资源日志。 若要将客户端设置为诊断客户端,请参阅配置指南

配置指南

若要启用此行为,需要配置服务、服务器和客户端。

服务端

若要启用此行为,请在“日志源设置”的“类型”部分中取消选中特定日志类型的复选框。

服务器端

此外,还设置 ServiceOptions.DiagnosticClientFilter 根据来自客户端的 HTTP 上下文定义诊断客户端的筛选器。 例如,使客户端具有中心 URL <HUB_URL>?diag=yes,然后设置 ServiceOptions.DiagnosticClientFilter 以筛选诊断客户端。 如果返回 true,则客户端标记为诊断客户端。 其他情况下它仍是常规客户端。 可以在启动类中设置 ServiceOptions.DiagnosticClientFilter,如下所示:

// sample: mark a client as diagnostic client when it has query string "?diag=yes" in hub URL
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services
        .AddSignalR()
        .AddAzureSignalR(o =>
        {
            o.ConnectionString = "<YOUR_ASRS_CONNECTION_STRING>";
            o.DiagnosticClientFilter = context => context.Request.Query["diag"] == "yes";
        });

    return services.BuildServiceProvider();
}
客户端

通过配置 HTTP 上下文将客户端标记为诊断客户端。 例如,通过添加查询字符串 diag=yes 将客户端标记为诊断客户端。

var connection = new HubConnectionBuilder()
    .WithUrl("<HUB_URL>?diag=yes")
    .Build();

获取帮助

我们建议先自行排查问题。 大多数问题都是由应用服务器或网络问题造成的。 遵循使用资源日志进行故障排除的指南基本故障排除指南找出根本原因。 如果依然无法解决问题,请考虑在 GitHub 中提出问题或者在 Azure 门户中创建票证。 提供:

  1. 问题发生前后大约 30 分钟的时间范围
  2. Azure SignalR 服务的资源 ID
  3. 问题详情,越具体越好:例如,应用服务器无法发送消息、客户端连接丢失等等
  4. 从服务器/客户端以及其他可能有用的材料收集的日志
  5. [可选] 重现代码

注意

如果在 GitHub 中提出问题,请勿公开透露敏感信息(如资源 ID、服务器/客户端日志)。 仅以私密方式发送给 Microsoft 组织中的成员。