0

質問

皆さんこんにちは、

私の問題の背景を少し説明します... 現在、私が働いている ISP 用に構築されたサイトがあり、請求状況に基づいてユーザーにメッセージを表示します。非有料の場合は非有料メッセージを表示し、不正使用の場合は不正使用のメッセージなどを表示します。トラフィックは、エンド ユーザの HTTP トラフィックをサイトにリダイレクトする Cisco SCE によって生成されます。

私が見ている問題は、過剰なトラフィックです。トラフィックは、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);

上記の 2 つの変更を行った後、CPU 負荷は劇的に減少したようです。私たちの SQL サーバーも、ようやく息ができるようになったことを神に感謝していると思います。

助けてくれてありがとう!!

4

2 に答える 2

2

DataSet はメモリ内にあることを覚えておく必要があります。DataSet を検索するには、探しているレコードを見つけるのに多くの CPU サイクルが必要です。

それに加えて、これは Web アプリケーションであるため、多くのヒットが発生するため、このルーチンを非常に頻繁に呼び出すことになります。

ヒット カウントをデータベース サーバーに格納し、サーバーを更新してクエリを実行し、ヒット カウントを超えているかどうかを確認することをお勧めします。負荷を処理できるだけでなく、クエリを実行するデータ セットのサイズも処理できます。

于 2009-01-02T17:59:29.327 に答える
1

私が試してみたいことがいくつかあります:

  • 最初に目にするのは、このコードが実行されるたびに「DeleteOldEntries」サブルーチンを呼び出していることです。これにより、パスごとに DataTable 全体をスキャンします。これを特定の時間にのみ実行するように制限できる別の方法はありますか? 15 秒ごとに実行するタイマーではない場合は、「CheckHitCount」が実行されるたびにインクリメントする状態の 2 番目の変数 (「ExecCount」など) を使用して、10 回または 20 回ごとにのみパージするようにします。このようにして、実行のたびにコードのこの潜在的に高価なセクションを回避できます。
  • もう 1 つのオプションは、DataTable にインデックスを追加することです。.NET が DataTables のルックアップをどのように処理するかはわかりませんが、おそらくこれは興味深いでしょう: MSDN の記事

ANTS Profiler のようなものを使用して、実行中に最も時間が費やされている場所を確認できますか? このページは毎秒何回も呼び出されていると思うので、影響を少しでも減らすことができれば、大きな違いが生まれます。

いくつかの結果が得られても満足できない場合は、質問を変更して新しい情報を追加してください。これにより、満足のいく解決策に向けて作業を続けることができます。

于 2009-01-02T20:51:57.437 に答える