9

背景(このセクションはスキップできます)

数百台のマシンで最新の状態に保つ必要がある大量のデータ(約3 mb)があります。一部のマシンはC#を実行し、一部のマシンはJavaを実行します。データはいつでも変更される可能性があり、数分以内にクライアントに伝達する必要があります。データは、4つの負荷分散サーバーからJson形式で配信されます。これらの4つのサーバーは、Mvc 3およびC#4.0でASP.NET4.0を実行しています。

4台のサーバーで実行されるコードには、Json応答をハッシュしてから、ハッシュを文字列に変換するハッシュアルゴリズムがあります。このハッシュはクライアントに渡されます。次に、数分ごとに、クライアントはハッシュを使用してサーバーにpingを実行し、ハッシュが古くなっている場合は新しいJsonオブジェクトが返されます。ハッシュがまだ最新の場合は、本文が空の304が返されます。

時折、4つのボックスによって生成されたハッシュがボックス間で一貫していないことがあります。これは、クライアントが常にデータをダウンロードしていることを意味します(各要求が異なるサーバーにヒットする可能性があります)。

コードスニペット

ハッシュを生成するために使用されるコードは次のとおりです。

internal static HashAlgorithm Hasher { get; set; }
...
Hasher = new SHA1Managed();
...
Convert.ToBase64String(Hasher.ComputeHash(Encoding.ASCII.GetBytes(jsonString)));

問題をデバッグするために、次のように分割しました。

Prehash = PreHashBuilder.ToString();
ASCIIBytes = Encoding.ASCII.GetBytes(Prehash);
HashedBytes = Hasher.ComputeHash(ASCIIBytes);
Hash = Convert.ToBase64String(HashedBytes);

次に、上記の値を吐き出すルートを追加し、BeyondCompareを使用して違いを比較しました。

バイト配列は、以下を使用してBeyondCompareで使用するために文字列形式に変換されます。

private static string GetString(byte[] bytes)
{
    StringBuilder sb = new StringBuilder();
    foreach (byte b in bytes)
    {
        sb.Append(b);
    }
    return sb.ToString();
} 

ご覧のとおり、バイト配列はバイトのシーケンスとして文字通り表示されます。「変換」されません。

問題

PrehashとASCIIBytesの値は同じであることがわかりましたが、HashedBytesの値が異なっていました。つまり、ハッシュも異なっていました。

4つのサーバーボックスでIISWebサイトを数回再起動し、ハッシュが異なる場合は、BeyondCompareの値を比較しました。いずれの場合も、異なるのは「HashedBytes」値でした(SHA1Managed.ComputeHash(...)の結果)

質問

私は何が間違っているのですか?ComputeHash関数への入力は同じです。SHA1Managedマシンは依存していますか?4台のマシンの半分の時間は同じハッシュを持っているので、それはうまくいきません。

StackOverFlowとBingを検索しましたが、この問題を抱えている人を見つけることができませんでした。私が見つけた最も近いものは彼らのエンコーディングに問題がある人々でした、しかし私はエンコーディングが問題ではないことを証明したと思います。

出力

私はそれが長いのでここにすべてを捨てないことを望んでいました、しかしここに私が比較しているダンプのスニペットがあります:

Hash:o1ZxBaVuU6OhE6De96wJXUvmz3M =
HashedBytes:163861135165110831631611916022224717299375230207115
ASCIIBytes:.... Prehash:.. ..

異なるサーバー上の2つのページを比較すると、ASCIIバイトは同一ですが、HashedBytesは同一ではありません。私がバイトに使用するダンプメソッドは変換を行わず、各バイトを順番にダンプするだけです。'。'でバイトを区切ることができます。私は考えます。

フォローアップ b.ToString(CultureInfo.InvariantCulture)を変更し、HashAlgorithmを静的プロパティではなくローカル変数にしました。コードがサーバーにデプロイされるのを待っています。

4

3 に答える 3

13

問題を再現しようとしましたが、SHA1Managedプロパティをグローバル静的ではなくローカル変数にすると、再現できませんでした。

問題はマルチスレッドにありました。静的とマークしたSHA1Managedクラスを除いて、私のコードはスレッドセーフでした。SHA1Managed.ComputeHashはその下でスレッドセーフであると想定しましたが、内部静的とマークされている場合は明らかにそうではありません。

繰り返しますが、SHA1Managed.ComputeHashは、internal staticとマークされている場合、スレッドセーフではありません。

MSDNの状態:

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

内部静的がパブリック静的とは異なる動作をする理由がわかりません。

@pstを回答としてマークし、問題を明確にするためにコメントを追加しますが、@ pstがコメントを作成したため、回答としてマークできません。

ご入力いただきありがとうございます。

于 2012-09-28T18:36:33.313 に答える
0

StringBuilder.Append(byte)がbyte.ToString(CultureInfo.CurrentCulture)を呼び出すため、GetStringメソッドは異なるカルチャのマシンで異なる結果を生成する可能性があります。試す

private static string GetString(byte[] bytes)
{
    StringBuilder sb = new StringBuilder();
    foreach (byte b in bytes)
    {
        sb.Append(b.ToString(CultureInfo.InvariantCulture));
    }
    return sb.ToString();
} 

ただし、バイト値の10進文字列表現を使用しない方法を使用する方が適切です。

于 2012-09-28T17:40:59.090 に答える
0

問題は、コードが先行ゼロを混乱させている可能性があることです。比較するコードを文字列に配列するために、以下を使用してください。信頼できる結果が得られ、バイト配列を文字列に変換してマシン間で送信できるように特別に設計されています。

using System.Runtime.Remoting.Metadata.W3cXsd2001;

public byte[] StringToBytes(string value)
{
    SoapHexBinary soapHexBinary = SoapHexBinary.Parse(value);
    return soapHexBinary.Value;
}

public string BytesToString(byte[] value)
{
    SoapHexBinary soapHexBinary = new SoapHexBinary(value);
    return soapHexBinary.ToString();
}

また、JSONに微妙な違いがないことを確認することをお勧めします。これにより、完全に異なるハッシュが作成されます。たとえば、一部の文化では、「千六百七」という数字を、、、またはさらにも表し1,600.7ます1 000.71 600,7このウィキペディアのページを参照してください)。

于 2012-09-28T17:56:30.120 に答える