MongoDB C#驱动程序不会释放连接然后发生错误

时间:2011-09-16 14:53:52

标签: mongodb mongodb-.net-driver

我正在使用最新版本的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#驱动程序有问题吗?帮助!

3 个答案:

答案 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数据的订户启动任意数量的盒子。

如果你需要扩展,这种方法会很好。