亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > .NET > 正文

.NET Core中使用Redis與Memcached的序列化問題詳析

2024-07-10 13:33:00
字體:
來源:轉載
供稿:網友

前言

在使用分布式緩存的時候,都不可避免的要做這樣一步操作,將數據序列化后再存儲到緩存中去。

序列化這一操作,或許是顯式的,或許是隱式的,這個取決于使用的package是否有幫我們做這樣一件事。

本文會拿在.NET Core環境下使用Redis和Memcached來當例子說明,其中,Redis主要是用StackExchange.Redis,Memcached主要是用EnyimMemcachedCore。

先來看看一些我們常用的序列化方法。

常見的序列化方法

或許,比較常見的做法就是將一個對象序列化成byte數組,然后用這個數組和緩存服務器進行交互。

關于序列化,業界有不少算法,這些算法在某種意義上表現的結果就是速度和體積這兩個問題。

其實當操作分布式緩存的時候,我們對這兩個問題其實也是比較看重的!

在同等條件下,序列化和反序列化的速度,可以決定執行的速度是否能快一點。

序列化的結果,也就是我們要往內存里面塞的東西,如果能讓其小一點,也是能節省不少寶貴的內存空間。

當然,本文的重點不是去比較那種序列化方法比較牛逼,而是介紹怎么結合緩存去使用,也順帶提一下在使用緩存時,序列化可以考慮的一些點。

下面來看看一些常用的序列化的庫:

  • System.Runtime.Serialization.Formatters.Binary
  • Newtonsoft.Json
  • protobuf-net
  • MessagePack-CSharp
  • ....

在這些庫中

System.Runtime.Serialization.Formatters.Binary是.NET類庫中本身就有的,所以想在不依賴第三方的packages時,這是個不錯的選擇。

Newtonsoft.Json應該不用多說了。

protobuf-net是.NET實現的Protocol Buffers。

MessagePack-CSharp是極快的MessagePack序列化工具。

這幾種序列化的庫也是筆者平時有所涉及的,還有一些不熟悉的就沒列出來了!

在開始之前,我們先定義一個產品類,后面相關的操作都是基于這個類來說明。

public class Product{ public int Id { get; set; } public string Name { get; set; }}

下面先來看看Redis的使用。

Redis

在介紹序列化之前,我們需要知道在StackExchange.Redis中,我們要存儲的數據都是以RedisValue的形式存在的。并且RedisValue是支持string,byte[]等多種數據類型的。

換句話說就是,在我們使用StackExchange.Redis時,存進Redis的數據需要序列化成RedisValue所支持的類型。

這就是前面說的需要顯式的進行序列化的操作。

先來看看.NET類庫提供的BinaryFormatter。

序列化的操作

using (var ms = new MemoryStream()){ formatter.Serialize(ms, product);   db.StringSet("binaryformatter", ms.ToArray(), TimeSpan.FromMinutes(1));}

反序列化的操作

var value = db.StringGet("binaryformatter");using (var ms = new MemoryStream(value)){ var desValue = (Product)(new BinaryFormatter().Deserialize(ms)); Console.WriteLine($"{desValue.Id}-{desValue.Name}");}

寫起來還是挺簡單的,但是這個時候運行代碼會提示下面的錯誤!

.net,core,redis,asp.net,memcached

說是我們的Product類沒有標記Serializable。下面就是在Product類加上[Serializable]。

.net,core,redis,asp.net,memcached

再次運行,已經能成功了。

.net,core,redis,asp.net,memcached

再來看看Newtonsoft.Json

序列化的操作

using (var ms = new MemoryStream()){ using (var sr = new StreamWriter(ms, Encoding.UTF8)) using (var jtr = new JsonTextWriter(sr)) { jsonSerializer.Serialize(jtr, product); }   db.StringSet("json", ms.ToArray(), TimeSpan.FromMinutes(1));}

反序列化的操作

var bytes = db.StringGet("json");using (var ms = new MemoryStream(bytes))using (var sr = new StreamReader(ms, Encoding.UTF8))using (var jtr = new JsonTextReader(sr)){ var desValue = jsonSerializer.Deserialize<Product>(jtr); Console.WriteLine($"{desValue.Id}-{desValue.Name}");}

由于Newtonsoft.Json對我們要進行序列化的類有沒有加上Serializable并沒有什么強制性的要求,所以去掉或保留都可以。

運行起來是比較順利的。

.net,core,redis,asp.net,memcached

當然,也可以用下面的方式來處理的:

var objStr = JsonConvert.SerializeObject(product);db.StringSet("json", Encoding.UTF8.GetBytes(objStr), TimeSpan.FromMinutes(1));var resStr = Encoding.UTF8.GetString(db.StringGet("json"));var res = JsonConvert.DeserializeObject<Product>(resStr);

再來看看ProtoBuf

序列化的操作

using (var ms = new MemoryStream()){ Serializer.Serialize(ms, product); db.StringSet("protobuf", ms.ToArray(), TimeSpan.FromMinutes(1));}

反序列化的操作

var value = db.StringGet("protobuf");using (var ms = new MemoryStream(value)){ var desValue = Serializer.Deserialize<Product>(ms);  Console.WriteLine($"{desValue.Id}-{desValue.Name}");}

用法看起來也是中規中矩。

但是想這樣就跑起來是沒那么順利的。錯誤提示如下:

.net,core,redis,asp.net,memcached

處理方法有兩個,一個是在Product類和屬性上面加上對應的Attribute,另一個是用ProtoBuf.Meta在運行時來處理這個問題。可以參考AutoProtobuf的實現。

下面用第一種方式來處理,直接加上[ProtoContract][ProtoMember]這兩個Attribute。

.net,core,redis,asp.net,memcached

再次運行就是我們所期望的結果了。

.net,core,redis,asp.net,memcached

最后來看看MessagePack,據其在Github上的說明和對比,似乎比其他序列化的庫都強悍不少。

它默認也是要像Protobuf那樣加上MessagePackObjectKey這兩個Attribute的。

不過它也提供了一個IFormatterResolver參數,可以讓我們有所選擇。

下面用的是不需要加Attribute的方法來演示。

序列化的操作

var serValue = MessagePackSerializer.Serialize(product, ContractlessStandardResolver.Instance);db.StringSet("messagepack", serValue, TimeSpan.FromMinutes(1));

反序列化的操作

var value = db.StringGet("messagepack");var desValue = MessagePackSerializer.Deserialize<Product>(value, ContractlessStandardResolver.Instance);

此時運行起來也是正常的。

.net,core,redis,asp.net,memcached

其實序列化這一步,對Redis來說是十分簡單的,因為它顯式的讓我們去處理,然后把結果進行存儲。

上面演示的4種方法,從使用上看,似乎都差不多,沒有太大的區別。

如果拿Redis和Memcached對比,會發現Memcached的操作可能比Redis的略微復雜了一點。

下面來看看Memcached的使用。

Memcached

EnyimMemcachedCore默認有一個 DefaultTranscoder
,對于常規的數據類型(int,string等)本文不細說,只是特別說明object類型。

在DefaultTranscoder中,對Object類型的數據進行序列化是基于Bson的。

還有一個BinaryFormatterTranscoder是屬于默認的另一個實現,這個就是基于我們前面的說.NET類庫自帶的System.Runtime.Serialization.Formatters.Binary

先來看看這兩種自帶的Transcoder要怎么用。

先定義好初始化Memcached相關的方法,以及讀寫緩存的方法。

初始化Memcached如下:

private static void InitMemcached(string transcoder = ""){ IServiceCollection services = new ServiceCollection(); services.AddEnyimMemcached(options => {  options.AddServer("127.0.0.1", 11211);  options.Transcoder = transcoder; }); services.AddLogging(); IServiceProvider serviceProvider = services.BuildServiceProvider(); _client = serviceProvider.GetService<IMemcachedClient>() as MemcachedClient;}

這里的transcoder就是我們要選擇那種序列化方法(針對object類型),如果是空就用Bson,如果是BinaryFormatterTranscoder用的就是BinaryFormatter。

需要注意下面兩個說明

  • 2.1.0版本之后,Transcoder由ITranscoder類型變更為string類型。
  • 2.1.0.5版本之后,可以通過依賴注入的形式來完成,而不用指定string類型的Transcoder。

讀寫緩存的操作如下:

private static void MemcachedTrancode(Product product){ _client.Store(Enyim.Caching.Memcached.StoreMode.Set, "defalut", product, DateTime.Now.AddMinutes(1)); Console.WriteLine("serialize succeed!"); var desValue = _client.ExecuteGet<Product>("defalut").Value; Console.WriteLine($"{desValue.Id}-{desValue.Name}"); Console.WriteLine("deserialize succeed!");}

我們在Main方法中的代碼如下 :

static void Main(string[] args){ Product product = new Product {  Id = 999,  Name = "Product999" }; //Bson string transcoder = ""; //BinaryFormatter //string transcoder = "BinaryFormatterTranscoder";    InitMemcached(transcoder); MemcachedTrancode(product); Console.ReadKey();}

對于自帶的兩種Transcoder,跑起來還是比較順利的,在用BinaryFormatterTranscoder時記得給Product類加上[Serializable]就好!

下面來看看如何借助MessagePack來實現Memcached的Transcoder。

這里繼承DefaultTranscoder就可以了,然后重寫SerializeObject,DeserializeObject和Deserialize這三個方法。

public class MessagePackTranscoder : DefaultTranscoder{ protected override ArraySegment<byte> SerializeObject(object value) {  return MessagePackSerializer.SerializeUnsafe(value, TypelessContractlessStandardResolver.Instance); } public override T Deserialize<T>(CacheItem item) {  return (T)base.Deserialize(item); } protected override object DeserializeObject(ArraySegment<byte> value) {  return MessagePackSerializer.Deserialize<object>(value, TypelessContractlessStandardResolver.Instance); }}

慶幸的是,MessagePack有方法可以讓我們直接把一個object序列化成ArraySegment,也可以把ArraySegment 反序列化成一個object!!

相比Json和Protobuf,省去了不少操作?。?/p>

這個時候,我們有兩種方式來使用這個新定義的MessagePackTranscoder。

方式一 :在使用的時候,我們只需要替換前面定義的transcoder變量即可(適用>=2.1.0版本)。

string transcoder = "CachingSerializer.MessagePackTranscoder,CachingSerializer";

注:如果使用方式一來處理,記得將transcoder的拼寫不要錯,并且要帶上命名空間,不然創建的Transcoder會一直是null,從而走的就是Bson了! 本質是 Activator.CreateInstance,應該不用多解釋。

方式二:通過依賴注入的方式來處理(適用>=2.1.0.5版本)

private static void InitMemcached(string transcoder = ""){ IServiceCollection services = new ServiceCollection(); services.AddEnyimMemcached(options => {  options.AddServer("127.0.0.1", 11211);  //這里保持空字符串或不賦值,就會走下面的AddSingleton  //如果這里賦了正確的值,后面的AddSingleton就不會起作用了  options.Transcoder = transcoder; }); //使用新定義的MessagePackTranscoder services.AddSingleton<ITranscoder, MessagePackTranscoder>(); //others...}

運行之前加個斷點,確保真的進了我們重寫的方法中。

.net,core,redis,asp.net,memcached

最后的結果:

.net,core,redis,asp.net,memcached

Protobuf和Json的,在這里就不一一介紹了,這兩個處理起來比MessagePack復雜了不少??梢詤⒖糓emcachedTranscoder這個開源項目,也是MessagePack作者寫的,雖然是5年前的,但是一樣的好用。

對于Redis來說,在調用Set方法時要顯式的將我們的值先進行序列化,不那么簡潔,所以都會進行一次封裝在使用。

對于Memcached來說,在調用Set方法的時候雖然不需要顯式的進行序列化,但是有可能要我們自己去實現一個Transcoder,這也是有點麻煩的。

下面給大家推薦一個簡單的緩存庫來處理這些問題。

使用EasyCaching來簡化操作

EasyCaching是筆者在業余時間寫的一個簡單的開源項目,主要目的是想簡化緩存的操作,目前也在不斷的完善中。

EasyCaching提供了前面所說的4種序列化方法可供選擇:

  • BinaryFormatter
  • MessagePack
  • Json
  • ProtoBuf

如果這4種都不滿足需求,也可以自己寫一個,只要實現IEasyCachingSerializer這個接口相應的方法即可。

Redis

在介紹怎么用序列化之前,先來簡單看看是怎么用的(用ASP.NET Core Web API做演示)。

添加Redis相關的nuget包

Install-Package EasyCaching.Redis

修改Startup

public class Startup{ //... public void ConfigureServices(IServiceCollection services) {  //other services.  //Important step for Redis Caching    services.AddDefaultRedisCache(option=>  {       option.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));   option.Password = "";  }); }}

然后在控制器中使用:

[Route("api/[controller]")]public class ValuesController : Controller{ private readonly IEasyCachingProvider _provider; public ValuesController(IEasyCachingProvider provider) {  this._provider = provider; } [HttpGet] public string Get() {  //Set  _provider.Set("demo", "123", TimeSpan.FromMinutes(1));  //Get without data retriever  var res = _provider.Get<string>("demo");  _provider.Set("product:1", new Product { Id = 1, Name = "name"}, TimeSpan.FromMinutes(1))  var product = _provider.Get<Product>("product:1");  return $"{res.Value}-{product.Value.Id}-{product.Value.Name}";  }}
  • 使用的時候,在構造函數對IEasyCachingProvider進行依賴注入即可。
  • Redis默認用了BinaryFormatter來進行序列化。

下面我們要如何去替換我們想要的新的序列化方法呢?

以MessagePack為例,先通過nuget安裝package

Install-Package EasyCaching.Serialization.MessagePack

然后只需要在ConfigureServices方法中加上下面這句就可以了。

public void ConfigureServices(IServiceCollection services){ //others.. services.AddDefaultMessagePackSerializer();}

Memcached

同樣先來簡單看看是怎么用的(用ASP.NET Core Web API做演示)。

添加Memcached的nuget包

Install-Package EasyCaching.Memcached

修改Startup

public class Startup{ //... public void ConfigureServices(IServiceCollection services) {  services.AddMvc();  //Important step for Memcached Cache  services.AddDefaultMemcached(option=>  {       option.AddServer("127.0.0.1",11211);     });   } public void Configure(IApplicationBuilder app, IHostingEnvironment env) {  //Important step for Memcache Cache  app.UseDefaultMemcached();  }}

在控制器中使用時和Redis是一模一樣的。

這里需要注意的是,在EasyCaching中,默認使用的序列化方法并不是DefaultTranscoder中的Bson,而是BinaryFormatter

如何去替換默認的序列化操作呢?

同樣以MessagePack為例,先通過nuget安裝package

Install-Package EasyCaching.Serialization.MessagePack

剩下的操作和Redis是一樣的!

public void ConfigureServices(IServiceCollection services){ //others.. services.AddDefaultMemcached(op=> {      op.AddServer("127.0.0.1",11211); }); //specify the Transcoder use messagepack serializer. services.AddDefaultMessagePackSerializer();}

因為在EasyCaching中,有一個自己的Transcoder,這個Transcoder對IEasyCachingSerializer進行注入,所以只需要指定對應的Serializer即可。

總結

一、 先來看看文中提到的4種序列化的庫

System.Runtime.Serialization.Formatters.Binary在使用上需要加上[Serializable],效率是最慢的,優勢就是類庫里面就有,不需要額外引用其他package。

Newtonsoft.Json使用起來比較友善,可能是用的多的緣故,也不需要我們對已經定義好的類加一些Attribute上去。

protobuf-net使用起來可能就略微麻煩一點,可以在定義類的時候加上相應的Attribute,也可以在運行時去處理(要注意處理子類),不過它的口碑還是不錯的。

MessagePack-CSharp雖然可以不添加Attribute,但是不加比加的時候也會有所損耗。

至于如何選擇,可能就要視情況而定了!

有興趣的可以用BenchmarkDotNet跑跑分,我也簡單寫了一個可供參考:SerializerBenchmark

二、在對緩存操作的時候,可能會更傾向于“隱式”操作,能直接將一個object扔進去,也可以直接將一個object拿出來,至少能方便使用方。

三、序列化操作時,Redis要比Memcached簡單一些。

最后,如果您在使用EasyCaching,有問題或建議可以聯系我!

前半部分的示例代碼:CachingSerializer

后半部分的示例代碼:sample

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到ASP.NET教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产成人aa精品一区在线播放| 亚洲激情视频在线| 日韩成人在线视频观看| 欧美激情一区二区久久久| 久久精品中文字幕| 日韩精品免费在线播放| 日韩精品高清在线观看| 国产一区香蕉久久| 久久精品视频免费播放| 欧美日韩亚洲91| 国产精品日韩欧美| 精品国产乱码久久久久酒店| 国产偷国产偷亚洲清高网站| 精品国产一区二区三区久久久| 久久免费视频观看| 在线播放精品一区二区三区| 91免费国产视频| 亚洲欧洲一区二区三区在线观看| 欧美性猛交xxxx乱大交3| 亚洲精品视频网上网址在线观看| 亚洲影视九九影院在线观看| 国产午夜精品免费一区二区三区| 亚洲女人天堂视频| 九九热精品在线| 亚洲成人网在线观看| 91国自产精品中文字幕亚洲| 亚洲一区av在线播放| 成人黄色激情网| 这里只有精品视频在线| 最新的欧美黄色| 亚洲精品99999| 亚洲男子天堂网| www国产精品视频| 欧美成人精品一区二区三区| 91成人国产在线观看| 国产在线a不卡| 欧美日韩国产123| 国产精品美女主播在线观看纯欲| 亚洲一区999| 日韩欧美一区二区三区| 日本乱人伦a精品| 中文欧美在线视频| 精品亚洲一区二区三区在线观看| 欧美成人合集magnet| 亚洲欧美国产日韩天堂区| 一本大道久久加勒比香蕉| 亚洲精品国产综合区久久久久久久| 精品国产一区二区三区久久| 亚洲japanese制服美女| 欧美日韩国产91| 一区二区三区视频观看| 亚洲欧美制服另类日韩| 亚洲性夜色噜噜噜7777| 久久久精品国产| 国产精品ⅴa在线观看h| 久久久久久国产精品三级玉女聊斋| 少妇av一区二区三区| 亚洲欧美国产精品专区久久| 538国产精品一区二区在线| 中文字幕在线视频日韩| 国产精品自拍偷拍| 欧美大片欧美激情性色a∨久久| 亚洲一二在线观看| 国产一区二区黑人欧美xxxx| 日韩av影片在线观看| 欧美成人中文字幕在线| 久久人人爽人人| 亚洲日本欧美中文幕| 国产精品99久久久久久久久久久久| 国产精品成人免费视频| 成人国产亚洲精品a区天堂华泰| 亚洲综合第一页| 久久精品国产96久久久香蕉| 日韩欧美在线观看| 国产日韩中文字幕| 欧美日本中文字幕| 亚洲电影免费在线观看| 日韩av在线高清| 成人国产精品色哟哟| 欧美日韩精品在线观看| xxx成人少妇69| 亚洲女性裸体视频| 国产亚洲一级高清| 亚洲欧美日韩精品| 国产成人精品日本亚洲专区61| 97精品伊人久久久大香线蕉| 精品久久香蕉国产线看观看亚洲| 精品国产91久久久| 亚洲美女久久久| 欧美精品videosex极品1| 一区二区欧美久久| 久久97久久97精品免视看| 国产精品电影在线观看| 欧美精品videofree1080p| 色综合亚洲精品激情狠狠| 日韩欧美在线免费观看| 成人在线免费观看视视频| 欧美激情一级二级| 日韩高清电影免费观看完整版| 久久久精品一区二区| 丝袜亚洲另类欧美重口| 欧洲永久精品大片ww免费漫画| 性色av一区二区三区在线观看| 久久青草福利网站| 欧美日韩国产页| 亲爱的老师9免费观看全集电视剧| 精品一区二区三区三区| 国产主播在线一区| 亚洲欧美日本精品| 一级做a爰片久久毛片美女图片| 亚洲电影免费观看高清| 久久久久久久久久久久久久久久久久av| 亚洲欧美日韩天堂| 亚洲最大成人在线| 国产精品色悠悠| 成人精品在线视频| 亚洲视频综合网| 欧美性猛交xxxx乱大交极品| 欧美老少做受xxxx高潮| 日韩av在线一区| 亚洲女人被黑人巨大进入al| 精品视频偷偷看在线观看| 欧美日韩亚洲系列| 中文字幕精品视频| 亚洲男人7777| 亚洲天堂av网| 国产精品久久久久久久久久久久| 国产91精品黑色丝袜高跟鞋| 国产日本欧美一区二区三区在线| 国产欧美日韩丝袜精品一区| 92版电视剧仙鹤神针在线观看| 国产成人精品日本亚洲| xxxxxxxxx欧美| 热re91久久精品国99热蜜臀| 欧亚精品在线观看| 亚洲高清久久久久久| 久久精品电影网| 国产精品视频网站| 久久久久久亚洲精品中文字幕| 亚洲最大成人在线| 欧美日韩精品在线| 日韩一区二区三区在线播放| 欧美大片在线看免费观看| 一区二区三区高清国产| 欧美日韩在线视频一区| 日韩中文字幕视频在线观看| 91精品久久久久久久久久久久久| 久久精品视频免费播放| 国产精品美女在线| 98精品国产自产在线观看| 97国产成人精品视频| 亚洲一区二区三区乱码aⅴ蜜桃女| 国产精品pans私拍| 欧美激情第三页| 欧美亚洲一级片| 精品久久久国产精品999| 久久久久久久久久久免费精品| 精品久久久久久久久久久| 国产精品久久久av久久久| 欧美一级bbbbb性bbbb喷潮片| 在线播放国产一区二区三区| 欧美最猛性xxxxx(亚洲精品)| 欧美大片欧美激情性色a∨久久| 国产精品99蜜臀久久不卡二区|