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

Posted

技术标签:

【中文标题】用于阻止大量站点流量的 IIS 模块【英文标题】:IIS Module for Blocking Heavy Site Traffic 【发布时间】:2010-09-29 06:47:21 【问题描述】:

问题

大家好,

关于我的问题的一些背景知识...我目前有一个为我工作的 ISP 构建的站点,该站点根据用户的帐单状态向用户显示消息。当他们处于非付费状态时,我会显示一条非付费消息,如果他们处于滥用状态,我会显示一条滥用消息等。流量由思科 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 利用率高?是否有其他方法可以阻止此流量?

您能提供的任何帮助将不胜感激。

谢谢, C


解决方案

我已将模块中的代码更改为仅每 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 服务器现在也正在感谢上帝,它终于可以呼吸了。

感谢大家的帮助!!

【问题讨论】:

我不太了解将 Windows 用作服务器,但这不是您的 Web 服务器和/或防火墙应该能够共同处理的事情吗? 【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

有几件事我会尝试:

我看到的第一件事是,每次运行此代码时,您都会调用“DeleteOldEntries”子程序,这会导致它在每次通过时扫描整个 DataTable。有没有另一种方法可以限制它只在特定时间运行?如果不是每 15 秒运行一次的计时器,那么可能是状态中的第二个变量(如“ExecCount”),每次运行“CheckHitCount”时都会递增,这样您就只能每 10 次或 20 次清除一次?这样一来,您就可以避免每次运行时都可能出现成本高昂的代码部分。 另一个选项是向您的数据表添加索引。我不确定 .NET 如何处理 DataTables 中的查找,但也许您会对此感兴趣:MSDN Article

您能否使用 ANTS Profiler 之类的工具来查看执行期间花费最多的时间?因为我想这个页面每秒被调用很多很多次,任何可以降低影响的方式都会产生很大的不同。

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

【讨论】:

以上是关于用于阻止大量站点流量的 IIS 模块的主要内容,如果未能解决你的问题,请参考以下文章

Varnish 用于阻止拒绝服务攻击

将站点转移到 SSL 后,跨域请求被 Ajax 阻止

我可以在 NGINX 中执行 GeoIP 阻止而无需为 NGINX Plus 付费吗?

git被阻止,如何安装npm模块

谷歌云防火墙是不是默认阻止子网间流量?

IIS 服务器阻止访问 .webp 文件