摘要: 大概一年前开始在思考 构造函数中 依赖注入较多,这对系统性能及硬件资源消耗产生一些优化想法。一般较多公司的项目都使用Autofac 依赖注入(Scoped 作用域),但是发现过多的对象产生 会消耗 CPU , 内存 并给GC(垃圾回收)造成一定的压力。那么开始思考是否能够使用 单例 (Singleton)来解决这些问题呢?带着这些想法开始ReView整个项目的代码,排查是否存在 单例 会造成 线程安全 或 方法内修改全局变量的代码(结果是乐观的.... )。于是开始了性能测试....论证.. 试运行... ,结果是超预期的(CPU 从 60%-降低到--》10%, 内存 从 33%-降低到--》20%, 接口平均响应时间 从 120毫秒--降低到--》50毫秒 . 1500/QPS (不含内部服务相互调用)) 和@InCerry 沟通结果,说可以写个 案例 和大家分享分享... 于是乎 有了这一片文章。
基础概念介绍1.依赖注入(Dependency Injection , DI)
依赖注入(Dependency Injection,DI)是一种实现控制反转(IoC)的技术。它是指通过外部的方式将一个对象的依赖关系注入到该对象中,而不是由该对象自己创建或查找依赖的对象。依赖注入可以通过构造函数、属性或方法参数等方式实现。
依赖注入的好处是可以降低对象之间的耦合性,提高代码的可测试性和可维护性。通过将依赖关系从对象内部移动到外部,我们可以更容易地替换依赖的对象,以及更容易地进行单元测试。同时,依赖注入也可以使代码更加灵活和可扩展,因为我们可以通过注入不同的依赖来改变对象的行为。
日常编码的时候大家追求的都是高内聚低耦合这种就是良性的依赖,避免 牵一发动全身的则是恶性依赖重则推到重构、轻则维护困难。
2. 控制反转 (Inversion of Control , IoC)
控制反转 (Inversion of Control , IoC)最早是世界级软件开发大师 Martin Fowler 提出的一种设计原则,它指导我们将控制权从应用程序代码中转移到框架或容器中。IoC原则要求我们将对象的创建、依赖注入和生命周期管理等控制权交给框架或容器来处理,而不是由应用程序代码来直接控制。
这样做的好处是,可以降低代码的耦合性,提高代码的可测试性和可维护性。框架或容器负责管理对象的创建和销毁,以及解决对象之间的依赖关系,应用程序代码只需要关注业务逻辑的实现。
3.依赖倒置原则(Dependence Inversion Principle , DIP)
依赖倒置原则(Dependence Inversion Principle , DIP)是面向对象设计中的一个原则,它指导我们在设计软件时应该依赖于抽象而不是具体实现。
DIP原则要求高层模块不应该依赖于低层模块,而是应该依赖于抽象接口。这样做的好处是,当我们需要修改低层模块的实现时,高层模块不需要做任何修改,只需要修改抽象接口的实现即可。这样可以提高代码的灵活性和可维护性。
生命周期1.单例模式 (Singleton)
单例模式是指在整个应用程序中只创建一个对象实例,并且该实例在整个应用程序的生命周期内都是可用的。单例模式可以通过IoC容器来管理,容器会在第一次请求该对象时创建一个实例,并在后续的请求中返回同一个实例。在整个应用程序生命周期中只创建一个实例,并且该实例将被共享和重用。
由于只创建一个实例并重用它,因此在性能方面可能更高效。 但是,*** →→→※※※注意:如果该实例包含状态或可变数据,可能需要考虑线程安全性 和 避免修改全局变量 ※※※⬅⬅⬅***。
2.作用域模式 (Scoped)
作用域模式是指根据对象的作用域来管理对象的生命周期。常见的作用域包括请求作用域、会话作用域和应用程序作用域。在请求作用域中,每个请求都会创建一个新的对象实例,并且该实例只在该请求的处理过程中可用。在会话作用域中,每个会话都会创建一个新的对象实例,并且该实例在整个会话的生命周期内可用。
在每个请求或作用域内创建一个实例,并且该实例只在该请求或作用域内共享和重用。作用域模式适用于那些需要根据不同的上下文来管理对象生命周期的情况。
3.瞬时模式 (Transient)
瞬时模式是指每次请求都会创建一个新的对象实例,并且该实例只在该请求的处理过程中可用。瞬时模式适用于那些不需要共享状态或资源的对象,每次请求都需要一个新的对象实例。 (这种一般实际项目中 用的比较少。)
Autofac 更多信息: (文档) (源码)
Microsoft.Extensions.DependencyInjection 更多信息: (文档)
单例模式的调整1. 调整后的代码
1. 因:Services & Repositories 构造函数依赖注入较多,且 注入的class类 的构造函数又有构造函数,由此导致请求需要实例化的对象非常多,较多的对象又会对GC造成一定的影响。(当然你可以调整成属性注入来解决此问题)
2. 所:调整为 Singleton 单例模式 提升系统性能,需要特别注意:如果实例包含状态或可变数据,可能需要考虑线程安全性 和 避免修改全局变量 (请做好压力测试 以及 灰度上线观察)。
Me Dyx : 单例& 作用域)从底层 解释一下区别呢?
老A (蒋老师 Artech):由于方法对应IL没有本质区别,所以两者的区别在于一个不需要每次实例化分配内存,如果调用频繁,会增加GC压力。
Me Dyx: 能使用单例的时候 是否应该优先使用 单例呢? 毕竟 new 一个新对象 有开销,还要垃圾回收 调用 GC 。
老A (蒋老师 Artech): 当然 , 面向GC编程
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
///
/// 依赖注入 new
///
publicstaticvoidRegisterDependencyNew()
varbuilder =newContainerBuilder();
// 注册 MVC 容器的实现
builder.RegisterControllers(Assembly.GetExecutingAssembly());
// 注册服务和仓储
RegisterTypesBySuffix(builder,"Service");
RegisterTypesBySuffix(builder,"Repository");
// 注册缓存管理器和 Redis 缓存管理器
//builder.RegisterInstance(CacheSetting.CacheManager).SingleInstance();
//builder.Register(r =>
//{
// return CacheSetting.CacheManager;
//}).AsSelf().SingleInstance();
//builder.RegisterType().As().SingleInstance();
// 注册 Cap 发布器
//builder.RegisterInstance(GetCapPublisher()).SingleInstance();
//builder.Register(r =>
//{
// return CapConfig.Services.BuildServiceProvider().GetRequiredService();
//}).AsSelf().SingleInstance();
varcontainer = builder.Build();
DependencyResolver.SetResolver(newAutofacDependencyResolver(container));
privatestaticvoidRegisterTypesBySuffix(ContainerBuilder builder,stringsuffix)
varassemblys = BuildManager.GetReferencedAssemblies().Cast();
builder.RegisterAssemblyTypes(assemblys.ToArray())
.Where(t => t.Name.EndsWith(suffix))
.AsImplementedInterfaces()
.SingleInstance();
2. 调整前的代码
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
///
/// 依赖注入-Old
///
publicstaticvoidRegisterDependencyOld()
varbuilder =newContainerBuilder();
//注册mvc容器的实现
builder.RegisterControllers(Assembly.GetExecutingAssembly());
//如果有web类型,请使用如下获取Assenbly方法
varassemblys = BuildManager.GetReferencedAssemblies().Cast().ToList();
builder.RegisterAssemblyTypes(assemblys.ToArray()).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(assemblys.ToArray()).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces();
/*
//在Autofac中注册Redis的连接,并设置为Singleton (官方建議保留Connection,重複使用)
builder.Register(r =>
return ConnectionMultiplexer.Connect(DbSetting.Redis);
}).AsSelf().SingleInstance();
*/
//在Autofac中注册CacheManager 缓存配置,并设置为Singleton[]
//builder.Register(r =>
//{
// return CacheSetting.CacheManager;
//}).AsSelf().SingleInstance();
//builder.Register(c => new RedisCacheManager()).As().AsSelf().SingleInstance();
//builder.Register(r =>
//{
// return CapConfig.Services.BuildServiceProvider().GetRequiredService();
//}).AsSelf().SingleInstance();
varcontainer = builder.Build();
DependencyResolver.SetResolver(newAutofacDependencyResolver(container));
生产运行状态监控1. CPU
2. 内存
3. 接口响应时间
关于性能优化1. 框架版本
* *.NET Framework和.NET Core是微软的两个不同的开发平台。
1. .NET Framework:.NET Framework是微软最早发布的开发平台,它是一个完整的、统一的Windows应用程序开发框架。它支持多种编程语言(如C#、VB.NET等)和多种应用类型(如Windows桌面应用、ASP.NET Web应用等)。.NET Framework依赖于Windows操作系统,并且只能在Windows上运行。
2. .NET Core:.NET Core是微软在.NET Framework基础上进行的重写和改进,它是一个跨平台的开发平台。.NET Core具有更小、更快、更模块化的特点,可以在Windows、Linux和macOS等多个操作系统上运行。.NET Core支持多种编程语言(如C#、F#、VB.NET等)和多种应用类型(如控制台应用、Web应用、移动应用等)。
* *升级到.NET Core版本对性能有以下好处:
1. 更高的性能:.NET Core在性能方面进行了优化,具有更快的启动时间和更高的吞吐量。它采用了新的JIT编译器(RyuJIT)和优化的垃圾回收器(CoreCLR),可以提供更好的性能。
2. 更小的内存占用:.NET Core采用了更精简的运行时库,可以减少应用程序的内存占用。这对于云计算和容器化部署非常有利。
3. 跨平台支持:.NET Core可以在多个操作系统上运行,包括Windows、Linux和macOS等。这使得开发人员可以更灵活地选择运行环境,并且可以更好地适应不同的部署需求。
4. 更好的可扩展性:.NET Core提供了更多的开发工具和库,可以更方便地构建可扩展的应用程序。它支持微服务架构和容器化部署,可以更好地应对大规模应用的需求。
升级到.NET Core版本可以带来更高的性能、更小的内存占用、更好的跨平台支持和更好的可扩展性。这些优势使得.NET Core成为现代应用程序开发具有性能优势。
2. 升级插件 (.NET Upgrade Assistant 插件, .NET Framework 升级至跨平台的 .NET Core)
1. 在 VS 2022 中进行 .NET Upgrade Assistant 的安装。
2. 按照 提示下一步 等待片刻 即可:
3. 打开您需要升级的项目,在项目上点击右键就会出现 Upgrade 按钮:
4. 升级后... 可能编辑器会提示N个错误...别慌.. 很多都是一个原因导致的,升级相关第三方组件支持 .net core, 静下心来 逐个解决,上线前做好 充足的测试。
(保守估计,在您不修改项目原有逻辑,整体性能会提升 30%+ ,什么你不信?^_^ 接着往下看 其他公司案例... )
因 .NET Core 的底层全部重构了具有后发优势(重新开发,重新面向云原生设计 从 core 1.0 / 1.1 /2.0 / 2.1 “不完善比较坑” , 到现在的 3.1 ,5.0, 6.0 ,7.0, 以及即将发布的 8.0 经过不断完善改进 目前已经非常稳定可靠 ), 抛弃了原有的.NET Framework 底层和Window深度捆绑。
使用 .NET 升级助手将 ASP.NET Framework 新式化为 ASP.NET Core - Training | Microsoft Learn
从 ASP.NET 更新到 ASP.NET Core | Microsoft Learn
创业/副业必备:
本站已持续更新1W+创业副业顶尖课程,涵盖多个领域。
点击查看详情
评论(0)