开篇先引用下鲁迅先生笔下的《拿来主义》一说,暂且粗浅的理解为,拿来就用,以为是能达到自己想要的效果。
向鲁迅先生致敬!
算是2021年的新项目吧,内部精心规划,来年招兵买马,注入了不少新鲜血液。新人或许有新气象。老人带领下的新团队,开启了老项目的重构计划...
新项目,名义上已DDD框架为设计原则,由于团队专业素质参差不齐,加之个人经验的影响,至今都不能说是一个清晰完整的DDD项目。业内,尤其是不同平台下的社区生态,架构到框架,理论到实践,总是能够
衍生出无数可能。这里我们暂且不吐糟这些不同认知及经验构建出来的框架,或许这样可能催生出璀璨的行业文化。
时隔多年,到2023年,抽出一些时间,细看了下先前项目的源代码。脑海中只是隐约记得,总是有运营和用户反馈,某某页面,第一次加载总是很慢,连续刷新几次,也快不到那儿去。于是抽查了印象中的几个页面,
的确如此,首次很慢,几十个HTTP请求,1s以上的就接近10个。而问及这个问题时,开发者的表态,总是以单表数据太大,有关键词模糊等为由,不由分说了。粗略的看了下源代码,预感出,在多级缓存方面可能存在问题。项目中,有两种缓存,一种基于进程内IMemoryCache的方式,另一种,则是接入redis。
这里先不要断言,我们不能确保IMemoryCache的形式就不会有什么问题。于是基于此,创建了两个单元测试方法。一个是在调用较慢接口(HTTP请求耗时较长,一般超过1s的,从长到短),在这个过程中执行单元测试,
按步执行,在某些位置,的确出现了卡顿现象。首次出现卡顿的位置,就是使用IMemoryCache的地方。发现,开发者,无所顾忌的,读取了整表的数据,然后放入缓存,致使这个表在数据增多时,初次从数据库读取载入,
需要较长时间。那我们就先排除这个吧,注释掉这段,临时补上一段,继续。
全文所说项目,乃是基于.net5、mysql、Redis、docker等这些技术栈为基础。
发现,在请求HTTP的时,进入Controller的时间,明显超出预期。试想,应该是IOC在构建Controller时消耗了较长时间,导致我们发出请求,却迟迟没有进入Action。为了进一步验证是IOC构建Controller的过程出现了问题,
我尝试请求一个简单的接口,简单到不需要通过任何协议来获取数据的方法。事实证明,造成当前阶段卡顿的原因,不在接口,而在进入接口前,经历了什么。
再进一步分析,分析Controller构造函数,注入了什么?注入的东西有没有依赖什么服务,比如redis。初步分析,依赖外部服务的有两种,第一个种,SqlSugarClient,第二种,CSRedisClient。那么我们继续进行第二个单元测试,
尝试分辨初始化依赖SqlSugarClient和CSRedisClient的服务,分析用时较长的一方。经分析,注入依赖CSRedisClient的地方,用时较长。由于项目引用的依赖CSRedisClient的组件,为当时团队开发,且nuget包上传至自建Nuget仓库中。
那我们就要进一步分析nuget包里的东西了。
/// <summary> /// 创建缓存 /// </summary> /// <param name="config"></param> /// <returns></returns> private IDistributedCache CreateCache(RedisConfig config) {
var _client = new CSRedisClient($"{config.ConnectionString},prefix={config.InstanceName},testcluster=false");
returnreturn new CSRedisCache(_client); }
看上面代码,他们是自定义了一个Cache类,内部通过这个方法,来创建IDistributedCache 对象。而且项目中依赖Cache类的服务,非单例,而是Score,这样致使每次请求,都会构建一个
CSRedisClient实例,而且初始化CSRedisClient的地方,的确有卡顿。改造下吧!
/// <summary> /// <![CDATA[推荐,添加Redis单例服务]]> /// </summary> /// <param name="services"></param> /// <param name="func"></param> public static void AddRedisCacheSingletonService(this IServiceCollection services, Func<RedisConfig> func) { var config = func.Invoke(); services.TryAddSingleton(new RedisConfig { ConnectionString = config.ConnectionString, InstanceName = config.InstanceName }); services.TryAddSingleton<ICache, Cache>(); }
来一个单例,使用CSRedisClient时推荐使用单例模式,不够用,考虑对象池吧。有时候,不需要CSRedisClient的方法,由于Service构造函数注入,不得不构建了。这里可考虑使用懒加载Layz<T>,用法也很简单,只有使用的时候,才会被初始化,
也就是在首次使用Layz Vaue时,就初始化了。后面同一个Lazy实例,访问Value仍是同一个实例,相当于缓存了Value。
/// <summary> /// 创建缓存(延迟加载) /// </summary> /// <param name="config"></param> /// <returns></returns> private Lazy<IDistributedCache> CreateCache(RedisConfig config) { return new Lazy<IDistributedCache>(() => { _client = new CSRedisClient($"{config.ConnectionString},prefix={config.InstanceName},testcluster=false"); return new CSRedisCache(_client); }); }
经过两个地方的调整,接口响应时间从3s+,降低到了平均267ms。
开篇先引用下鲁迅先生笔下的《拿来主义》一说,暂且粗浅的理解为,拿来就用,以为是能达到自己想要的效果。
算是2021年的新项目吧,内部精心规划,来年招兵买马,注入了不少新鲜血液。新人或许有新气象。老人带领下的新团队,开启了老项目的重构计划...
新项目,名义上已DDD框架为设计原则,由于团队专业素质参差不齐,加之个人经验的影响,至今都不能说是一个清晰完整的DDD项目。业内,尤其是不同平台下的社区生态,架构到框架,理论到实践,总是能够
衍生出无数可能。这里我们暂且不吐糟这些不同认知及经验构建出来的框架,或许这样可能催生出璀璨的行业文化。
时隔多年,到2023年,抽出一些时间,细看了下先前项目的源代码。脑海中只是隐约记得,总是有运营和用户反馈,某某页面,第一次加载总是很慢,连续刷新几次,也快不到那儿去。于是抽查了印象中的几个页面,
的确如此,首次很慢,几十个HTTP请求,1s以上的就接近10个。而问及这个问题时,开发者的表态,总是以单表数据太大,有关键词模糊等为由,不由分说了。粗略的看了下源代码,预感出,在多级缓存方面可能存在问题。项目中,有两种缓存,一种基于进程内IMemoryCache的方式,另一种,则是接入redis。
这里先不要断言,我们不能确保IMemoryCache的形式就不会有什么问题。于是基于此,创建了两个单元测试方法。一个是在调用较慢接口(HTTP请求耗时较长,一般超过1s的,从长到短),在这个过程中执行单元测试,
按步执行,在某些位置,的确出现了卡顿现象。首次出现卡顿的位置,就是使用IMemoryCache的地方。发现,开发者,无所顾忌的,读取了整表的数据,然后放入缓存,致使这个表在数据增多时,初次从数据库读取载入,
需要较长时间。那我们就先排除这个吧,注释掉这段,临时补上一段,继续。
发现,在请求HTTP的时,进入Controller的时间,明显超出预期。试想,应该是IOC在构建Controller时消耗了较长时间,导致我们发出请求,却迟迟没有进入Action。为了进一步验证是IOC构建Controller的过程出现了问题,
我尝试请求一个简单的接口,简单到不需要通过任何协议来获取数据的方法。事实证明,造成当前阶段卡顿的原因,不在接口,而在进入接口前,经历了什么。
再进一步分析,分析Controller构造函数,注入了什么?注入的东西有没有依赖什么服务,比如redis。初步分析,依赖外部服务的有两种,第一个种,SqlSugarClient,第二种,CSRedisClient。那么我们继续进行第二个单元测试,
尝试分辨初始化依赖SqlSugarClient和CSRedisClient的服务,分析用时较长的一方。经分析,注入依赖CSRedisClient的地方,用时较长。由于项目引用的依赖CSRedisClient的组件,为当时团队开发,且nuget包上传至自建Nuget仓库中。
那我们就要进一步分析nuget包里的东西了。
/// <summary> /// 创建缓存 /// </summary> /// <param name="config"></param> /// <returns></returns> private IDistributedCache CreateCache(RedisConfig config) {
var _client = new CSRedisClient($"{config.ConnectionString},prefix={config.InstanceName},testcluster=false");
returnreturn new CSRedisCache(_client); }
看上面代码,他们是自定义了一个Cache类,内部通过这个方法,来创建IDistributedCache 对象。而且项目中依赖Cache类的服务,非单例,而是Score,这样致使每次请求,都会构建一个
CSRedisClient实例,而且初始化CSRedisClient的地方,的确有卡顿。改造下吧!
/// <summary> /// <![CDATA[推荐,添加Redis单例服务]]> /// </summary> /// <param name="services"></param> /// <param name="func"></param> public static void AddRedisCacheSingletonService(this IServiceCollection services, Func<RedisConfig> func) { var config = func.Invoke(); services.TryAddSingleton(new RedisConfig { ConnectionString = config.ConnectionString, InstanceName = config.InstanceName }); services.TryAddSingleton<ICache, Cache>(); }
来一个单例,使用CSRedisClient时推荐使用单例模式,不够用,考虑对象池吧。有时候,不需要CSRedisClient的方法,由于Service构造函数注入,不得不构建了。这里可考虑使用懒加载Layz<T>,用法也很简单,只有使用的时候,才会被初始化,
也就是在首次使用Layz Vaue时,就初始化了。后面同一个Lazy实例,访问Value仍是同一个实例,相当于缓存了Value。
/// <summary> /// 创建缓存(延迟加载) /// </summary> /// <param name="config"></param> /// <returns></returns> private Lazy<IDistributedCache> CreateCache(RedisConfig config) { return new Lazy<IDistributedCache>(() => { _client = new CSRedisClient($"{config.ConnectionString},prefix={config.InstanceName},testcluster=false"); return new CSRedisCache(_client); }); }
经过两个地方的调整,接口响应时间从3s+,降低到了平均267ms。
好的东西,拿来就用的同时,有空请审视一下。
至此,算是今天的随笔,算是告一段落。想来,已经快两年没有更新过博客了,到了饭点,各位干饭要紧!~
标签:这么,缓存,InstanceName,Controller,拿来主义,CSRedisClient,new,config From: https://www.cnblogs.com/ibeisha/p/17567992.html