WebAPI性能优化小结
作者:互联网
一、本地缓存
-
设计思路
查询数据时先查看本地缓存中是否有数据,如果有数据直接返回,如果没有数据,到数据库查询后添加到本地缓存,并将数据返回。
-
优缺点
-
缺点
Memory是服务器内存的缓存,如果并发量大并查询的数据不一致,会造成内存非常大,同时会造成GC不断的回收内存,由于Memory内部使用的是静态变量,造成内存无法回收,GC每回收一次,就会耗费一次CPU资源,如果GC回收的频率比较大,大么耗费的CPU资源就较大。
- 解决方案:1.设置缓存时间。2.设置缓存大小。
-
优点
数据读写速度时间缩短,性能提升。
-
-
使用
-
安装Nuget包
Microsoft.Extensions.Caching.Memory
-
Startup.cs注册
//ConfigureServices方法中注册缓存服务 Service.AddMemoryCache(options=>{ options.SizeLimit = 1024*1024*100; //设置缓存大小 });
-
使用方法
//在构造方法中注入 private readonly IMemoryCache memoryCache; 构造函数 (IMemoryCache _memoryCache) { memoryCache =_memoryCache; } //测试对象 Person per = new Person(); //查询缓存方法 //memoryCache.Get<Person>(key); //为了放防止多线程并发 bool flag = memoryCache.TryGetValue<缓存对象>(key,out per); //true:有数据 false:无数据 //限制缓存大小 var cacheEntryOptions = new MemoryCacheEntryOptions(). //设置单个缓存大下 SetSize(1024). //设置过期时间 自动失效 SetSlidingExpiration(TimeSpan.FromSeconds(3)); //添加缓存 memoryCache.Set<Person>(key,value,cacheEntryOptions);
-
二、分布式缓存
简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。
-
原理
Redis数据库中的数据时存放在内存中,并非磁盘中,不需要把每次查询进行IO操作。把使用的数据查询加载的内存中,在内存中操作,提升查询效率。
-
使用场景
1.任何可丢失数据。2.不经常变动的数据。
-
使用方式
-
在appsetting.json添加Redis配置
"ConnectionStrings: RedisCaching节点配置信息
{ "ConnectionStrings": { "ConnectionString": "Data Source=127.0.0.1;Initial Catalog=db;User ID=uid;Password=123456;Pooling=True;Max Pool Size=512;Connect Timeout=500;", "JwtSetting": { "Issuer": "jwtIssuer", //颁发者 "Audience": "jwtAudience", //可以给哪些客户端使用 "SecretKey": "chuangqianmingyueguang" //加密的Key }, "RedisCaching": { "Enabled": true, "ConnectionString": "127.0.0.1:6379" } } }
-
添加SerializeHelper.cs 对象序列化操作
public class SerializeHelper { /// <summary> /// 序列化 /// </summary> /// <param name="item"></param> /// <returns></returns> public static byte[] Serialize(object item) { var jsonString = JsonConvert.SerializeObject(item); return Encoding.UTF8.GetBytes(jsonString); } /// <summary> /// 反序列化 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="value"></param> /// <returns></returns> public static TEntity Deserialize<TEntity>(byte[] value) { if (value == null) { return default(TEntity); } var jsonString = Encoding.UTF8.GetString(value); return JsonConvert.DeserializeObject<TEntity>(jsonString); } }
-
定义接口和实现类
新建IRedisCacheManager接口和RedisCacheManager类,并引用Nuget包StackExchange.Redis
public class RedisCacheManager : IRedisCacheManager { private readonly string redisConnenctionString; public volatile ConnectionMultiplexer redisConnection; private readonly object redisConnectionLock = new object(); public RedisCacheManager() { string redisConfiguration = ConfigHelper.GetSectionValue("ConnectionStrings:RedisCaching:ConnectionString");//获取连接字符串 if (string.IsNullOrWhiteSpace(redisConfiguration)) { throw new ArgumentException("redis config is empty", nameof(redisConfiguration)); } this.redisConnenctionString = redisConfiguration; this.redisConnection = GetRedisConnection(); } /// <summary> /// 核心代码,获取连接实例 /// 通过双if 夹lock的方式,实现单例模式 /// </summary> /// <returns></returns> private ConnectionMultiplexer GetRedisConnection() { //如果已经连接实例,直接返回 if (this.redisConnection != null && this.redisConnection.IsConnected) { return this.redisConnection; } //加锁,防止异步编程中,出现单例无效的问题 lock (redisConnectionLock) { if (this.redisConnection != null) { //释放redis连接 this.redisConnection.Dispose(); } try { this.redisConnection = ConnectionMultiplexer.Connect(redisConnenctionString); } catch (Exception) { throw new Exception("Redis服务未启用,请开启该服务"); } } return this.redisConnection; } public void Clear() { foreach (var endPoint in this.GetRedisConnection().GetEndPoints()) { var server = this.GetRedisConnection().GetServer(endPoint); foreach (var key in server.Keys()) { redisConnection.GetDatabase().KeyDelete(key); } } } public bool Get(string key) { return redisConnection.GetDatabase().KeyExists(key); } public string GetValue(string key) { return redisConnection.GetDatabase().StringGet(key); } public TEntity Get<TEntity>(string key) { var value = redisConnection.GetDatabase().StringGet(key); if (value.HasValue) { //需要用的反序列化,将Redis存储的Byte[],进行反序列化 return SerializeHelper.Deserialize<TEntity>(value); } else { return default(TEntity); } } public void Remove(string key) { redisConnection.GetDatabase().KeyDelete(key); } public void Set(string key, object value, TimeSpan cacheTime) { if (value != null) { //序列化,将object值生成RedisValue redisConnection.GetDatabase().StringSet(key, SerializeHelper.Serialize(value), cacheTime); } } public bool SetValue(string key, byte[] value) { return redisConnection.GetDatabase().StringSet(key, value, TimeSpan.FromSeconds(120)); } }
-
将Redis服务注入到容器中
在ConfigureServices中 进行注入:
//注册Redis services.AddSingleton<IRedisCacheManager, RedisCacheManager>();
-
控制器中使用
/// <summary> /// 测试Redis /// </summary> /// <returns></returns> [HttpGet] public async Task<IActionResult> Redis(int id) { var key = $"Redis{id}"; UserNew user = new UserNew(); if (_redisCacheManager.Get<object>(key) != null) { user = _redisCacheManager.Get<UserNew>(key); } else { user = new UserNew { UserId = id, UserName = "bingle", Age = 18 }; _redisCacheManager.Set(key, user, TimeSpan.FromHours(2));//缓存2小时 } return Ok(user); }
-
三、响应缓存
-
原理
当客户端第一次请求服务器,服务器响应后,服务器会往响应头里写入两个参数 :【1、是否开启缓存存储数据。2、校验】,并存储到客户端,客户端会将数据存储到缓存仓库中;当客户端第二次请求到服务器,会将etag传到服务器进行校验,如果两个etag是相等的,服务端会返给客户端304,客户端会从缓存仓库中获取数据。
-
场景:
不可变的数据使用。
-
缺陷
会浪费大量的客户端和服务器进行交互。
-
协商缓存:
-
安装Nuget包
Marvin.Cache.Headers
-
在Startup.cs中注册
//ConfigureServices方法中注册 Service.AddHttpCacheHeaders((options)=>{options.MaxAge = ....;//设置过期时间 默认60s options.CacheLocation = ....;//public 公共的 private 私有的只能当前客户端使用 options.NoStore= ...;// 设置响应头信息 不走本地缓存 options.NoTransform= ....;//设置请求头信息 }, (options1)=>{}); //Configure方法中启动并存储校验信息 app.UseHttpCacheHeaders();
-
控制器中添加代码
HttpContext.Response.Headers.Add("cache-control","max-age=60,public"); //是否开启缓存储数据 设置缓存时间 HttpContext.Response.Headers.Add("etag",Value);//校验 HttpContext.Response.Headers.Add("last-modified","Mon,24 Dec 2022 09:49:49 GMT");
-
-
强制缓存:
- 安装Nuget包:
Marvin.Cache.Headers
-
在Startup.cs中注册
//ConfigureServices方法中注册 Service.AddHttpCacheHeaders((options)=>{ options.MustRevalidate = true; //全局的方式 不建议使用 }); //Configure方法中启动并存储校验信息 app.UseHttpCacheHeaders();
-
控制器中添加代码
HttpContext.Response.Headers.Add("cache-control","max-age=60,public,must-revalidate");
-
针对某个控制器使用
//在控制器方法上添加特性 [HttpCacheExpiration(CacheLocation = CacheLocation.Public,MaxAge=60)] 设置NoStore = true 不走缓存 [HttpCacheValidation(MustRevalidate = true)]
-
使用场景
1.字典数据
2.静态资源,如图片,视频,文本等。
3.js或者css文件
四、数据压缩
-
在Startup类ConfigureServices方法中注册
//响应数据压缩 services.AddResponseCompression();
-
在Startup类Configure方法中开启服务
//必须写在中间件的开头 app.UseResponseCompression();
-
数据压缩的目的
传输数据的时候,减少数据传输的带宽,提升性能。
-
场景
只要涉及到数据传输,就可以进行压缩
标签:WebAPI,缓存,return,redisConnection,小结,value,key,优化,public 来源: https://www.cnblogs.com/wml-it/p/16508164.html