PushStreamContent流不会在加载时刷新

时间:2014-07-04 00:51:00

标签: asp.net-web-api asp.net-web-api2

我正在使用PushStreamContent来保持与每个客户端的持久连接。每20秒向每个客户端流推送短心跳消息对100个客户端很有效,但在大约200个客户端,客户端首先开始接收它几秒延迟,然后它根本不显示。

我的控制器代码是

    // Based loosely on https://aspnetwebstack.codeplex.com/discussions/359056
    // and http://blogs.msdn.com/b/henrikn/archive/2012/04/23/using-cookies-with-asp-net-web-api.aspx
    public class LiveController : ApiController
    {
        public HttpResponseMessage Get(HttpRequestMessage request)
        {
            if (_timer == null)
            {
                // 20 second timer
                _timer = new Timer(TimerCallback, this, 20000, 20000);    
            }

            // Get '?clientid=xxx'
            HttpResponseMessage response = request.CreateResponse();
            var kvp = request.GetQueryNameValuePairs().Where(q => q.Key.ToLower() == "clientid").FirstOrDefault();
            string clientId = kvp.Value; 

            HttpContext.Current.Response.ClientDisconnectedToken.Register(
                                                                    delegate(object obj)
                                                                    {
                                                                        // Client has cleanly disconnected
                                                                        var disconnectedClientId = (string)obj;
                                                                        CloseStreamFor(disconnectedClientId);
                                                                    }
                                                                    , clientId);

            response.Content = new PushStreamContent(
                                    delegate(Stream stream, HttpContent content, TransportContext context)
                                    {
                                        SaveStreamFor(clientId, stream);
                                    }
                                    , "text/event-stream");


            return response;
        }

        private static void CloseStreamFor(string clientId)
        {
            Stream oldStream;
            _streams.TryRemove(clientId, out oldStream);
            if (oldStream != null)
                oldStream.Close();
        }

        private static void SaveStreamFor(string clientId, Stream stream)
        {
            _streams.TryAdd(clientId, stream);
        }


        private static void TimerCallback(object obj)
        {
            DateTime start = DateTime.Now;

            // Disable timer
            _timer.Change(Timeout.Infinite, Timeout.Infinite);      

            // Every 20 seconds, send a heartbeat to each client
            var recipients = _streams.ToArray();
            foreach (var kvp in recipients)
            {
                string clientId = kvp.Key;
                var stream = kvp.Value;
                try
                {
                    // ***
                    // Adding this Trace statement and running in debugger caused
                    // heartbeats to be reliably flushed!
                    // ***
                    Trace.WriteLine(string.Format("** {0}: Timercallback: {1}", DateTime.Now.ToString("G"), clientId));

                    WriteHeartBeat(stream);
                }
                catch (Exception ex)
                {
                    CloseStreamFor(clientId);
                }
            }

           // Trace... (this trace statement had no effect) 
            _timer.Change(20000, 20000);      // re-enable timer
        }

        private static void WriteHeartBeat(Stream stream)
        {
            WriteStream(stream, "event:heartbeat\ndata:-\n\n");
        }


        private static void WriteStream(Stream stream, string data)
        {
            byte[] arr = Encoding.ASCII.GetBytes(data);
            stream.Write(arr, 0, arr.Length);
            stream.Flush();
        }

        private static readonly ConcurrentDictionary<string, Stream> _streams = new ConcurrentDictionary<string, Stream>();
        private static Timer _timer;
    }

是否会有一些影响此问题的ASP.NET或IIS设置?我在Windows Server 2008 R2上运行。

更新:

如果1)添加Trace.WriteLine语句,2)附加Visual Studio 2013调试器并调试和捕获Trace.WriteLines,则可靠地发送心跳。

这两者都是必要的;如果删除了Trace.WriteLine,则在调试器下运行无效。如果Trace.WriteLine在那里,但程序没有在调试器下运行(而是SysInternals的DbgView显示跟踪消息),心跳是不可靠的。

更新2:

稍后与微软发生两次支持事件,结论如下:

1)通过使用业务类Internet连接而不是Home连接

解决了200个客户端的延迟

2)调试器是否附加确实没有任何区别;

3)需要对web.config进行以下两次添加,以确保及时发送心跳,并且由于客户端“不清”地断开连接而导致心跳失败(例如,通过拔出计算机而非正常关闭程序,干净地发出TCP RST)触发器及时的ClientDisconnected回调:

<httpRuntime executionTimeout="5" />

<serverRuntime appConcurrentRequestLimit="50000" uploadReadAheadSize="1" frequentHitThreshold="2147483647" />

0 个答案:

没有答案