C# COMET 服务器导致 IIS 崩溃

5
我正在尝试使用Comet进行实验。我开发了一个非常简单的聊天Web应用程序——基本上是通过C#实现Comet的“Hello World”。问题在于,IIS有时会崩溃,我的意思是它停止响应HTTP请求。然后需要很长时间才能重新启动应用程序池,有时甚至整个IIS服务都需要重启。我几乎可以确定罪魁祸首是ManualResetEvent对象,我使用它来阻塞Comet请求线程,直到收到释放(更新)这些线程的信号为止。我试着编写一个HTTP处理程序来解决这个问题,并将可重用属性设置为false(将新的请求线程放置在另一个ManualResetEvent对象的实例中),但这并没有起作用。我还尝试实现IRegisteredObject,以便在应用程序关闭时释放这些线程,但似乎也没有起作用。它仍然会崩溃,而且我没有注意到任何崩溃模式。我几乎可以确定是静态实例和ManualResetEvent的组合导致了这种情况。我只是不确定如何修复它。
Comet.cs(我的简单Comet库)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Mail;
using System.Web.Hosting;

namespace Comet
{
    public class CometCore : IRegisteredObject
    {
        #region Globals
        private static CometCore m_instance = null;
        private List<CometRequest> m_requests = new List<CometRequest>();
        private int m_timeout = 120000; //Default - 20 minutes;
        #endregion

        #region Constructor(s)
        public CometCore()
        {
            HostingEnvironment.RegisterObject(this);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Singleton instance of the class
        /// </summary>
        public static CometCore Instance
        {
            get
            {
                if (m_instance == null)
                    m_instance = new CometCore();
                return m_instance;
            }
        }

        /// <summary>
        /// In milliseconds or -1 for no timeout.
        /// </summary>
        public int Timeout { get { return m_timeout; } set { m_timeout = value; } }
        #endregion

        #region Public Methods
        /// <summary>
        /// Pauses the thread until an update command with the same id is sent.
        /// </summary>
        /// <param name="id"></param>
        public void WaitForUpdates(string id)
        {
            //Add this request (and thread) to the list and then make it wait.
            CometRequest request;
            m_requests.Add(request = new CometRequest(id));

            if (m_timeout > -1)
                request.MRE.WaitOne(m_timeout);
            else
                request.MRE.WaitOne();
        }

        /// <summary>
        /// Un-pauses the threads with this id.
        /// </summary>
        /// <param name="id"></param>
        public void SendUpdate(string id)
        {
            for (int i = 0; i < m_requests.Count; i++)
            {
                if (m_requests[i].ID.Equals(id))
                {
                    m_requests[i].MRE.Set();
                    m_requests.RemoveAt(i);
                    i--;
                }
            }
        }
        #endregion

        public void Stop(bool immediate)
        {
            //release all threads
            for (int i = 0; i < m_requests.Count; i++)
            {
                m_requests[i].MRE.Set();
                m_requests.RemoveAt(i);
                i--;
            }
        }
    }

    public class CometRequest
    {
        public string ID = null;
        public ManualResetEvent MRE = new ManualResetEvent(false);
        public CometRequest(string pID) { ID = pID; }
    }
}

我的聊天类和Web服务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using Comet;

namespace CometTest
{
    /// <summary>
    /// Summary description for Chat
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    [System.Web.Script.Services.ScriptService]
    public class Chat : System.Web.Services.WebService
    {

        [WebMethod]
        public string ReceiveChat()
        {
            return ChatData.Instance.GetLines();
        }

        [WebMethod]
        public string ReceiveChat_Comet()
        {
            CometCore.Instance.WaitForUpdates("chat");
            return ChatData.Instance.GetLines();
        }

        [WebMethod]
        public void Send(string line)
        {
            ChatData.Instance.Add(line);
            CometCore.Instance.SendUpdate("chat");
        }
    }

    public class ChatData
    {
        private static ChatData m_instance = null;
        private List<string> m_chatLines = new List<string>();
        private const int m_maxLines = 5;

        public static ChatData Instance
        {
            get
            {
                if (m_instance == null)
                    m_instance = new ChatData();
                return m_instance;
            }
        }

        public string GetLines()
        {
            string ret = string.Empty;
            for (int i = 0; i < m_chatLines.Count; i++)
            {
                ret += m_chatLines[i] + "<br>";
            }
            return ret;
        }

        public void Add(string line)
        {
            m_chatLines.Insert(0, line);
            if (m_chatLines.Count > m_maxLines)
            {
                m_chatLines.RemoveAt(m_chatLines.Count - 1);
            }
        }
    }
}

测试的aspx文件
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CometTest.Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">

        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Chat.asmx" />
            </Services>
        </asp:ScriptManager>

        <div id="lyrChatLines" style="height: 200px; width: 300px; border: 1px solid #cccccc; overflow: scroll">
        </div>

        <asp:Panel runat="server" DefaultButton="cmdSend">
            <asp:UpdatePanel runat="server">
                <ContentTemplate>
                    <asp:TextBox style="width: 220px" runat="server" ID="txtChat"></asp:TextBox>
                    <asp:Button runat="server" ID="cmdSend" Text="Send" OnClick="cmdSend_Click" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </asp:Panel>

        <script type="text/javascript">

            function CometReceive()
            {
                CometTest.Chat.ReceiveChat_Comet(receive, commError, commError);
            }

            function ReceiveNow()
            {
                CometTest.Chat.ReceiveChat(receive, commError, commError);
            }

            function receive(str)
            {
                document.getElementById("lyrChatLines").innerHTML = str;
                setTimeout("CometReceive()", 0);
            }

            function commError()
            {
                document.getElementById("lyrChatLines").innerHTML =
                    "Communication Error...";
                setTimeout("CometReceive()", 5000);
            }

            setTimeout("ReceiveNow()", 0);
        </script>
    </form>
</body>
</html>

还有aspx页面的后端代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CometTest
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void cmdSend_Click(object sender, EventArgs e)
        {
            Chat service = new Chat();
            service.Send
            (
                Request.UserHostAddress + "> " +
                txtChat.Text
            );
            txtChat.Text = string.Empty;
            txtChat.Focus();
        }
    }
}

如果有人对似乎任意崩溃的原因和/或修复方法有好的理论,如果你能发帖分享就非常感激 :)
1个回答

1

这个问题.NET Comet engine有一些链接可以指引你朝正确的方向前进。你需要考虑实现一个IHttpAsyncHandler来处理长时间运行的comet请求。


谢谢!我会尽快尝试一下。我写了一个http处理程序来解决这个问题,但我是实现的IHttpHandler(而不是异步),所以我肯定会尝试一下。您认为是否可能在不创建HTTP处理程序的情况下完成这项任务?我之所以这样问,是因为如果可以开发一个简单的库,在像我上面提到的web服务中很容易使用,那就太好了。 - Stewart Anderson
1
我没有使用过Web服务,所以无法发表评论。但是,我已经使用IHttpAsyncHandler开发了一个Comet服务器,并且它运行良好。IIS/http.sys使用固定数量的线程来处理传入请求,如果您使用长时间运行或阻塞(.WaitOne)调用来占用它们,您很快就会使iis崩溃。 - snoopy-do
我也会尝试使用异步网络方法。 - Stewart Anderson
异步HTTP处理程序可以解决这个问题!我认为无法从客户端(通过JavaScript)调用异步web方法,因此该方法将不起作用,但是异步http处理程序可以。唯一的问题是它使向客户端发送数据变得有点不太方便。哦,好吧,它是基于彗星通信机制的,它可以正常工作,似乎永远不会崩溃。如果有人发现了使用异步web方法的方法,请告诉我! - Stewart Anderson
如果您将页面方法标记为可由脚本调用,那么脚本可以调用该页面方法,就像调用服务一样。与服务不同,在页面方法中,您可以使用Page.RegisterAsyncTask,它应该可以满足您的需求。 - eselk
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接