用于阻止重型站点流量的IIS模块

时间:2009-01-02 17:49:34

标签: .net asp.net iis module

问题

大家好,

关于我的问题的一些背景...我目前有一个为我工作的ISP构建的网站,它根据用户的帐单状态向用户显示消息。当他们处于非付费状态时,我会显示非付费消息,如果他们处于滥用状态,我会显示滥用消息等。流量由Cisco SCE生成,将最终用户的HTTP流量重定向到我的网站。

我看到的问题是流量过大。我相信流量可能是P2P流量,自动更新或其他任何类型。基本上任何使用端口80的东西都会被SCE重定向到我的页面。

我试图在我的服务器上强制执行的解决方案是放置一个模块,根据其命中数阻止用户。因此,如果它们在特定持续时间内超过阈值,它们将被重定向到另一个页面,这将有望将处理器的负载带走,因为它不必执行所有的SQL查找和智能。 ASP.NET页面。

但是,当我尝试强制执行我构建的模块时,实际上会产生相反的结果(增加CPU负载)。该模块使用存储在应用程序状态中的内存表,它用于通过IP跟踪请求。以下是该模块的代码:

public class IpHitCount : IHttpModule
{
    const string tableKey = "appIpLog";

    #region IHttpModule Members

    public void Dispose()
    {

    }

    public void Init(HttpApplication context)
    {
        context.PreRequestHandlerExecute += new EventHandler(checkHitCount);
    }

    #endregion

    private void checkHitCount(object sender, EventArgs e)
    {
        // Cast the parameter into a HttpApp object
        HttpApplication app = (HttpApplication)sender;

        // make sure that this is the user's first request for the app
        // (all first requests are routed through main)
        if (app.Request.Url.AbsolutePath.ToLower().Contains("main.aspx"))
        {
            // If the in memory table does not exist, then create it
            if (app.Application[tableKey] == null)
            {
                app.Application[tableKey] = CreateTable();
            }

            DataSet ds = (DataSet)app.Application[tableKey];
            DataTable tbl = ds.Tables["IpTable"];
            DeleteOldEntries(tbl);

            string filter = string.Format("ip = '{0}'", app.Request.UserHostAddress);
            DataRow[] matchedRows = tbl.Select(filter);

            if (matchedRows.Length > 0)
            {
                DataRow matchedRow = matchedRows[0];
                if ((int)matchedRow["hitCount"] > 4)
                {
                    app.Response.Redirect("HitCountExceeded.htm", true);
                }
                else
                {
                    matchedRow["hitCount"] = (int)matchedRow["hitCount"] + 1;
                }
            }
            else
            {
                DataRow newEntry = tbl.NewRow();
                newEntry["timestamp"] = DateTime.Now;
                newEntry["hitCount"] = 1;
                newEntry["ip"] = app.Request.UserHostAddress;
                tbl.Rows.Add(newEntry);
            }                
        }
    }

    private DataSet CreateTable()
    {
        DataSet ds = new DataSet();
        DataTable table = new DataTable("IpTable");

        DataColumn col1 = new DataColumn("timestamp", typeof(DateTime));
        col1.AutoIncrement = false;
        col1.DefaultValue = DateTime.Now;
        col1.ReadOnly = false;
        col1.Unique = false;

        DataColumn col2 = new DataColumn("ip", typeof(string));
        col1.AutoIncrement = false;
        col1.ReadOnly = false;  
        col1.Unique = false;

        DataColumn col3 = new DataColumn("hitCount", typeof(int));
        col1.AutoIncrement = false;
        col1.ReadOnly = false;
        col1.Unique = false;

        table.Columns.Add(col1);
        table.Columns.Add(col2);
        table.Columns.Add(col3);

        ds.Tables.Add(table);

        return ds;
    }

    private void DeleteOldEntries(DataTable tbl)
    {
        // build the where clause
        string filter = "timestamp < '" + DateTime.Now.AddMinutes(-5.0).ToString() + "'";

        // run the query against the table
        DataRow[] rowsToDelete = tbl.Select(filter);

        // individually delete each row returned
        foreach (DataRow row in rowsToDelete)
        {
            row.Delete();
        }
    }
}

所以我想知道的是:您是否可以看到我在模块中做错了,这可能导致高CPU利用率?是否有其他方法可以阻止此流量?

非常感谢您提供的任何帮助。

谢谢, ç


解决方案

我已将模块中的代码更改为每1分钟只运行一次删除部分:


    if (app.Application[deletedKey] == null)
    app.Application[deletedKey] = DateTime.Now;

    DateTime deletedDate = (DateTime)app.Application[deletedKey];

    if (DateTime.Now >= deletedDate.AddMinutes(1))
    {
        DeleteOldEntries(tbl);
        app.Application[deletedKey] = DateTime.Now;
    }

我还添加了一些代码,我相信索引我的数据集的IP列。虽然看起来不对,所以我不确定它是在做我打算做的事情:


    DataColumn[] key = new DataColumn[1];
    key[0] = col1;

    table.PrimaryKey = key;

    ds.Tables.Add(table);

进行上述两项更改后,CPU负载似乎急剧下降。我想我们的SQL服务器现在也感谢上帝,它终于可以呼吸了。

感谢您的帮助!!

2 个答案:

答案 0 :(得分:2)

好吧,你必须记住DataSet将在内存中,并且要搜索DataSet,它将花费大量的CPU周期来查找你正在寻找的记录。

除此之外,由于这是一个Web应用程序,因此您将获得大量的点击,因此您最终会非常频繁地调用此例程。

我的建议是将命中计数存储在数据库服务器中,然后更新并查询服务器以查看是否超过了命中计数。它将能够处理负载,并处理您要查询的数据集的大小。

答案 1 :(得分:1)

我尝试过几件事:

  • 我看到的第一件事是每次运行此代码时都会调用“DeleteOldEntries”子,这会导致它在每次传递时都会扫描整个DataTable。还有另一种方法可以将此限制为仅在特定时间运行吗?如果不是每15秒运行一次的计时器,则可能是状态中的第二个变量(如“ExecCount”),每次运行“CheckHitCount”时都会递增,这样您每隔10或20次清除一次?这样,您可以在每次运行时避免使用这个代码可能很昂贵的部分。
  • 另一个选择是为DataTable添加索引。我不确定.NET如何处理DataTables中的查找,但您可能对此感兴趣:MSDN Article

你可以使用像ANTS Profiler这样的东西来查看执行期间花费的时间最多吗?因为我认为这个页面被称为很多次,每次/秒,所以你可以用任何方式降低影响,即使是一点也会产生很大的不同。

如果您获得了一些结果但仍然不满意,请务必修改您的问题以添加新信息,这样我们就可以继续努力寻找您满意的解决方案。