我正在使用最新版本的MongoDB(在Win 64服务器上)和C#驱动程序。我有一个Windows服务,每分钟执行800次读取和更新,几分钟后,当前使用的线程超过200,然后每个mongodb调用都会出现此错误:
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#驱动程序有问题吗?帮助!
答案 0 :(得分:4)
C#驱动程序有一个连接池,默认情况下连接池的最大大小为100。因此,您永远不应该从单个C#客户端进程看到超过100个与mongod的连接。 1.1版本的C#驱动程序确实在重负载下偶尔出现问题,其中一个连接上的错误可能导致断开连接和连接的风暴。您可以通过查看服务器日志来判断是否发生了这种情况,每次打开或关闭连接时都会写入日志条目。如果是这样,你能试试本周发布的1.2 C#驱动程序吗?
您不需要创建待处理更新的队列。通过限制并发请求的数量,连接池充当排序队列。
如果您可以在服务器日志中找到任何内容,请告诉我,如果还有其他任何内容,我可以为您提供帮助。
答案 1 :(得分: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);
}
}
}
答案 2 :(得分:0)
您是否听说过消息队列? 你可以放一堆盒子来处理这样的负载,并使用消息排队机制将你的数据保存到mongodb。 但是,在这种情况下,您的消息队列必须能够运行并发发布订阅。 一个免费的消息队列(在我看来非常好)是使用RabbitMQ的MassTransit。
工作流程将是: 1.在消息队列中发布数据; 2.一旦到达那里,就可以根据需要为保存和处理mongo数据的订户启动任意数量的盒子。
如果你需要扩展,这种方法会很好。