Asp.net web api PushStreamContent流在负载下不刷新

Asp.net web api PushStreamContent流在负载下不刷新,asp.net-web-api,asp.net-web-api2,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/arch

我使用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;
    }
//松散地基于https://aspnetwebstack.codeplex.com/discussions/359056
//及http://blogs.msdn.com/b/henrikn/archive/2012/04/23/using-cookies-with-asp-net-web-api.aspx
公共类LiveController:ApicController
{
公共HttpResponseMessage获取(HttpRequestMessage请求)
{
如果(_timer==null)
{
//20秒计时器
_计时器=新计时器(TimerCallback,this,20000,20000);
}
//获取“?clientid=xxx”
HttpResponseMessage response=request.CreateResponse();
var kvp=request.GetQueryNameValuePairs()。其中(q=>q.Key.ToLower()==“clientid”).FirstOrDefault();
字符串clientId=kvp.Value;
HttpContext.Current.Response.ClientDisconnectedToken.Register(
委托(对象obj)
{
//客户端已完全断开连接
var disconnectedClientId=(字符串)obj;
CloseStreamFor(断开连接的客户端ID);
}
,clientId);
response.Content=新的PushStreamContent(
委托(流、HttpContent内容、TransportContext上下文)
{
SaveStreamFor(clientId,stream);
}
“文本/事件流”);
返回响应;
}
私有静态void CloseStreamFor(字符串clientId)
{
溪流;
_streams.TryRemove(clientId,out oldStream);
if(oldStream!=null)
oldStream.Close();
}
私有静态void SaveStreamFor(字符串clientId,Stream)
{
_streams.TryAdd(clientId,stream);
}
专用静态无效TimerCallback(对象obj)
{
DateTime start=DateTime.Now;
//禁用计时器
_timer.Change(Timeout.Infinite,Timeout.Infinite);
//每20秒向每个客户端发送一次心跳信号
var recipients=_streams.ToArray();
foreach(收件人中的var kvp)
{
字符串clientId=kvp.Key;
var stream=kvp.值;
尝试
{
// ***
//添加此跟踪语句并在调试器中运行会导致
//心跳被可靠地刷新!
// ***
WriteLine(string.Format(“**{0}:Timercallback:{1}”,DateTime.Now.ToString(“G”),clientId));
WriteHeartBeat(流);
}
捕获(例外情况除外)
{
CloseStreamFor(clientId);
}
}
//跟踪…(此跟踪语句无效)
_timer.Change(20000,20000);//重新启用计时器
}
私有静态void WriteHeartBeat(流)
{
WriteStream(流,“事件:心跳\n数据:-\n\n”);
}
私有静态void WriteStream(流、字符串数据)
{
byte[]arr=Encoding.ASCII.GetBytes(数据);
流写入(arr,0,arr.Length);
stream.Flush();
}
私有静态只读ConcurrentDictionary _streams=new ConcurrentDictionary();
专用静态定时器_定时器;
}
是否存在影响此操作的ASP.NET或IIS设置?我正在Windows Server 2008 R2上运行

更新:

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

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

更新2:

随后与Microsoft发生了两起支持事件,以下是结论:

1) 通过使用商务级互联网连接而不是家庭连接,解决了200个客户端的延迟问题

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

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


在VS调试器和Reflector中进行分析,stream.Flush什么都不做。传递给PushStreamContent委托的流是CompleteTaskOnCloseStream,它继承自DelegatingStream,DelegatingStream的内部流是HttpResponseStream,它委托HttpWriter执行写入。HttpWriter缓冲数据,不刷新。这已得到证实。确认冲洗不可靠。是否有解决方案?。整个警察局