<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

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

        來源:懂視網 責編:小采 時間:2020-11-27 22:35:02
        文檔

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

        .NET Core中使用Redis與Memcached的序列化問題詳析:前言 在使用分布式緩存的時候,都不可避免的要做這樣一步操作,將數據序列化后再存儲到緩存中去。 序列化這一操作,或許是顯式的,或許是隱式的,這個取決于使用的package是否有幫我們做這樣一件事。 本文會拿在.NET Core環境下使用Redis和Memcache
        推薦度:
        導讀.NET Core中使用Redis與Memcached的序列化問題詳析:前言 在使用分布式緩存的時候,都不可避免的要做這樣一步操作,將數據序列化后再存儲到緩存中去。 序列化這一操作,或許是顯式的,或許是隱式的,這個取決于使用的package是否有幫我們做這樣一件事。 本文會拿在.NET Core環境下使用Redis和Memcache

        前言

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

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

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

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

        常見的序列化方法

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

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

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

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

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

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

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

      1. System.Runtime.Serialization.Formatters.Binary
      2. Newtonsoft.Json
      3. protobuf-net
      4. MessagePack-CSharp
      5. ....
      6. 在這些庫中

        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}");
        }

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

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

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

        再來看看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并沒有什么強制性的要求,所以去掉或保留都可以。

        運行起來是比較順利的。

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

        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}");
        }

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

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

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

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

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

        最后來看看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);

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

        其實序列化這一步,對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。

        需要注意下面兩個說明

      7. 2.1.0版本之后,Transcoder由ITranscoder類型變更為string類型。
      8. 2.1.0.5版本之后,可以通過依賴注入的形式來完成,而不用指定string類型的Transcoder。
      9. 讀寫緩存的操作如下:

        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,省去了不少操作!!

        這個時候,我們有兩種方式來使用這個新定義的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...
        }

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

        最后的結果:

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

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

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

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

        使用EasyCaching來簡化操作

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

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

      10. BinaryFormatter
      11. MessagePack
      12. Json
      13. ProtoBuf
      14. 如果這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}"; 
         }
        }
      15. 使用的時候,在構造函數對IEasyCachingProvider進行依賴注入即可。
      16. Redis默認用了BinaryFormatter來進行序列化。
      17. 下面我們要如何去替換我們想要的新的序列化方法呢?

        以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

        好了,

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

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

        .NET Core中使用Redis與Memcached的序列化問題詳析:前言 在使用分布式緩存的時候,都不可避免的要做這樣一步操作,將數據序列化后再存儲到緩存中去。 序列化這一操作,或許是顯式的,或許是隱式的,這個取決于使用的package是否有幫我們做這樣一件事。 本文會拿在.NET Core環境下使用Redis和Memcache
        推薦度:
        標簽: net core redis
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 国产亚洲午夜高清国产拍精品| 免费观看的av毛片的网站| 亚洲午夜激情视频| 免费无码午夜福利片 | 拨牐拨牐x8免费| 久久精品国产99国产精品亚洲| 69视频免费在线观看| 亚洲电影中文字幕| 四虎在线视频免费观看视频| 亚洲国产日产无码精品| 免费看韩国黄a片在线观看| 亚洲精华国产精华精华液| 国产高清免费在线| 一级一级一片免费高清| 亚洲日产无码中文字幕| 久久这里只精品热免费99| 亚洲欧洲久久精品| 国产免费69成人精品视频| 一区二区三区在线免费观看视频| 亚洲一区二区三区乱码A| 日本视频免费高清一本18| 亚洲经典在线观看| 免费看美女让人桶尿口| 人妻巨大乳hd免费看| 亚洲AV人人澡人人爽人人夜夜 | 国产精品怡红院永久免费| 亚洲性色AV日韩在线观看| mm1313亚洲精品无码又大又粗 | 日本免费人成黄页网观看视频| 色哟哟国产精品免费观看| 亚洲高清在线播放| 最近中文字幕mv免费高清电影 | 日韩欧美一区二区三区免费观看| 国产AV无码专区亚洲AV麻豆丫| 久久亚洲国产精品123区| 99国产精品免费视频观看| 亚洲爆乳大丰满无码专区| 国产av天堂亚洲国产av天堂| 成人毛片免费在线观看| 中国内地毛片免费高清| 亚洲欧好州第一的日产suv|