解决单身人士的Autofac会造成瓶颈

时间:2013-04-05 05:00:32

标签: c# asp.net-mvc autofac

我在asp.net MVC应用程序中使用Autofac并遇到了锁定问题。只要服务依赖于单例,该服务就会从根生存期范围中解析出来。这是因为Autofac:

  • 解析根范围中的单例组件
  • 解析根作用域中具有必须从root解析的依赖项的任何组件。

此外,当从任何范围解析时,Autofac会锁定该范围。我认为这些都是很好的设计决策。我的问题是那些依赖单身人士的行为不端的人有慢的构造者。这些为任何需要解决单身人士的人造成了瓶颈。

由于这是在MVC应用程序中,每个请求都会映射到某个构造函数注入的MVC控制器。此外,我的大多数控制器都依赖于各种单例服务(日志记录,缓存等)。

对于具有快速构造函数的东西,这不是问题。但是,只要请求一个写得不好的类,我的吞吐量就会因为每个新请求都被阻塞在那个行为不当的构造函数上。例如:

给出这3个类

//registered as SingleInstance()
class MySingleton {}

class BadTransient
{
    public BadTransient(MySingleton s)
    { Thread.Sleep(5000); }
}  

class FriendlyTransient {}

已解决,如

using(var scope = container.BeginLifetimeScope("nested scope"))
{

    //resolves from child scope
    var myFriend = scope.Resolve<FriendlyTransient>(); 

    //resolves from root because it's a singleton
    var singleton = scope.Resolve<MySingleton>(); 

    //resolves from root because it has a singleton dependency. 
    //**** this locks the root scope for 5 seconds
    //****   no one else can resolve singletons.
    var troublemaker = scope.Resolve<BadTransient>();         
}

有没有办法可以避免这个瓶颈?

显而易见的答案是拥有快速构造函数。实际情况是,并非我的代码库中的所有构造函数都能保证快速。 <遗留代码中有很多遗留代码,有很多依赖第三方代码的代码,代码看起来很快,但依赖的代码不是&#39 ; t,这些代码通常很快但在奇怪的情况下会崩溃等等。教育开发人员只能在一定程度上起作用。

我们发现它们时一直在修复构造函数,但我需要一个更主动的解决方案。让我的用户进行质量检查是不可接受的。

注意:我并不关心那些不依赖单身人士的缓慢构造者。他们将锁定他们的生命范围,但它不会阻止其他线程

2 个答案:

答案 0 :(得分:2)

我同意@nemesv对象构造应该很快,但这也意味着不能在OnActivated初始化。相反,您应该在首次使用组件时懒惰地执行此操作。例如,通过内部实现代理或一些Lazy<T>

但是,如果您的应用程序具有相当高的吞吐量和并发特性,并且通过性能分析验证锁定是一个瓶颈,那么您可以考虑切换到无锁的IoC容器。

答案 1 :(得分:2)

我通过使用autofac的闭包语法重新注册所有单例来解决问题 这使构造逻辑保持在autofac中,但从子生命周期范围中解析单例。实质上:

builder.Register<MySingleton>().AsSelf().AsImplementedInterfaces();
//.. other registrations

var container = builder.Build();

// now resolve each singleton, forcing all to be constructed if not already
//   and then register the instance
var builder2 = new ContainerBuilder();

var mySingleton = container.Resolve<MySingleton>();
builder2.Register(c => mySingleton).AsSelf().AsImplementedInterfaces();
builder2.Update(container);

//.....
var scope = container.BeginLifetimeScope("child scope");   
scope.Resolve<MySingleton>(); //not resolved from root!

然后,由于有很多单例,我可以通过编程方式查询它们的类型,我编写了一个函数,它接受类型列表并运行上面的代码。它必须做一点反射魔法,但只能在正常的autofac注册码结束时在app启动时运行。

void ReRegisterSingletons(IContainer container, List<Type> singletonTypes)
{
     var builder= new ContainerBuilder();
     foreach(var type in singletonTypes)
     {

          var resolved = container.Resolve(type);
          var method = this.GetType().GetMethod("ReRegister").MakeGenericMethod(new []{type});
          method.Invoke(this, new []{resolved});
     }

     builder.Update(container);
}

void Register<T>(ContainerBuilder builder, object singleton)
{
    var theObj = (T)singleton;

     //a typed lambda was the only way I could get both the class name and the interface names to resolve from the child scope. RegisterInstance still resolves from root, and the non-generic lamba register can resolve the class name from child scope but not the interface names...
    builder.Register(c => theObj).AsSelf().AsImplementedInterfaces();
}