您现在的位置是:网站首页> 编程资料编程资料
.NET Core中使用Redis与Memcached的序列化问题详析_实用技巧_
2023-05-24
449人已围观
简介 .NET Core中使用Redis与Memcached的序列化问题详析_实用技巧_
前言
在使用分布式缓存的时候,都不可避免的要做这样一步操作,将数据序列化后再存储到缓存中去。
序列化这一操作,或许是显式的,或许是隐式的,这个取决于使用的package是否有帮我们做这样一件事。
本文会拿在.NET Core环境下使用Redis和Memcached来当例子说明,其中,Redis主要是用StackExchange.Redis,Memcached主要是用EnyimMemcachedCore。
先来看看一些我们常用的序列化方法。
常见的序列化方法
或许,比较常见的做法就是将一个对象序列化成byte数组,然后用这个数组和缓存服务器进行交互。
关于序列化,业界有不少算法,这些算法在某种意义上表现的结果就是速度和体积这两个问题。
其实当操作分布式缓存的时候,我们对这两个问题其实也是比较看重的!
在同等条件下,序列化和反序列化的速度,可以决定执行的速度是否能快一点。
序列化的结果,也就是我们要往内存里面塞的东西,如果能让其小一点,也是能节省不少宝贵的内存空间。
当然,本文的重点不是去比较那种序列化方法比较牛逼,而是介绍怎么结合缓存去使用,也顺带提一下在使用缓存时,序列化可以考虑的一些点。
下面来看看一些常用的序列化的库:
在这些库中
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(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(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(ms); Console.WriteLine($"{desValue.Id}-{desValue.Name}"); } 用法看起来也是中规中矩。
但是想这样就跑起来是没那么顺利的。错误提示如下:

处理方法有两个,一个是在Product类和属性上面加上对应的Attribute,另一个是用ProtoBuf.Meta在运行时来处理这个问题。可以参考AutoProtobuf的实现。
下面用第一种方式来处理,直接加上[ProtoContract]和[ProtoMember]这两个Attribute。

再次运行就是我们所期望的结果了。

最后来看看MessagePack,据其在Github上的说明和对比,似乎比其他序列化的库都强悍不少。
它默认也是要像Protobuf那样加上MessagePackObject和Key这两个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(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() 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("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 SerializeObject(object value) { return MessagePackSerializer.SerializeUnsafe(value, TypelessContractlessStandardResolver.Instance); } public override T Deserialize(CacheItem item) { return (T)base.Deserialize(item); } protected override object DeserializeObject(ArraySegment value) { return MessagePackSerializer.Deserialize 庆幸的是,MessagePack有方法可以让我们直接把一个object序列化成ArraySegment
相比Json和Protobuf,省去了不少操作!!
这个时候,我们有两种方式来使用这个新定义的MessagePackTranscoder。
方式一 :在使用的时候,我们只需要替换前面定义的transcoder变量即可(适用>=2.1.0版本)。
string transcoder = "CachingSerializer.MessagePackTranscoder,CachingSerializer";
注:提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
相关内容
- ASP.net WebAPI跨域调用问题的解决方法_实用技巧_
- 详解Asp.Net Core 2.1+的视图缓存(响应缓存)_实用技巧_
- 基于Jexus-5.6.3使用详解_实用技巧_
- .Net Core和jexus配置HTTPS服务方法_实用技巧_
- 如何为asp.net core添加protobuf支持详解_实用技巧_
- asp.net core项目中如何使用html文件_实用技巧_
- .NET Core 实现定时抓取网站文章并发送到邮箱_实用技巧_
- 详解Asp.net web.config customErrors 如何设置_实用技巧_
- XAML: 自定义控件中事件处理的最佳实践方法_实用技巧_
- ASP.NET没有魔法_ASP.NET MVC 模型验证方法_实用技巧_
