Domanda

Domanda

Ciao a tutti,

Un po 'di storia sul mio problema ... Al momento ho un sito creato per l'ISP per il quale lavoro che visualizza i messaggi agli utenti in base al loro stato di fatturazione. Quando sono in modalità Non-Pay visualizzo un messaggio Non-Pay e se sono in Abuso, visualizzo un messaggio di abuso, ecc. Il traffico viene generato da un Cisco SCE che reindirizza il traffico HTTP dell'utente finale al mio sito.

Il problema che vedo è un traffico eccessivo. Credo che il traffico potrebbe essere traffico P2P, aggiornamenti automatici o qualsiasi altra cosa del genere. Fondamentalmente tutto ciò che utilizza la porta 80 viene reindirizzato dalla SCE alla mia pagina.

La soluzione che sto cercando di imporre sul mio server è quella di mettere in atto un modulo che blocchi gli utenti in base al numero di accessi. Quindi, se superano una soglia per un certo periodo di tempo, verranno reindirizzati a un'altra pagina che, si spera, eliminerà il carico dal processore poiché non dovrà eseguire tutte le ricerche e le informazioni SQL che avvengono nel Pagina ASP.NET.

Tuttavia, quando provo a far rispettare un modulo che ho creato, in realtà ha il risultato opposto (aumenta il carico della CPU). Il modulo utilizza una tabella in memoria archiviata nello stato dell'applicazione che utilizza per tenere traccia delle richieste tramite IP. Ecco il codice per il modulo:

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();
        }
    }
}

Quindi quello che mi chiedo è il seguente: c'è qualcosa che vedi che sto facendo di sbagliato nel modulo, che potrebbe causare un elevato utilizzo della CPU? Esiste un modo alternativo per bloccare questo traffico?

Qualsiasi aiuto che puoi fornire sarebbe molto apprezzato.

Grazie, C


Soluzione

Ho modificato il codice nel modulo per eseguire solo la sezione di eliminazione ogni 1 minuto:


    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;
    }

Ho anche aggiunto del codice che credo indicizza la colonna IP del mio set di dati. Non sembra giusto, quindi non sono sicuro che stia facendo ciò che intendo fare:


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

    table.PrimaryKey = key;

    ds.Tables.Add(table);

Dopo aver apportato le due modifiche precedenti, il carico della CPU sembra essere diminuito drasticamente. Immagino che il nostro server SQL stia ringraziando anche Dio ora che può finalmente respirare.

Grazie per tutto l'aiuto !!

È stato utile?

Soluzione

Ci sono un paio di cose che proverei:

  • La prima cosa che vedo è che stai chiamando " DeleteOldEntries " sub ogni volta che questo codice viene eseguito, il che gli fa eseguire una scansione dell'intera DataTable ad ogni passaggio. C'è un altro modo in cui potresti limitare questo per funzionare solo in determinati momenti? Se non un timer che lo esegue ogni 15 secondi, allora forse una seconda variabile nello stato (come "ExecCount") che si incrementa ogni volta che "CheckHitCount" viene eseguito, in modo da eliminare ogni 10 o 20 volte? In questo modo, puoi evitare questa sezione potenzialmente costosa del codice ad ogni esecuzione.
  • Un'altra opzione è l'aggiunta di un indice al DataTable. Non sono sicuro di come .NET gestisca le ricerche in DataTables, ma forse questo potrebbe interessarti: Articolo MSDN

Puoi usare qualcosa come ANTS Profiler per vedere dove trascorre più tempo durante l'esecuzione? Dato che immagino che questa pagina venga chiamata molte, molte volte / secondo, in ogni modo puoi ridurre l'impatto anche se un po 'farebbe una grande differenza.

Se ottieni alcuni risultati ma non sei ancora soddisfatto, assicurati di modificare la tua domanda per aggiungere le nuove informazioni in modo che possiamo continuare a lavorare per una soluzione di cui sei soddisfatto.

Altri suggerimenti

Bene, devi ricordare che il DataSet sarà in memoria, e per cercare nel DataSet, ci vorranno molti cicli della CPU per trovare i record che stai cercando.

Aggiungete a ciò il fatto che poiché si tratta di un'applicazione Web, otterrete molti risultati, quindi finirete per chiamare questa routine molto, molto spesso.

La mia raccomandazione sarebbe quella di memorizzare il conteggio degli hit in un server di database e quindi aggiornare e interrogare il server per vedere se il conteggio degli hit viene superato. Sarà in grado di gestire il carico, nonché gestire le dimensioni del set di dati che si intende interrogare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top