大量のサイトトラフィックをブロックするための IIS モジュール
質問
質問
こんにちは皆さん、
私の問題の背景を少し説明します...私は現在、請求ステータスに基づいてユーザーにメッセージを表示するサイトを、勤務している 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 サーバーも、ようやく息を吹き返すことができて、神に感謝しているのではないかと思います。
ご協力ありがとうございました!!
解決
試してみたいことがいくつかあります。
- 最初に目に入るのは、このコードが実行されるたびに「DeleteOldEntries」サブルーチンを呼び出していることです。これにより、パスごとに DataTable 全体のスキャンが実行されます。これを特定の時間にのみ実行するように制限できる別の方法はありますか?15 秒ごとに実行するタイマーではない場合は、「CheckHitCount」が実行されるたびに増加する状態の 2 番目の変数 (「ExecCount」など) を使用して、10 回または 20 回ごとにパージするだけでよいでしょうか。こうすることで、実行のたびに、コストがかかる可能性のあるコードのセクションを回避できます。
- もう 1 つのオプションは、DataTable にインデックスを追加することです。.NET が DataTable のルックアップをどのように処理するかはわかりませんが、おそらくこれはあなたにとって興味深いものになるでしょう。 MSDN 記事
ANTS Profiler のようなものを使用して、実行中に最も多くの時間が費やされた場所を確認できますか?このページは毎秒何度も呼び出されると思われるため、影響を少しでも軽減できる方法であれば大きな違いになります。
何らかの結果が得られたものの、まだ満足できない場合は、満足のいく解決策を目指して作業を続けることができるように、必ず質問を変更して新しい情報を追加してください。
他のヒント
まあ、DataSetはメモリ内にあることを覚えておく必要があり、DataSetを検索するには、探しているレコードを見つけるために多くのCPUサイクルが必要になります。
これに加えて、これはWebアプリケーションであるため、多くのヒットを取得するため、非常に頻繁にこのルーチンを呼び出すことになります。
推奨事項は、ヒットカウントをデータベースサーバーに保存し、サーバーを更新およびクエリして、ヒットカウントを超えているかどうかを確認することです。負荷を処理できるだけでなく、クエリ対象のデータセットのサイズも処理できます。