【问题标题】:IDbConnection lifecycle management with persistent HTTP connections具有持久 HTTP 连接的 IDbConnection 生命周期管理
【发布时间】:2012-10-28 15:19:28
【问题描述】:

当我的 ASP.NET MVC 应用程序(如 SignalR 集线器)中存在持久 HTTP 连接时,我在管理具有范围为 HttpContext 的 StructureMap 的打开数据库连接的生命周期时遇到问题。

我的 DI 容器 StructureMap 将打开的 IDbConnection 注入到多个服务中。为确保这些数据库连接已关闭并正确处理,我在 EndRequest 事件上调用 ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects()

这对 MVC 控制器非常有用,直到需要数据库连接的服务被注入到 SignalR 集线器中,该集线器为每个客户端保持持久的 HTTP 连接打开并最终使连接池饱和。

如果我将IDbConnection 限定为单例,则每个应用程序只会打开一个连接并且池不会饱和,但这是a bad idea,以防连接被锁定或超时。

那么也许有一种方法可以为我的 SignalR 集线器自定义数据库连接的范围?我尝试在每个 Hub 方法中解析一个服务实例,但这仍然会在 HttpContext 范围内实例化一个数据库连接,并在调用客户端的集线器连接期间保持打开状态。

当周围有持久的 HTTP 连接时,我应该如何在 HTTP 范围的上下文中使用 StructureMap 管理数据库连接的生命周期?

示例代码

典型服务

public class MyService
{
    private IDbConnection _con;
    public MyService(IDbConnection con)
    {
        _con = con;
    }

    public IEnumerable<string> GetStuff()
    {
        return _con.Select<string>("SELECT someString FROM SomeTable").ToList();
    }
}

典型的 SignalR 集线器

public class MyHub : Hub
{
    private MyService _service;
    public MyHub(MyService service)
    {
        _service = service; // Oh Noes! This will open a database connection
                            // for each Client because of HttpContext scope
    }

    public Task AddMessage()
    {
        var result = _service.GetStuff();
        // ...
    }
}

结构图配置

For<IDbConnection>()
    .HybridHttpOrThreadLocalScoped()
    .Use(() => BaseController.GetOpenConnection(MyConnectionString));

Global.asax.cs

public class GlobalApplication : System.Web.HttpApplication
{
    public GlobalApplication()
    {
        EndRequest += delegate
        {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        };
    }
    // ...
 }

【问题讨论】:

  • 在每个集线器方法上创建您的服务是一个解决方案吗?没有足够的信息来回答。 SignalR 将管理多少客户?他们多久通信/调用一次服务器方法?
  • 嘿@VladCiobanu,我用关于在每个集线器方法中解析服务实例的注释更新了我的问题。对于这个特定的应用程序,SignalR 管理大约 10-40 个持久客户端(目前),但是如果负载在高峰时间翻倍,这很容易使连接池最大化。我希望对我的问题有一个规范的答案。
  • 我想我已经解决了这个问题,方法是使用GetNestedContainer() 来显式处理服务并打开它使用的连接,从而解决每个 SignalR 集线器方法中的服务实例。
  • 不,看起来嵌套容器只显式处理瞬态实例,而IDbConnection 不能是瞬态的,因为服务层中的 LINQ 查询实现。
  • 已使用瞬态 IDbConnection 的命名实例解决!很快就会发布答案。

标签: asp.net-mvc structuremap signalr lifetime idbconnection


【解决方案1】:

在 SignalR 1.0.0 Alpha 中,Hub 的实现 IDisposable。 SignalR Hub 实例与HttpContext 不同,它是短暂的,因此如果您在HubDispose 方法中关闭您的IDbConnection,您不应该不必要地使您的连接池饱和。

【讨论】:

  • 谢谢@halter73。默认注入的IDbConnection 实例的范围为HttpContext,如果集线器关闭它会影响其他服务。我的解决方案是使用IDbConnection 的瞬态命名实例,该实例在每个 SignalR 方法的嵌套 StructureMap 容器中解析和处置。我不确定这是否是最好的方法,但它可以确保我的连接池永远不会饱和。
  • 这绝对不是最好的方法。当您使用 websockets 时,每次调用都会获得一个集线器实例,但需要一个长时间运行的 http 请求。所以你会泄漏。
  • 用我的解决方案查看新答案,@dfowler。
【解决方案2】:

使用临时数据库连接和嵌套 StructureMap 容器的解决方案

首先,在 StructureMap 中配置一个命名的临时数据库连接实例:

For<IDbConnection>()
    .Transient() // scope
    .Add(x => BaseController.GetOpenConnection(connectionString, IsDebugging()))
    .Named("Transient");

确保在您的默认实例之前配置此,否则它将覆盖默认实例。

其次,将IContainer 注入您的 SignalR 集线器,以便您可以构建嵌套的 StructureMap 容器:

public class JobHub : Hub
{
    private readonly IContainer _container;

    public JobHub(IContainer container)
    {
        _container = container;
    }

    public Task DoStuff(string input)
    {
        // ...

在您的 SignalR 方法中实例化一个嵌套容器并解析您命名的临时数据库连接:

        using (var httpRequestScope = _container.GetNestedContainer())
        {
            var transientConnection =
                    httpRequestScope.GetInstance<IDbConnection>("Transient");

使用.With&lt;IDbConnection&gt;(transientConnection) 确保嵌套容器实例化的服务和存储库使用此连接:

            var myService = httpRequestScope
                .With<IDbConnection>(transientConnection)
                .GetInstance<MyService>();

            var result = myService.DoStuff(input);

            return Clients.addResult(result);
        }
    }
}

最后,作用域using (...) 语句将确保您的嵌套容器自行清理,包括数据库连接。

这里的缺点是您为每个 SignalR 方法调用打开和关闭数据库连接,但由于连接是池化的,因此提前释放可能不是那么糟糕。您的里程数应取决于您的 SignalR 请求量。

您也许可以放弃嵌套容器,只需向DependencyResolver.Current 询问命名的连接实例,但您可能必须记住显式关闭每个连接以防止泄漏。

【讨论】:

    猜你喜欢
    • 2018-01-31
    • 2015-02-01
    • 1970-01-01
    • 2011-05-18
    • 2021-02-16
    • 2012-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多