2011-09-16 19 views
2

私はMongoDBの最新バージョン(Windows 64 Server上)とC#ドライバを使用しています。私は800をやっているWindowsサービスを読み込んで、毎分アップデートがあり、数分後に使用されている現在のスレッドが200以上になると、その後すべての単一のMongoDBの呼び出しは、このエラーを与える:MongoDB C#ドライバは接続を解除してからエラーを返しません

System.IO.IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 
    at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) 
    at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size) 

私は上のインデックスを持っていますそれが問題ではないので、読んでいるフィールド。ここでは、読み取りのためのコードは次のとおりです。

public static UserUpdateMongo Find(int userId, long deviceId) 
{ 
    return Collection().Find(
     Query.And(
      Query.EQ("UserId", userId), 
      Query.EQ("DeviceId", deviceId))).FirstOrDefault(); 
} 

私はそうのような接続をインスタンス化します。

var settings = new MongoServerSettings 
{ 
    Server = new MongoServerAddress(segments[0], Convert.ToInt32(segments[1])),MaxConnectionPoolSize = 1000}; 
    Server = MongoServer.Create(settings); 
} 

私が何か間違ったことやC#のドライバの問題がありますかな?助けて!!

答えて

4

C#ドライバには接続プールがあり、接続プールの最大サイズはデフォルトで100になっています。したがって、単一のC#クライアントプロセスから100を超える接続をmongodに見られることはありません。 C#ドライバの1.1バージョンでは、重い負荷の下で時折問題が発生しました.1つの接続でエラーが発生すると、接続が切断されて接続される可能性があります。接続がオープンまたはクローズされるたびにログ・エントリーが書き込まれるサーバー・ログを見ることで、それが起こっているかどうかを知ることができます。もしそうなら、今週リリースされた1.2 C#ドライバを試すことができますか?

保留中の更新のキューを作成する必要はありません。接続プールは、並行要求の数を制限することによって、並べ替えのキューとして機能します。

サーバーのログに何かを見つけることができたらお知らせします。さらに何かがある場合は、私にお手伝いしてください。

+1

私は接続が100に達するまで接続が増え続けるため、すべての要求が「転送接続からデータを読み取れません」というエラーが発生するため、接続プールのサイズが100であることがわかりました。私は200までの限界を増やそうとしましたが、同じことが起こりました。接続は200まで増え、再びエラーになります。しかし、エラーのために接続が増えていないため、C#ドライバが処理するにはあまりにも多く(一度設定された制限に達すると)なったら、ただちにビルドされ、解放されずにエラーになります。 – Justin

+1

私はMongoサーバオブジェクトはスレッドセーフであり、同時リクエストのキューのように動作するはずですが、実際には重大な負荷を処理できません。ああ、あなたの応答のおかげで、私は1.2ドライバを試してみますが、一度接続が高いと上記のトランスポートエラー以外にエラーが発生していないので、何の効果もないように思えます。 – Justin

+0

実際に私はちょうどチェックして、私はすでに1.2.0.4274を使用しています。 – Justin

2

解決策は、個々のスレッドごとにレコードを保存しないようにし、それらをメモリの「保留中」リストに追加することでした。次に、別のスレッドを持っていて、mongodbへのすべてのセーブを同期して処理します。なぜ私は非同期呼び出しがC#ドライバをトリップさせるのか分かりませんが、今はきれいに動作しています。

public static class UserUpdateSaver 
    { 
     public static List<UserUpdateView> PendingUserUpdates; 

     public static void Initialize() 
     { 
      PendingUserUpdates = new List<UserUpdateView>(); 
      var saveUserUpdatesTime = Convert.ToInt32(ConfigurationBL.ReadApplicationValue("SaveUserUpdatesTime")); 
      LogWriter.Write("Setting up timer to save user updates every " + saveUserUpdatesTime + " seconds", LoggingEnums.LogEntryType.Warning); 
      var worker = new BackgroundWorker(); 
      worker.DoWork += delegate(object s, DoWorkEventArgs args) 
      { 
       while (true) 
       {//process pending user updates every x seconds. 
        Thread.Sleep(saveUserUpdatesTime * 1000); 
        ProcessPendingUserUpdates(); 
       } 
      }; 
      worker.RunWorkerAsync(); 
     } 

     public static void AddUserUpdateToSave(UserUpdateView userUpdate) 
     { 
      Monitor.Enter(PendingUserUpdates); 
      PendingUserUpdates.Add(userUpdate); 
      Monitor.Exit(PendingUserUpdates); 
     } 

     private static void ProcessPendingUserUpdates() 
     { 
      //get pending user updates. 
      var pendingUserUpdates = new List<UserUpdateView>(PendingUserUpdates); 
      if (pendingUserUpdates.Count > 0) 
      { 
       var startDate = DateTime.Now; 

       foreach (var userUpdate in pendingUserUpdates) 
       { 
        try 
        { 
         UserUpdateStore.Update(userUpdate); 
        } 
        catch (Exception exc) 
        { 
         LogWriter.WriteError(exc); 
        } 
        finally 
        { 
         Monitor.Enter(PendingUserUpdates); 
         PendingUserUpdates.Remove(userUpdate); 
         Monitor.Exit(PendingUserUpdates); 
        } 
       } 

       var duration = DateTime.Now.Subtract(startDate); 
       LogWriter.Write(String.Format("Processed {0} user updates in {1} seconds", 
        pendingUserUpdates.Count, duration.TotalSeconds), LoggingEnums.LogEntryType.Warning); 
      } 
      else 
      { 
       LogWriter.Write("No user updates to process", LoggingEnums.LogEntryType.Warning); 
      } 
     } 
    } 
+1

これはうまくいきませんでした。単純なforループで保存するだけでは、十分に速くはありません。私はマルチスレッドのセーブを行うことができる必要があります。そうしないと、10秒ごとに〜15Kの挿入/更新となる負荷に追いつくことができなくなります。 – Justin

0

メッセージキューについて聞いたことがありますか? このような負荷を処理するためのボックスをたくさん入れ、メッセージキューイングメカニズムを使用してデータをmongodbに保存することができます。 ただし、この場合、メッセージキューで同時発行購読を実行できる必要があります。 無料のメッセージキュー(私の意見では非常に良い)は、RabbitMQのMassTransitです。

ワークフローは次のようになります。 1.メッセージキューにデータを公開します。 2.そこにいったん入ったら、あなたのmongoデータを保存して処理するサブスクライバで、必要なだけ多くのボックスを起動します。

この手法は、縮尺を変更する必要がある場合に適しています。

関連する問題