【问题标题】:Nhibernate, SQL Server and IIS 7.5 Connection leak : Timeout prior to obtaining a connection from the app poolNhibernate、SQL Server 和 IIS 7.5 连接泄漏:从应用程序池获取连接之前超时
【发布时间】:2016-06-05 00:49:24
【问题描述】:

我有一个 MVC 应用程序部署在 3 个独立的服务器(测试、暂存和生产环境)上。测试和暂存服务器运行良好,但偶尔,生产服务器确实会出现此错误

System.InvalidOperationException 超时已过。在从池中获取连接之前已经过了超时时间。这可能是因为所有池连接都在>使用中并且已达到最大池大小。

elmah 捕获的完整堆栈跟踪如下所示。

生产服务器在 SQL Server 2008 和 IIS 7.5 上。

我的 Nhibernate Driver 连接器如下图所示

 <?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
    <property    name="dialect">NHibernate.Dialect.MsSql2008Dialect,NHibernate</property>
    <property name="adonet.batch_size">0</property>
    <property name="show_sql">true</property> 
    <mapping assembly="BusinessLogic"/>
</session-factory>
</hibernate-configuration>

这是我的获取当前会话方法

 public static ISession GetCurrentSession()
        {
            HttpContext context = HttpContext.Current;
            ISession currentSession = context.Items[CURRENT_NHIBERNATE_SESSION_KEY] as ISession;

            if (currentSession == null)
            {
                currentSession = sessionFactory.OpenSession();
                context.Items[CURRENT_NHIBERNATE_SESSION_KEY] = currentSession;
            }
            if (currentSession.Connection.State == System.Data.ConnectionState.Closed)
            {
                currentSession = sessionFactory.OpenSession();
            }
            if (!currentSession.IsConnected)
            {
                currentSession = sessionFactory.OpenSession();
            }
            if (!currentSession.IsOpen)
            {
                currentSession = sessionFactory.OpenSession();
            }
            if (currentSession.IsDirty())
            {
                currentSession.Clear();
            }

            return currentSession;
        }

我的所有业务逻辑都使用了 IRepository 模式。我的一种方法如下所示:

 public IList<CourseRequirement> getCourseRequirement(int requirementId)
            {
                ISession  session = _courseRequirment.CurrentSession;
                var cr = session.QueryOver<CourseRequirement>().Where(x => x.RequirementId == requirementId);
                return cr.List<CourseRequirement>();
            }

此外,我正在使用 autofac 进行依赖注入。每次发生时,我也可以在内部异常中看到这一点。

System.InvalidOperationException:尝试执行时出错 创建一个“ASSEMBLY.Controllers.AccountController”类型的控制器。 确保控制器有一个无参数的公共构造函数。 ---> Autofac.Core.DependencyResolutionException:在类型上调用构造函数“Void .ctor()”时引发异常 'NHibernateRepository`2'。 ---> 超时。超时时间 在从池中获取连接之前经过。这可能有 发生是因为所有池连接都在使用中并且最大池大小 达到了。 (有关详细信息,请参阅内部异常。)---> System.InvalidOperationException:超时已过期。超时时间 在从池中获取连接之前经过。这可能有 发生是因为所有池连接都在使用中并且最大池大小 已到达。

我做了什么

我已经检查了这些linklink 等等。 how-to-fix-sql-connection-leaks-in-this-code 等其他人建议将 sql 放入 using 语句中。 几乎所有的链接都表明应用程序中存在连接泄漏,并建议将所有事务放在 using 语句中。

实现意味着重写我的业务逻辑中的大多数方法。

我很担心,因为这不是我用这种方式编写的唯一应用程序,除了测试和登台环境都很好。从日志报告来看,这通常发生在网站流量较低时,例如早上 6:00、凌晨 3:00 但时间不固定。

请帮忙

更新 这是我的 autofac 依赖解析器(我可以从内部异常中看到 autofac 偶尔无法实例化某些服务

 public class FormDependencyResolver
    {
        public static void RegisterDependencies()
        {

            var builder = new ContainerBuilder();

            builder.RegisterControllers(typeof(MvcApplication).Assembly);


            builder.Register(c =>
                //register FakeHttpContext when HttpContext is not available
                HttpContext.Current != null ?
                (new HttpContextWrapper(HttpContext.Current) as HttpContextBase) :
                (new FakeHttpContext("~/") as HttpContextBase))
                .As<HttpContextBase>()
                .InstancePerLifetimeScope();
            builder.Register(c => c.Resolve<HttpContextBase>().Request)
                .As<HttpRequestBase>().InstancePerLifetimeScope();

            builder.Register(c => c.Resolve<HttpContextBase>().Response)
                .As<HttpResponseBase>()
                .InstancePerLifetimeScope();
            builder.Register(c => c.Resolve<HttpContextBase>().Server)
                .As<HttpServerUtilityBase>()
                .InstancePerLifetimeScope();
            builder.Register(c => c.Resolve<HttpContextBase>().Session)
                .As<HttpSessionStateBase>()
                .InstancePerLifetimeScope();



            builder.RegisterGeneric(typeof(NHibernateRepository<,>)).As(typeof(IRepository<,>)).InstancePerLifetimeScope();
            builder.RegisterType<AsyncService>().As<IAsyncService>().InstancePerLifetimeScope();//.InstancePerLifetimeScope();
            builder.RegisterType<SMSSender>().As<ISMSSender>().InstancePerLifetimeScope();
            builder.RegisterType<SMSAccount>().As<ISMSAccount>().InstancePerLifetimeScope();
            builder.RegisterType<BusinessLogic.Services.AuthenticationService>().As<IAuthenticationService>().InstancePerLifetimeScope();
            builder.RegisterType<PageHelper>().As<IPageHelper>().InstancePerLifetimeScope();

            builder.RegisterType<UtilityService>().As<IUtilityService>().InstancePerLifetimeScope();
            builder.RegisterType<WebWorker>().As<IWebWorker>().InstancePerLifetimeScope();
            builder.RegisterType<DateTimeHelper>().As<IDateTimeHelper>().InstancePerLifetimeScope();
            builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
            builder.RegisterType<AuditService>().As<IAuditService>().InstancePerLifetimeScope();
            builder.RegisterType<HtmlHelper>().InstancePerDependency();
            builder.RegisterType<PaymentService>().As<IPaymentService>().InstancePerLifetimeScope();

            IContainer container = builder.Build();
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

        }
    }

【问题讨论】:

    标签: c# asp.net-mvc nhibernate memory-leaks autofac


    【解决方案1】:

    您正在每个if 语句中打开新的会话实例。其中几个可以一次执行,因为没有else 语句。如果不调用Dispose(),之前打开的会话将超出范围。

    下面的代码是您应该需要的一切。只需确保在 HTTP 请求结束时释放会话实例即可。

    private static readonly object syncObject = new Object();
    
    public static ISession GetCurrentSession()
    {   
         lock (syncObject)
         {
             HttpContext context = HttpContext.Current;
             ISession currentSession = context.Items[CURRENT_NHIBERNATE_SESSION_KEY] as ISession;
    
             if (currentSession == null)
             {
                 currentSession = sessionFactory.OpenSession();
             }
    
             context.Items[CURRENT_NHIBERNATE_SESSION_KEY] = currentSession;    
    
             return currentSession;
         }
    }
    

    【讨论】:

    • 感谢@Dmitry S,但当前会话在 if 语句之外实例化。我的意思是我们在 if 语句中只有一个实例但有多个赋值。
    • sessionFactory.OpenSession() 为每个作业初始化一个新会话。
    猜你喜欢
    • 2017-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多