Модуль IIS для блокировки интенсивного трафика сайта

StackOverflow https://stackoverflow.com/questions/407536

  •  03-07-2019
  •  | 
  •  

Вопрос

Вопрос

Всем здравствуйте,

Немного предыстории моего вопроса...В настоящее время у меня есть сайт, созданный для интернет-провайдера, на которого я работаю, который отображает сообщения пользователям в зависимости от их статуса оплаты.Когда они находятся в режиме «Неплатёж», я показываю сообщение «Неплатёж», а если они находятся в режиме «Злоупотребление», я показываю сообщение о нарушении и т. д.Трафик генерируется Cisco SCE, который перенаправляет HTTP-трафик конечного пользователя на мой сайт.

Проблема, которую я вижу, - это чрезмерный трафик.Я полагаю, что трафик может быть P2P-трафиком, автоматическими обновлениями или чем-то еще в этом роде.По сути, все, что использует порт 80, перенаправляется SCE на мою страницу.

Решение, которое я пытаюсь реализовать на своем сервере, состоит в том, чтобы установить модуль, который блокирует пользователей на основе их количества обращений.Таким образом, если они превысят пороговое значение в течение определенного периода времени, они будут перенаправлены на другую страницу, что, как мы надеемся, снимет нагрузку с процессора, поскольку ему не придется выполнять все запросы SQL и анализ, которые происходят в Страница ASP.NET.

Однако когда я пытаюсь применить созданный мною модуль, на самом деле результат оказывается противоположным (увеличивается загрузка процессора).Модуль использует таблицу в памяти, хранящуюся в состоянии приложения, которую он использует для отслеживания запросов по 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();
        }
    }
}

Итак, меня интересует следующее:Видите ли вы что-то, что я делаю неправильно в модуле, что может быть причиной высокой загрузки ЦП?Есть ли альтернативный способ заблокировать этот трафик?

Любая помощь, которую вы можете оказать, будет очень признательна.

Спасибо, 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);

После внесения двух вышеуказанных изменений нагрузка на процессор, похоже, резко снизилась.Я полагаю, что наш SQL-сервер теперь тоже благодарит Бога за то, что наконец-то может дышать.

Спасибо за всю помощь!!

Это было полезно?

Решение

Есть пара вещей, которые я бы попробовал:

  • Первое, что я вижу, это то, что вы вызываете подпрограмму «DeleteOldEntries» каждый раз, когда запускается этот код, что заставляет его выполнять сканирование всего DataTable при каждом проходе.Есть ли другой способ ограничить это запуском только в определенное время?Если не таймер, который запускает его каждые 15 секунд, то, возможно, вторая переменная в состоянии (например, «ExecCount»), которая увеличивается каждый раз при запуске «CheckHitCount», чтобы вы очищали только каждый 10-й или 20-й раз?Таким образом, вы сможете избежать этого потенциально дорогостоящего участка кода при каждом запуске.
  • Другой вариант — добавить индекс в ваш DataTable.Я не уверен, как .NET обрабатывает поиск в DataTables, но, возможно, это вас заинтересует: Статья MSDN

Можете ли вы использовать что-то вроде ANTS Profiler, чтобы увидеть, на что тратится больше всего времени во время выполнения?Поскольку я предполагаю, что эту страницу вызывают много-много раз в секунду, любой способ хоть немного снизить воздействие будет иметь большое значение.

Если вы получили некоторые результаты, но по-прежнему недовольны, обязательно измените свой вопрос, добавив новую информацию, чтобы мы могли продолжать работать над решением, которое вас устроит.

Другие советы

Что ж, вы должны помнить, что DataSet будет находиться в памяти, и для поиска в DataSet потребуется много циклов процессора, чтобы найти нужные записи.

Добавьте к этому тот факт, что, поскольку это веб-приложение, вы получите много обращений, поэтому вам придется вызывать эту процедуру очень и очень часто.

Я бы рекомендовал хранить количество обращений на сервере базы данных, а затем обновлять и запрашивать сервер, чтобы узнать, превышено ли количество обращений.Он сможет справиться с нагрузкой, а также с размером набора данных, который вы собираетесь запросить.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top