- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章.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序列化工具.
这几种序列化的库也是笔者平时有所涉及的,还有一些不熟悉的就没列出来了! 。
在开始之前,我们先定义一个产品类,后面相关的操作都是基于这个类来说明.
1
2
3
4
5
|
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.
序列化的操作 。
1
2
3
4
5
|
using
(var ms =
new
MemoryStream())
{
formatter.Serialize(ms, product);
db.StringSet(
"binaryformatter"
, ms.ToArray(), TimeSpan.FromMinutes(1));
}
|
反序列化的操作 。
1
2
3
4
5
6
|
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 。
序列化的操作 。
1
2
3
4
5
6
7
8
9
|
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));
}
|
反序列化的操作 。
1
2
3
4
5
6
7
8
|
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并没有什么强制性的要求,所以去掉或保留都可以.
运行起来是比较顺利的.
当然,也可以用下面的方式来处理的:
1
2
3
4
|
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 。
序列化的操作 。
1
2
3
4
5
|
using
(var ms =
new
MemoryStream())
{
Serializer.Serialize(ms, product);
db.StringSet(
"protobuf"
, ms.ToArray(), TimeSpan.FromMinutes(1));
}
|
反序列化的操作 。
1
2
3
4
5
6
|
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那样加上MessagePackObject和Key这两个Attribute的.
不过它也提供了一个IFormatterResolver参数,可以让我们有所选择.
下面用的是不需要加Attribute的方法来演示.
序列化的操作 。
1
2
|
var serValue = MessagePackSerializer.Serialize(product, ContractlessStandardResolver.Instance);
db.StringSet(
"messagepack"
, serValue, TimeSpan.FromMinutes(1));
|
反序列化的操作 。
1
2
|
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如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
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.
需要注意下面两个说明 。
读写缓存的操作如下:
1
2
3
4
5
6
7
8
9
10
11
|
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方法中的代码如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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 这三个方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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版本).
1
|
string
transcoder =
"CachingSerializer.MessagePackTranscoder,CachingSerializer"
;
|
注:如果使用方式一来处理,记得将transcoder的拼写不要错,并且要带上命名空间,不然创建的Transcoder会一直是null,从而走的就是Bson了! 本质是 Activator.CreateInstance,应该不用多解释.
方式二:通过依赖注入的方式来处理(适用>=2.1.0.5版本) 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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种序列化方法可供选择:
如果这4种都不满足需求,也可以自己写一个,只要实现IEasyCachingSerializer这个接口相应的方法即可.
Redis 。
在介绍怎么用序列化之前,先来简单看看是怎么用的(用ASP.NET Core Web API做演示).
添加Redis相关的nuget包 。
1
|
Install-Package EasyCaching.Redis
|
修改Startup 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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 =
""
;
});
}
}
|
然后在控制器中使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
[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}"
;
}
}
|
下面我们要如何去替换我们想要的新的序列化方法呢?
以MessagePack为例,先通过nuget安装package 。
1
|
Install-Package EasyCaching.Serialization.MessagePack
|
然后只需要在ConfigureServices方法中加上下面这句就可以了.
1
2
3
4
5
|
public
void
ConfigureServices(IServiceCollection services)
{
//others..
services.AddDefaultMessagePackSerializer();
}
|
Memcached 。
同样先来简单看看是怎么用的(用ASP.NET Core Web API做演示).
添加Memcached的nuget包 。
1
|
Install-Package EasyCaching.Memcached
|
修改Startup 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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 。
1
|
Install-Package EasyCaching.Serialization.MessagePack
|
剩下的操作和Redis是一样的! 。
1
2
3
4
5
6
7
8
9
10
|
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即可.
总结 。
1、 先来看看文中提到的4种序列化的库 。
System.Runtime.Serialization.Formatters.Binary在使用上需要加上[Serializable],效率是最慢的,优势就是类库里面就有,不需要额外引用其他package.
Newtonsoft.Json使用起来比较友善,可能是用的多的缘故,也不需要我们对已经定义好的类加一些Attribute上去.
protobuf-net使用起来可能就略微麻烦一点,可以在定义类的时候加上相应的Attribute,也可以在运行时去处理(要注意处理子类),不过它的口碑还是不错的.
MessagePack-CSharp虽然可以不添加Attribute,但是不加比加的时候也会有所损耗.
至于如何选择,可能就要视情况而定了! 。
有兴趣的可以用BenchmarkDotNet跑跑分,我也简单写了一个可供参考:SerializerBenchmark 。
2、在对缓存操作的时候,可能会更倾向于“隐式”操作,能直接将一个object扔进去,也可以直接将一个object拿出来,至少能方便使用方.
3、序列化操作时,Redis要比Memcached简单一些.
最后,如果您在使用EasyCaching,有问题或建议可以联系我! 。
前半部分的示例代码:CachingSerializer 。
后半部分的示例代码:sample 。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我的支持.
最后此篇关于.NET Core中使用Redis与Memcached的序列化问题详析的文章就讲到这里了,如果你想了解更多关于.NET Core中使用Redis与Memcached的序列化问题详析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!