【问题标题】:Azure Web Jobs and SignalR memory leakAzure Web 作业和 SignalR 内存泄漏
【发布时间】:2024-01-08 09:15:01
【问题描述】:

所以我有一些网络作业偶尔会连接到信号器集线器并广播消息。下面只是一个示例,在本例中,它是一个用于开发的简单 Web 作业,其 TimerTrigger 属性设置为每 20 秒连续运行一次。就像下面代码中显示的那样。

    public static void Main()
    {

        JobHostConfiguration config = new JobHostConfiguration();
        config.Tracing.ConsoleLevel = TraceLevel.Verbose;
        config.UseTimers();
        if (config.IsDevelopment)
        {
            config.UseDevelopmentSettings();
        }
        var host = new JobHost(config);
        host.RunAndBlock();
    }

    public static void ProcessPush([TimerTrigger("00:00:20", RunOnStartup = true)] TimerInfo timerInfo, TextWriter log)
    {
        // Send a signalr message to the Hub
        try
        {            
            SendMessageToHub(log);
        }
        catch (Exception e)
        {
            log.WriteLine($"WebJob Push Exception: {e.Message}");
        }
    }

    private static async Task SendMessageToHub(TextWriter log)
    {
            var hub = new HubConnection(CloudConfigurationManager.GetSetting("MyWebSite"));
            var proxy = _hub.CreateHubProxy("MyHub");

            log.WriteLine("WebJob Push: Sending message to SignalR Hub.");
            if (_hub.State == Microsoft.AspNet.SignalR.Client.ConnectionState.Disconnected)
            {
                await _hub.Start();
            }
            await _proxy.Invoke("BroadcastMessage");
            log.WriteLine("WebJob Push: Sent message to SignalR Hub.");
    }

托管网站和信号中心的服务器上的内存总是会增加。在调查网站上的 IIS 日志时,使用长轮询的方式似乎在同一秒内有大量/批量的 POST 消息到达网站。然后它等待一会儿,然后被另一批消息轰炸。顺便说一句,这也会使 IIS 服务器上的 CPU 发疯。本文底部是 IIS 日志条目的示例。

我希望能够以与常规脉冲消息一致的方式从网络作业发送信号器消息(我们希望扩展网络作业 - 请原谅它现在在计时器上运行的事实) .

亲切的问候,

斯蒂芬

示例 IIS 日志条目 - 请注意它们都在同一秒内出现,而不是相隔 20 秒:

二○一六年十一月一十五日23时10分35秒POST / signalr /轮询clientProtocol = 1.4&运输= longPolling&connectionData = [%7B%22Name%22:%22MyHub%22%7D] connectionToken = TBbNVDpndk0riu8UvVzbGJrWjYIo7eMLcP4lk7ABV74OBMbZRTJrCRL1bzsPxpd1Tyle2rS3tV2JJrigninhu880ml51Xers76PPDX0Hf97dTBYR4k%2BVc2V9KAmiGt0p&MESSAGEID = d-80E0087- B%2C7D%7CEz%2C0%7CE0%2C0 443 - 104.210.116.149 SignalR.Client.NET45/2.2.1.0+(Microsoft+Windows+NT+6.2.9200.0) - 200 0 0 5343

2016年11月15日23时10分35秒POST / signalr /轮询clientProtocol = 1.4&运输= longPolling&connectionData = [%7B%22Name%22:%22MyHub%22%7D] connectionToken = KYwQXpNrPIU21NXMa0So5u42EwXTcMlGyLqL3tetx4WfOtTunHLclG%2BhPd%2BcPeZPmfe6KKvQL13XIU1W5fApuTv0XN5XFPoNUmyBjhhISoqodwcZeu3QKmkbaXcpHMtE&MESSAGEID = D- 80E0087-B%2C7D%7Cav%2C0%7Caw%2C2 443 - 104.210.116.149 SignalR.Client.NET45/2.2.1.0+(Microsoft+Windows+NT+6.2.9200.0) - 200 0 0 10641

2016-11-15 23:10:35 POST /signalr/poll clientProtocol=1.4&transport=longPolling&connectionData=[%7B%22Name%22:%22MyHub%22%7D]&connectionToken=nO%2BPZ8M5JJOpiobpJUV5%2FZvQyEKYjp%2FOuqQ% 2F0Bkq05TKRJZfeI%2FD%2BxRyPC7EsAAjXVqJr05PksorlMWrXocGkskfVsLU2Qvtx%2Fi1O8hU5lNz4KcoSc%2Bkv%2BlDpr2AZBLv&MESSAGEID = d-80E0087-B%2C7D%7CFB%2C0%7CFC%2C0 443 - 104.210.116.149 SignalR.Client.NET45 / 2.2.1.0 +(微软的Windows + + NT + 6.2 .9200.0) - 200 0 0 18282

2016-11-15 23:10:35 POST /signalr/poll clientProtocol=1.4&transport=longPolling&connectionData=[%7B%22Name%22:%22MyHub%22%7D]&connectionToken=wiGSRiNHdd7crhkcAMd%2FWy%2F3qGRZ5WdBm%2BdbR3b7aTbtpB8aaBGDil% 2fqawha6si5eeohsumcxau4pkefy%2bnoxog9fgyc4r666erxishybucsnlwo1ayh5zgdk7bfvme3e&messageId = d-800087-b%2c7d%7ce9%2c0%7ce_%2c0 443 - 104.210.116.149 signalr.client.net45 / 2.2.1.0 +(Microsoft + Windows + NT + 6.2.9200.0) - 200 0 0 0 11360

二○一六年十一月一十五日23时10分35秒POST / signalr /轮询clientProtocol = 1.4&运输= longPolling&connectionData = [%7B%22Name%22:%22MyHub%22%7D] connectionToken = hEJ1b0%2Bz2eeyC8IvYmOV3ffZ%2FAFQiQpEnJLUmCZTEVDLwcgOqhyQbQnu0R29sazp6BxcK4WsDhSbEdg2Sh4wMBSZjQtKMzASr2Fa2eY2HGgoVJcfDOMixQX2FCqfa%2BmP&MESSAGEID = d-80E0087-B%2C7D%7CFD%2C0%7CFE%2C0 443 - 104.210.116.149 SignalR.Client.NET45/2.2.1.0+(Microsoft+Windows+NT+6.2.9200.0) - 200 0 0 15798

二○一六年十一月一十五日23点10分35秒POST / signalr /轮询clientProtocol = 1.4&运输= longPolling&connectionData = [%7B%22Name%22:%22MyHub%22%7D] connectionToken = 2UsU63IHgaNO%2BBYmoamsKxFq7Vv3uaGigvR1NrGnntVnAbTg2C0%2BVXZnA9aT8siqpkBv%2Fo8avvvNTSBfQD77IspaO6jOnSU8rXMXDU2Vr6ojkWr% 2Fwt1LFsdNy3%2BHpDGC&messageId=d-80E0087-B%2C7D%7CEv%2C0%7CEw%2C0 443 - 104.210.116.149 SignalR.Client.NET45/2.2.1.0+(Microsoft+Windows+NT+6.2.9200.0) - 200 0 0 1184

等等等等

更新 - 显式停止集线器连接似乎已经处理了孤立客户端(或同一个 Web 作业客户端的多余客户端)。特别是添加 _hub.Stop();调用代理后。

【问题讨论】:

  • 轮询请求来自不同的客户端(每个客户端都有不同的connectionToken)。这仅意味着服务器在现有轮询请求上向客户端发送了消息,并且客户端必须重新建立新的轮询以获取新消息(如果有)。如果您想避免这些请求,请使用更高效的传输方式——最好是 websockets。 (有趣的是,客户端实际上正在运行长轮询。我可以想象它没有使用 websockets,因为它们默认被禁用,您可能忘记启用它们,但我不知道为什么它不使用 serverSentEvents)
  • 您是否尝试使触发函数异步并将 SendMessageToHub 方法签名中的 async void 更改为 async Task
  • 嗨,Thomas,对不起,我在这里编辑了代码示例。它实际上是:私有静态异步任务 SendMessageToHub(TextWriter log)
  • 嗨 Pawel,该网站肯定启用了网络套接字。我们大多数其他信号器流量都是通过 ws 完成的。但是,网络作业客户端似乎确实在进行长轮询。
  • 我开始认为我们可能需要在触发消息后对集线器连接执行 .Stop() 方法,以停止任何长轮询 - 因为客户端对接收任何内容都不感兴趣,只是触发消息。当网络作业完成时,信号器服务器不能收到断开信号......(?)

标签: iis signalr azure-webjobs azure-webjobssdk


【解决方案1】:

对于某些人来说,这听起来很明显,答案是确保我们在向它发送消息后停止集线器连接。下面是带有 cmets 包围的附加行的代码。

这似乎与浏览器客户端不同,客户端在一段时间不活动后断开连接。 webjob 客户端仍然存在,并继续轮询网站。因此,每次触发 webjob(通过计时器或通过从 azure 队列或主题中读取消息)时,它都会不断生成新客户端并保持该连接。

在 webjob 中,你必须明确地停止连接,否则内存和 CPU 会逐渐变废为宝。

private static async Task SendMessageToHub(TextWriter log)
{
        var hub = new HubConnection(CloudConfigurationManager.GetSetting("MyWebSite"));
        var proxy = _hub.CreateHubProxy("MyHub");

        log.WriteLine("WebJob Push: Sending message to SignalR Hub.");
        if (_hub.State == Microsoft.AspNet.SignalR.Client.ConnectionState.Disconnected)
        {
            await _hub.Start();
        }
        await _proxy.Invoke("BroadcastMessage");
        /////////////////////////////////////////////////////////////
        // Stopping the hub connection is necesssary in a web job  //
        _hub.Stop();   
        /////////////////////////////////////////////////////////////
        log.WriteLine("WebJob Push: Sent message to SignalR Hub.");
}

【讨论】:

  • 在停止连接后调用集线器方法可能是一个错误
  • 谢谢 Pawel,我只是在 * 中编辑帖子时将行放错了位置(尽管代码中正确:)已在此处的答案中修复它。