c# - 从 C# 驱动程序建立多个快速连接时,服务器发送了无效的随机数

标签 c# mongodb mongodb-.net-driver

我最近向我的开发数据库添加了身份验证,针对“admin”数据库进行身份验证,并在我的连接字符串中使用用户名/密码组合,例如mongodb://username:password@server:27017 .几乎立即我开始看到连接无法打开,异常显示“服务器发送了无效的随机数”。为了尝试缓解这个问题,我查看了 IMongoClient 对象的生命周期,并从实例化许多此类对象转向使用使用 Microsoft.Extensions.DependencyInjection 库注入(inject)到我的业务服务中的单例。这并没有缓解这个问题。我使用 .AddSingleton<IMongoClient>(factory => new MongoClient(Configuration["Storage:MongoConnectionString"])) 在 Startup.cs 中设置了 MongoClient .我知道连接字符串是正确的,因为它在 MongoDB Compass 中工作,也因为通过驱动程序的前几个调用成功工作;当多个并发调用正在进行时,问题开始发生。
我在 .NET Core 3.1.2 下使用 MongoDB .NET 驱动程序,版本 2.11.0。问题出现在我运行 Windows 10 的本地环境中,以及我在 VMware Photon 上的 Docker 中运行的暂存环境中。
该应用程序有两个组件与 MongoDB 建立连接,这两个组件都是 ASP.Net Core 应用程序,一个为我的应用程序的交互式使用提供 API,另一个运行 Quartz 调度程序以进行后台处理。我在 Docker 容器中运行 MongoDB 4.4.0 Community。
我对包含驱动程序的引用是:

<PackageReference Include="MongoDB.Bson" Version="2.11.0" />
<PackageReference Include="MongoDB.Driver" Version="2.11.0" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.11.0" />
根据 this post on the MongoDB Jira site我不是第一个遇到这个问题的人。 Mathias Lorenzen 在 Jira 上的问题中建议,他通过各种修复减少了他遇到的错误数量,包括重新创建用户、使用 SCRAM-SHA-1 以及增加服务器上允许的最大连接数。有了这些变化,我仍然会遇到这个问题。
我猜这个问题与与数据库身份验证结合使用时的线程有关。出于显而易见的原因,我无法通过禁用身份验证来解决此问题,从而将此代码投入生产使用,同样,使用同步模型而不是异步模型似乎会适得其反。我可以采取哪些步骤来尝试解决身份验证问题?这可能是 Mongo C# 驱动程序中的错误,还是我只是使用错误。
对我接下来可以尝试什么或替代方法的见解,将不胜感激。

编辑:根据要求的最小可重现示例:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Driver;

namespace MongoDbIssueExample
{
    internal class Program
    {
        private static IServiceProvider services;

        private static IServiceProvider BuildDependencyInjector()
        {
            services = new ServiceCollection()
                .AddSingleton<TestThingsService>()
                .AddSingleton<IMongoClient>(factory => new MongoClient("mongodb://username:password@server:27017"))
                .BuildServiceProvider();

            return services;
        }

        private static async Task DoSeed()
        {
            var service = services.GetService<TestThingsService>();
            // Don't do these async as we'll never get any data in...
            service.CreateTestThings().Wait();
            service.CreateOtherTestThings().Wait();
        }

        private static async Task DoTest()
        {
            var service = services.GetService<TestThingsService>();

            var things = service.GetTestThings();
            var otherThings = service.GetOtherTestThings();

            Task.WaitAll(things, otherThings);
        }

        private static async Task Main(string[] args)
        {
            BuildDependencyInjector();

            await DoTest();
        }
    }

    public class TestThingsService
    {
        private readonly IMongoClient _client;
        private readonly IMongoDatabase _database;
        private readonly IMongoCollection<OtherTestThing> _otherTestThingsCollection;
        private readonly IMongoCollection<TestThing> _testThingsCollection;

        public TestThingsService(IMongoClient client)
        {
            _client = client;
            _database = _client.GetDatabase("Things");
            _testThingsCollection = _database.GetCollection<TestThing>("TestThings");
            _otherTestThingsCollection = _database.GetCollection<OtherTestThing>("OtherTestThings");
        }

        public async Task CreateOtherTestThings()
        {
            for (var item = 1; item <= 10000; item++)
            {
                var testThing = new OtherTestThing {Id = item, Name = $"Other thing no. {item}", WhenCreated = DateTime.UtcNow};
                await _otherTestThingsCollection.ReplaceOneAsync(f => f.Id == item, testThing, new ReplaceOptions {IsUpsert = true});
            }
        }

        public async Task CreateTestThings()
        {
            for (var item = 1; item <= 10000; item++)
            {
                var testThing = new TestThing {Id = item, Name = $"Thing no. {item}", WhenCreated = DateTime.UtcNow};
                await _testThingsCollection.ReplaceOneAsync(f => f.Id == item, testThing, new ReplaceOptions {IsUpsert = true});
            }
        }


        public async Task<List<OtherTestThing>> GetOtherTestThings()
        {
            return await _otherTestThingsCollection.Find(_ => true).ToListAsync();
        }

        public async Task<List<TestThing>> GetTestThings()
        {
            return await _testThingsCollection.Find(_ => true).ToListAsync();
        }
    }

    public class OtherTestThing
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime WhenCreated { get; set; }
    }

    public class TestThing
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime WhenCreated { get; set; }
    }
}
需要引用如下:
        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.6" />
        <PackageReference Include="MongoDB.Bson" Version="2.11.0" />
        <PackageReference Include="MongoDB.Driver" Version="2.11.0" />
        <PackageReference Include="MongoDB.Driver.Core" Version="2.11.0" />

最佳答案

我最近也遇到了这个问题,并且能够使用您的测试代码在本地重现该问题。我调试了 mongo c# 驱动程序,发现问题是使用旨在将部分身份验证协议(protocol)搭载到初始服务器发现 (isMaster) 请求的功能的竞争条件——该身份验证状态的一部分最终存储在全局身份验证器中对象和多个并发身份验证请求使用最后看到的状态,这意味着一个请求看到为另一个请求生成的随机数与服务器响应不匹配,导致抛出异常。
有一个more recent bug report在包含详细信息的 mongo 错误跟踪器中。我也解决了这个问题 in this fork通过将身份验证状态与发现请求相关联,mongo c# 驱动程序。我正在使用 fork mongo 驱动程序的构建来解决这个问题(您可以通过在 fork 存储库中运行 build --target=Package 来创建其中一个)。

关于c# - 从 C# 驱动程序建立多个快速连接时,服务器发送了无效的随机数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63223544/

相关文章:

MongoDB 提示、技巧和陷阱

c# - MongoDB - 在 C# 中映射 map-reduce 集合

c# - 装饰模式问题C#/java

c# - Web api 上的 Azure 预热不起作用

mongodb - mongorestore 命令替换现有记录?

c# - 将 MongoDB Rest 的 Guid 转换为 C# CLR

带有查询 C# 驱动程序的 MongoDb Distinct

c# - MongoDB C# 驱动程序和线程安全

c# - WCF 服务是 Web 服务吗?

c# - 我可以使用获取和设置代码创建一个自动属性(无私有(private)成员)吗?