ASP.NET Core中使用EasyCaching作為緩存抽象層


⒈是什么?

CacheManager差不多,兩者的定位和功能都差不多。

EasyCaching主要提供了下面的幾個功能

  1. 統一的抽象緩存接口
  2. 多種常用的緩存Provider(InMemory,Redis,Memcached,SQLite)
  3. 為分布式緩存的數據序列化提供了多種選擇
  4. 二級緩存
  5. 緩存的AOP操作(able, put,evict)
  6. 多實例支持
  7. 支持Diagnostics
  8. Redis的特殊Provider

⒉示例(以InMemory為例)

  1.安裝Nuget包

    EasyCaching.InMemory

  2.在Startup中配置服務及請求管道

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using EasyCaching.Core;
 6 using EasyCaching.InMemory;
 7 using Microsoft.AspNetCore.Builder;
 8 using Microsoft.AspNetCore.Hosting;
 9 using Microsoft.AspNetCore.Http;
10 using Microsoft.AspNetCore.Mvc;
11 using Microsoft.Extensions.Configuration;
12 using Microsoft.Extensions.DependencyInjection;
13 
14 namespace Coreqi.EasyCaching
15 {
16     public class Startup
17     {
18         public Startup(IConfiguration configuration)
19         {
20             Configuration = configuration;
21         }
22 
23         public IConfiguration Configuration { get; }
24 
25         // This method gets called by the runtime. Use this method to add services to the container.
26         public void ConfigureServices(IServiceCollection services)
27         {
28             services.AddEasyCaching(option =>
29             {
30                 // 使用InMemory最簡單的配置
31                 option.UseInMemory("default");
32 
33                 //// 使用InMemory自定義的配置
34                 //option.UseInMemory(options => 
35                 //{
36                 //     // DBConfig這個是每種Provider的特有配置
37                 //     options.DBConfig = new InMemoryCachingOptions
38                 //     {
39                 //         // InMemory的過期掃描頻率,默認值是60秒
40                 //         ExpirationScanFrequency = 60, 
41                 //         // InMemory的最大緩存數量, 默認值是10000
42                 //         SizeLimit = 100 
43                 //     };
44                 //     // 預防緩存在同一時間全部失效,可以為每個key的過期時間添加一個隨機的秒數,默認值是120秒
45                 //     options.MaxRdSecond = 120;
46                 //     // 是否開啟日志,默認值是false
47                 //     options.EnableLogging = false;
48                 //     // 互斥鎖的存活時間, 默認值是5000毫秒
49                 //     options.LockMs = 5000;
50                 //     // 沒有獲取到互斥鎖時的休眠時間,默認值是300毫秒
51                 //     options.SleepMs = 300;
52                 // }, "m2");         
53 
54                 //// 讀取配置文件
55                 //option.UseInMemory(Configuration, "m3");
56             });
57 
58             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
59         }
60 
61         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
62         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
63         {
64             if (env.IsDevelopment())
65             {
66                 app.UseDeveloperExceptionPage();
67             }
68             else
69             {
70                 app.UseExceptionHandler("/Home/Error");
71             }
72 
73             app.UseStaticFiles();
74             app.UseCookiePolicy();
75 
76             // 如果使用的是Memcached或SQLite,還需要下面這個做一些初始化的操作
77             app.UseEasyCaching();
78 
79 
80             app.UseMvc(routes =>
81             {
82                 routes.MapRoute(
83                     name: "default",
84                     template: "{controller=Home}/{action=Index}/{id?}");
85             });
86         }
87     }
88 }

  3.創建一個實體類

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 
 6 namespace Coreqi.EasyCaching.Models
 7 {
 8     [Serializable]
 9     public class User
10     {
11         public int id { get; set; }
12         public string username { get; set; }
13         public string password { get; set; }
14         public int enabled { get; set; }
15     }
16 }

  4.模擬一個服務層

 1 using Coreqi.EasyCaching.Models;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Threading.Tasks;
 6 
 7 namespace Coreqi.EasyCaching.Services
 8 {
 9     public class UserService:IUserService
10     {
11         public static IList<User> users;
12         public UserService()
13         {
14             users = new List<User>
15             {
16                 new User{ id = 1,username = "fanqi",password = "admin",enabled = 1},
17                 new User{ id = 2,username = "gaoxing",password = "admin",enabled = 1}
18             };
19         }
20         public IList<User> getAll()
21         {
22             return users;
23         }
24         public User getById(int id)
25         {
26             return users.FirstOrDefault(f => f.id == id);
27         }
28         public User add(User user)
29         {
30             users.Add(user);
31             return user;
32         }
33         public User modify(User user)
34         {
35             delete(user.id);
36             add(user);
37             return user;
38         }
39         public void delete(int id)
40         {
41             users.Remove(getById(id));
42         }
43     }
44 }

  5.控制器中使用緩存

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using Coreqi.EasyCaching.Models;
 6 using Coreqi.EasyCaching.Services;
 7 using EasyCaching.Core;
 8 using Microsoft.AspNetCore.Mvc;
 9 
10 namespace Coreqi.EasyCaching.Controllers
11 {
12     [Route("api/[controller]")]
13     public class UserController : Controller
14     {
15         private readonly IEasyCachingProvider _cache;
16         private readonly IUserService _service;
17         public UserController(IEasyCachingProvider cache, IUserService service)
18         {
19             this._cache = cache;
20             this._service = service;
21         }
22 
23         [HttpGet]
24         [Route("add")]
25         public async Task<IActionResult> Add()
26         {
27             IList<User> users = _service.getAll();
28             _cache.Set("users", users, TimeSpan.FromMinutes(2));
29             await _cache.SetAsync("users2", users, TimeSpan.FromMinutes(3));
30             return await Task.FromResult(new JsonResult(new { message = "添加成功!" }));
31         }
32 
33         [HttpGet]
34         [Route("remove")]
35         public async Task<IActionResult> Remove()
36         {
37             _cache.Remove("users");
38             await _cache.RemoveAsync("users2");
39             return await Task.FromResult(new JsonResult(new { message = "刪除成功!" }));
40         }
41 
42         [HttpGet]
43         [Route("get")]
44         public async Task<IActionResult> Get()
45         {
46             var users = _cache.Get<List<User>>("users");
47             var users2 = await _cache.GetAsync<List<User>>("users2");
48             return await Task.FromResult(new JsonResult(new { users1 = users.Value,users2 = users2.Value }));
49         }
50     }
51 }

 ⒊改造為Redis示例

  1.安裝Nuget包

    EasyCaching.Redis

  2.在Startup中配置服務及請求管道

 1         // This method gets called by the runtime. Use this method to add services to the container.
 2         public void ConfigureServices(IServiceCollection services)
 3         {
 4             services.AddSingleton<IUserService, UserService>();
 5             services.AddEasyCaching(option =>
 6             {
 7                 //使用redis
 8                 option.UseRedis(config =>
 9                 {
10                     config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
11                 }, "localhostRedis");
12             });
13 
14             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
15         }

  3.啟動程序

    然后就發現數據緩存到Redis中了,不過,

對於序列化,一般都會有一個基於BinaryFormatter的默認實現,因為這個並不依賴於第三方類庫,如果我們沒有指定其它的,EasyCaching就會使用這個去進行數據的序列化,除了這個默認的實現,EasyCaching還提供了三種額外的選擇。Newtonsoft.Json,MessagePack和Protobuf。切換方法如下:

    1.安裝以下Nuget包(你用那個序列化就裝那個)

1 EasyCaching.Serialization.Json
2 EasyCaching.Serialization.MessagePack
3 EasyCaching.Serialization.Protobuf

    2.配置序列化方式

 1         // This method gets called by the runtime. Use this method to add services to the container.
 2         public void ConfigureServices(IServiceCollection services)
 3         {
 4             services.AddSingleton<IUserService, UserService>();
 5             services.AddEasyCaching(option =>
 6             {
 7                 //使用redis
 8                 option.UseRedis(config =>
 9                 {
10                     config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
11                 }, "localhostRedis")
12                 //.WithMessagePack(); //使用MessagePack替換BinaryFormatter
13                 //.WithProtobuf();    //使用Protobuf替換BinaryFormatter
14                 .WithJson();    //使用Newtonsoft.Json替換BinaryFormatter
15             });
16 
17             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
18         }

 

 ⒋多示例支持

  多實例指在同一個項目中同時使用多個緩存提供者,包括多個同一類型的緩存提供者或着是不同類型的緩存提供者。

  在代碼中借助IEasyCachingProviderFactory來指定使用那個緩存提供者。

  1.先添加兩個緩存提供者

 1         // This method gets called by the runtime. Use this method to add services to the container.
 2         public void ConfigureServices(IServiceCollection services)
 3         {
 4             services.AddSingleton<IUserService, UserService>();
 5             services.AddEasyCaching(option =>
 6             {
 7                 option.UseInMemory("m1");   //配置一個InMemory,名稱為m1
 8                 //使用redis
 9                 option.UseRedis(config =>
10                 {
11                     config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
12                 }, "localhostRedis")
13                 //.WithMessagePack(); //使用MessagePack替換BinaryFormatter
14                 //.WithProtobuf();    //使用Protobuf替換BinaryFormatter
15                 .WithJson();    //使用Newtonsoft.Json替換BinaryFormatter
16             });
17 
18             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
19         }

  2.在代碼中使用

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using Coreqi.EasyCaching.Models;
 6 using Coreqi.EasyCaching.Services;
 7 using EasyCaching.Core;
 8 using Microsoft.AspNetCore.Mvc;
 9 
10 namespace Coreqi.EasyCaching.Controllers
11 {
12     [Route("api/[controller]")]
13     public class LoginController : Controller
14     {
15         private readonly IEasyCachingProviderFactory _cacheFactory;
16         private readonly IUserService _userService;
17         public LoginController(IEasyCachingProviderFactory cacheFactory, IUserService userService)
18         {
19             this._cacheFactory = cacheFactory;
20             this._userService = userService;
21         }
22 
23         [HttpGet]
24         [Route("add")]
25         public async Task<IActionResult> Add()
26         {
27             var _cache1 = _cacheFactory.GetCachingProvider("m1");   //獲取名字為m1的provider
28             var _cache2 = _cacheFactory.GetCachingProvider("localhostRedis");   //獲取名字為localhostRedis的provider
29             IList<User> users = _userService.getAll();
30             IList<string> loginNames = users.Select(s => s.username).ToList();
31             _cache1.Set("loginNames", loginNames, TimeSpan.FromMinutes(2));
32             await _cache2.SetAsync("users", users, TimeSpan.FromMinutes(2));
33             return await Task.FromResult(new JsonResult(new { message = "添加成功!" }));
34         }
35 
36         [HttpGet]
37         [Route("get")]
38         public async Task<IActionResult> Get()
39         {
40             var _cache1 = _cacheFactory.GetCachingProvider("m1");   //獲取名字為m1的provider
41             var _cache2 = _cacheFactory.GetCachingProvider("localhostRedis");   //獲取名字為localhostRedis的provider
42             IList<string> loginNames = _cache1.Get<List<string>>("loginNames").Value;
43             IList<User> users = (await _cache2.GetAsync<List<User>>("users")).Value;
44             return await Task.FromResult(new JsonResult(new { loginNames = loginNames,users = users}));
45         }
46     }
47 }

 ⒌緩存的Aop操作

  一句話,提供和Java Spring中的@Cacheable、@CacheEvict、@CachePut等注解類似的Aop操作

  例如,我們以前的查詢代碼一般是這樣的

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using Coreqi.EasyCaching.Models;
 6 using Coreqi.EasyCaching.Services;
 7 using EasyCaching.Core;
 8 using Microsoft.AspNetCore.Mvc;
 9 
10 namespace Coreqi.EasyCaching.Controllers
11 {
12     [Route("api/[controller]")]
13     public class CacheAopController : Controller
14     {
15         private readonly IEasyCachingProvider _cache;
16         private readonly IUserService _service;
17         public CacheAopController(IEasyCachingProvider cache, IUserService service)
18         {
19             this._cache = cache;
20             this._service = service;
21         }
22 
23         [HttpGet]
24         [Route("get/{id}")]
25         public async Task<User> GetUserByIdAsync(int id)
26         {
27             string cacheKey = $"user:{id}";
28             var userInfo = await _cache.GetAsync<User>(cacheKey);
29             if (userInfo.HasValue)
30             {
31                 return userInfo.Value;
32             }
33             var user = _service.getById(id);
34             if (user != null)
35             {
36                 _cache.Set<User>(cacheKey, user, TimeSpan.FromHours(2));
37             }
38             return user;
39         }
40     }
41 }

  先去查詢緩存數據,沒有的話再去查庫然后保存到緩存中,一個查詢是這樣,那么多的CRUD都這樣豈不是要寫到吐?

   而我們使用EasyCaching的緩存AOP來簡化這一操作。

  1.Nuget包

  EasyCaching.Interceptor.AspectCore

  2.在接口的定義上添加一個Attribute標識。

 1 using Coreqi.EasyCaching.Models;
 2 using EasyCaching.Core.Interceptor;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Threading.Tasks;
 7 
 8 namespace Coreqi.EasyCaching.Services
 9 {
10     public interface IUserService
11     {
12         IList<User> getAll();
13 
14         [EasyCachingAble(Expiration =10)]
15         User getById(int id);
16 
17         User add(User user);
18 
19         User modify(User user);
20 
21         void delete(int id);
22     }
23 }

  3.配置服務使其生效

 1         // This method gets called by the runtime. Use this method to add services to the container.
 2         public IServiceProvider ConfigureServices(IServiceCollection services)
 3         {
 4             services.AddSingleton<IUserService, UserService>(); //必須將Service添加到IOC中,否則AOP不成功
 5             services.AddEasyCaching(option =>
 6             {
 7                 option.UseInMemory("m1");   //配置一個InMemory,名稱為m1
 8                 //使用redis
 9                 option.UseRedis(config =>
10                 {
11                     config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
12                 }, "localhostRedis")
13                 //.WithMessagePack(); //使用MessagePack替換BinaryFormatter
14                 //.WithProtobuf();    //使用Protobuf替換BinaryFormatter
15                 .WithJson();    //使用Newtonsoft.Json替換BinaryFormatter
16             });
17 
18             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
19 
20             return services.ConfigureAspectCoreInterceptor(options =>
21             {
22                 options.CacheProviderName = "localhostRedis";   //指定要使用的緩存提供者名稱
23             });
24         }

  *也可以在Attribute特性上指定

 1 using Coreqi.EasyCaching.Models;
 2 using EasyCaching.Core.Interceptor;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Threading.Tasks;
 7 
 8 namespace Coreqi.EasyCaching.Services
 9 {
10     public interface IUserService
11     {
12         IList<User> getAll();
13 
14         [EasyCachingAble(Expiration =10,CacheProviderName ="m1")]
15         User getById(int id);
16 
17         User add(User user);
18 
19         User modify(User user);
20 
21         void delete(int id);
22     }
23 }

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using Coreqi.EasyCaching.Models;
 6 using Coreqi.EasyCaching.Services;
 7 using EasyCaching.Core;
 8 using Microsoft.AspNetCore.Mvc;
 9 
10 namespace Coreqi.EasyCaching.Controllers
11 {
12     [Route("api/[controller]")]
13     public class CacheAopController : Controller
14     {
15         private readonly IEasyCachingProvider _cache;
16         private readonly IUserService _service;
17         public CacheAopController(IEasyCachingProvider cache, IUserService service)
18         {
19             this._cache = cache;
20             this._service = service;
21         }
22 
23         [HttpGet]
24         [Route("get/{id}")]
25         public User GetUserById(int id)
26         {
27             return _service.getById(id);
28         }
29     }
30 }

 

完成上面的操作后可以在調用方法的時候優先取緩存,沒有緩存的時候才會去執行方法。

EasyCaching提供的AOP Attritebute有一些通用的屬性。

配置名 說明
CacheKeyPrefix 指定生成緩存鍵的前綴,正常情況下是用在修改和刪除的緩存上
CacheProviderName 可以指定特殊的provider名字
IsHightAvailability 緩存相關操作出現異常時,是否還能繼續執行業務方法

EasyCachingAble和EasyCachingPut還有一個同名和配置。

配置名 說明
Expiration key的過期時間,單位是秒

EasyCachingEvict有兩個特殊的配置。

配置名 說明
IsAll 這個要搭配CacheKeyPrefix來用,就是刪除這個前綴的所有key
IsBefore 在業務方法執行之前刪除緩存還是執行之后

⒍支持Diagnostics【診斷系統】

  提供了Diagnostics的支持方便接入第三方的APM,實現追蹤。

  有一個接入Jaeger的一個案例。大家可以去看看。

⒎二級緩存

二級緩存,多級緩存,其實在緩存的小世界中還算是一個比較重要的東西!

一個最為頭疼的問題就是不同級的緩存如何做到近似實時的同步。

在EasyCaching中,二級緩存的實現邏輯大致就是下面的這張圖。

如果某個服務器上面的本地緩存被修改了,就會通過緩存總線去通知其他服務器把對應的本地緩存移除掉

  1.添加Nuget包。

1 EasyCaching.InMemory
2 EasyCaching.Redis
3 EasyCaching.HybridCache
4 EasyCaching.Bus.Redis

  2.添加配置

 1             services.AddEasyCaching(option =>
 2             {
 3                 option.UseInMemory("m1");   //配置一個InMemory,名稱為m1
 4                 //使用redis
 5                 option.UseRedis(config =>
 6                 {
 7                     config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
 8                     config.DBConfig.Database = 5;
 9                 }, "localhostRedis")
10                 //.WithMessagePack(); //使用MessagePack替換BinaryFormatter
11                 //.WithProtobuf();    //使用Protobuf替換BinaryFormatter
12                 .WithJson();    //使用Newtonsoft.Json替換BinaryFormatter
13 
14                 //使用Hybird
15                 option.UseHybrid(config =>
16                 {
17                     config.EnableLogging = false;   //是否開啟日志
18                     config.TopicName = "test_topic";    //緩存總線的訂閱主題
19                     config.LocalCacheProviderName = "m1";   //本地緩存的名字
20                     config.DistributedCacheProviderName = "localhostRedis"; //分布式緩存的名字
21                 });
22 
23                 //使用redis作為緩存總線
24                 option.WithRedisBus(config =>
25                 {
26                     config.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
27                     config.Database = 6;
28                 });
29 
30                 //使用CSRedis作為緩存總線
31                 //option.WithCSRedisBus(config =>
32                 //{
33                 //    config.ConnectionStrings = new List<string>
34                 //    {
35                 //        "127.0.0.1:6379,defaultDatabase=6,poolsize=10"
36                 //    };
37                 //});
38 
39                 //使用RabbitMq作為緩存總線
40                 //option.WithRabbitMQBus(config =>
41                 //{
42                 //    config = new RabbitMQBusOptions();
43                 //});
44             });

  3.使用

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using EasyCaching.Core;
 6 using Microsoft.AspNetCore.Mvc;
 7 
 8 namespace Coreqi.EasyCaching.Controllers
 9 {
10     [Route("api/[controller]")]
11     public class HybridCachingController : Controller
12     {
13         private readonly IHybridCachingProvider _provider;
14         public HybridCachingController(IHybridCachingProvider provider)
15         {
16             this._provider = provider;
17         }
18 
19         [HttpGet]
20         [Route("add")]
21         public string Add()
22         {
23             string cacheKey = "coreqiTest";
24             _provider.Set(cacheKey, "1111111", TimeSpan.FromSeconds(30));
25             return "添加成功!";
26         }
27     }
28 }

⒏特殊的Redis緩存提供者

Redis支持多種數據結構,還有一些原子遞增遞減的操作等等。為了支持這些操作,EasyCaching提供了一個獨立的接口,IRedisCachingProvider。

這個接口,目前也只支持了百分之六七十常用的一些操作,還有一些可能用的少的就沒加進去。

同樣的,這個接口也是支持多實例的,也可以通過IEasyCachingProviderFactory來獲取不同的provider實例。

在注入的時候,不需要額外的操作,和添加Redis是一樣的。不同的是,在使用的時候,不再是用IEasyCachingProvider,而是要用IRedisCachingProvider

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using EasyCaching.Core;
 6 using Microsoft.AspNetCore.Mvc;
 7 
 8 namespace Coreqi.EasyCaching.Controllers
 9 {
10     [Route("api/[controller]")]
11     public class MultiRedisController : Controller
12     {
13         private readonly IRedisCachingProvider _redis1;
14         private readonly IRedisCachingProvider _redis2;
15 
16         public MultiRedisController(IEasyCachingProviderFactory factory)
17         {
18             this._redis1 = factory.GetRedisProvider("redis1");
19             this._redis2 = factory.GetRedisProvider("redis2");
20         }
21 
22         [HttpGet]
23         [Route("get")]
24         public string Get()
25         {
26             _redis1.StringSet("keyredis1", "val");
27 
28             var res1 = _redis1.StringGet("keyredis1");
29             var res2 = _redis2.StringGet("keyredis1");
30 
31             return $"redis1 cached value: {res1}, redis2 cached value : {res2}";
32         }
33     }
34 }

⒐EasyCaching擴展性功能

除了這些基礎功能,還有一些擴展性的功能,在這里要非常感謝yrinleung,他把EasyCaching和WebApiClient,CAP等項目結合起來了。感興趣的可以看看這個項目EasyCaching.Extensions

 

參考文章:一篇短文帶您了解一下EasyCaching-----作者@Catcher ( 黃文清 )

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM