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

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

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

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

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

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

鉴于这3个class级

//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(); //resolves from root because it's a singleton var singleton = scope.Resolve(); //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(); } 

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

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

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

注意:我不关心那些不依赖于单例的慢构造函数。 它们将锁定它们的生命周期范围,但它不会阻止其他线程

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

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

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

 builder.Register().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(); builder2.Register(c => mySingleton).AsSelf().AsImplementedInterfaces(); builder2.Update(container); //..... var scope = container.BeginLifetimeScope("child scope"); scope.Resolve(); //not resolved from root! 

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

 void ReRegisterSingletons(IContainer container, List 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(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(); }