我们正在对与结构完全相同的旧数据库进行不同的集成,这些旧数据库基本上是无法更改的。为此,我们添加了一个辅助数据库来保存元信息,路由规则等内容,并临时保存旧数据库的数据。
我们主要使用NHibernate连接数据库。一个应用程序是WCF服务,它需要将传入的数据插入到真正宽的嵌套表(数十列)中。显然,性能是一个问题,因此我一直希望NHibernate交易尽可能经济。同时,并发似乎是一个问题。在生产中,我们开始遇到一些僵化的事务错误(死锁)。
我一直在采取平衡措施来处理这两个问题,但并没有真正消除并发问题。
服务行为设置为一次处理一个请求,如下所示:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode=ConcurrencyMode.Single]
public class LegacyGateService : ILegacyGateService
早些时候,经过互联网的一些“启发”(阅读:复制/粘贴),我最终分别为辅助数据库和遗留数据库添加了一组名为XxxNHibernateUtil的类。这些类控制NHibernate session ,并从预初始化的SessionFactories中生成或重用这些 session 。
对于辅助数据库,它看起来像这样:
public static class LegacyGateNHibernateUtil
{
private static readonly ISessionFactory sessionFactory = BuildSessionFactory();
private static ISessionFactory BuildSessionFactory()
{
try
{
Configuration Cfg = new Configuration();
Cfg.Configure();
Cfg.AddAssembly("LegacyGate.Persistence");
return Cfg.BuildSessionFactory();
}
catch (Exception ex)
{
throw ex;
}
}
public static ISessionFactory GetSessionFactory()
{
return sessionFactory;
}
public static ISession GetCurrentSession()
{
if (!CurrentSessionContext.HasBind(GetSessionFactory()))
CurrentSessionContext.Bind(GetSessionFactory().OpenSession());
return GetSessionFactory().GetCurrentSession();
}
public static void DisposeCurrentSession()
{
ISession currentSession = CurrentSessionContext.Unbind(GetSessionFactory());
if (currentSession != null)
{
if (currentSession.IsOpen)
currentSession.Close();
currentSession.Dispose();
}
}
}
每当事务需要 session 时,都会在服务请求调用期间查找并重新使用当前 session 。或者至少:那是应该发生的事情。
编辑:当然, session 上下文是在hibernate.cfg.xml中设置的,如下所示:
<property name="current_session_context_class">call</property>
对于旧数据库,NHibernateUtil适用于处理不同的可能数据库。为此,每个连接都会建立自己的SessionFactory,必须在Dictionary集合中查找该SessionFactory。否则,原理是相同的。
使用WCFStorm进行测试,这一次一次发送一个请求时似乎运行良好,但是,即使我只有一个代理且间隔很长,我开始进行负载测试时,也会得到大量不同种类的异常,这些异常都指向同时请求和交易彼此破坏。我曾尝试调整IsolationLevel,但现在还是有用的。
我认为我需要以不同的方式生成和处理 session ,以便对同一数据库的不同事务以有序的方式进行处理,而不会互相干扰。但是,我对如何使这项工作缺乏见识。任何帮助是极大的赞赏!
编辑对于一种服务方法,当使用多个代理进行测试时,前十几个调用工作正常,然后开始出现以下仅适用于辅助数据库的异常字符串:
“关闭阅读器后,无效的调用MetaData的尝试。”
至少异常1表示同一 session 被多个线程(可能是调用)访问。此外,其他指示当前 session 已被其他进程中断。但是,当我尝试隔离调用并使它们排队时,怎么可能呢?
对于另一种服务方法,这些问题不会出现在辅助数据库中,但是一段时间后,我开始通过与旧数据库的事务获取ZombiedTransaction异常(死锁)。还是...有什么作用?
最佳答案
简单的答案:您不会重复使用NHibernate session 。
它们不是重量级的对象,它们被设计为按照“工作单位”模式进行创建,操纵和处置。 尝试在多个请求中“共享”这些内容与它们的预期用法背道而驰。
从根本上讲,正确同步对 session 的访问的成本几乎可以肯定会抵消通过回收它们避免重新初始化它们而获得的任何 yield 。我们还要考虑这些成本是您将要执行的SQL的精简版。
请记住,NHibernate的 session 工厂是重量级的对象。它是线程安全的,因此您可以并且应该在所有开箱即用的请求之间共享一个实例。
您的代码在概念上应如下所示:
public class Service : IService
{
static Service()
{
Configuration Cfg = new Configuration();
Cfg.Configure();
Cfg.AddAssembly("LegacyGate.Persistence");
Service.SessionFactory = Cfg.BuildSessionFactory();
}
protected static ISessionFactory SessionFactory { get; private set; }
public void ServiceMethod(...)
{
using(var session = Service.SessionFactory.CreateSession())
{
// Do database stuff
...
}
}
}
顺便说一句:理想情况下,您需要将
ISessionFactory
依赖注入(inject)到服务中。
关于wcf - NHibernate和WCF : Performance (session reuse) vs.并发(同时请求),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8742282/