注:支持 .NET Core 的 memcached 客户端 EnyimMemcachedCore 的 NuGet 包下载地址:https://www.nuget.org/packages/EnyimMemcachedCore

经过一周的努力,我们的“.NET跨平台之旅”取得了一个重要的进展——基于.NET Core改写了开源的memcached .NET客户端EnyimMemcached,实现了Linux上访问memcached缓存,解决了跨平台.NET的缓存问题。

针对我们的应用场景,将实际应用迁移到部署在Linux服务器上的跨平台.NET(.NET Core)有两大障碍:一个障碍是Linux上访问SQL Server数据库,一个障碍是Linux上访问memcached缓存。第一个问题在苦等之后,终于被微软解决了,详见 .NET跨平台之旅:升级至ASP.NET 5 RC1,Linux上访问SQL Server数据库;而第二个问题,微软还没开始解决,目前的ASP.NET 5缓存组件只支持进程内的内存缓存与redis,不支持memcached。但我们不想苦等了,选择了自己动手、丰衣足食,尝试自己解决这个问题。

我们用的memcached缓存客户端是EnyimMemcached,之前对它进行过异步化改造,对源代码有些了解。用dnx基于.NET Core编译EnyimMemcached的源代码,出现了300多个编译错误,当时有点望而却步,但后来还是下定决心解决这些编译错误。

一类编译错误是对System.Configuration程序集的依赖,EnyimMemcached的配置是放在web.config中的,有不少代码依赖System.Configuration。而ASP.NET 5中根本没有web.config这个东东,corefx中自然也就没有System.Configuration的实现。为了解决这个问题,我们暂时放弃使用配置文件,通过硬编码进行配置,添加的主要代码如下:

IMemcachedClientConfiguration configuration = new MemcachedClientConfiguration(_loggger);
configuration.SocketPool.MinPoolSize = ;
configuration.SocketPool.MaxPoolSize = ;
configuration.SocketPool.ConnectionTimeout = new TimeSpan(, , );
configuration.SocketPool.ReceiveTimeout = new TimeSpan(, , );
configuration.SocketPool.DeadTimeout = new TimeSpan(, , );

一类编译错误是corefx(.NET Core Framework)中程序集的变化,比如:

  • IPEndPoint跑到了System.Net.Primitives程序集中
  • System.Security.Cryptography.HashAlgorithm跑到了System.Security.Cryptography.Algorithms程序集中
  • System.Threading.Timer成为了一个独立的程序集

一类编译错误是corefx中类库的变化,比如没有了System.Net.Dns.GetHostEntry(),需要改用System.Net.NameResolution程序集中的System.Net.Dns.GetHostAddressesAsync()。

还有一类最头疼的编译错误是corefx中没有二进制序列化(BinaryFormatter)的实现,而对于EnyimMemcached来说这是关键部分,对象的缓存读写全靠二进制序列化与反序列化。针对这个问题,我们改用Json.NET进行bson序列化与反序列化。

序列化实现代码如下:

protected virtual ArraySegment<byte> SerializeObject(object value)
{
using (var ms = new MemoryStream())
{
using (BsonWriter writer = new BsonWriter(ms))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(writer, value);
return new ArraySegment<byte>(ms.ToArray(), , (int)ms.Length);
}
}
}

反序列化实现代码如下:

T ITranscoder.Deserialize<T>(CacheItem item)
{
if (item.Data == null || item.Data.Count == ) return default(T); using (var ms = new MemoryStream(item.Data.ToArray()))
{
using (BsonReader reader = new BsonReader(ms))
{
if(typeof(T).GetTypeInfo().ImplementedInterfaces.Contains(typeof(IEnumerable)))
{
reader.ReadRootValueAsArray = true;
}
JsonSerializer serializer = new JsonSerializer();
return serializer.Deserialize<T>(reader);
}
}
}

Json.NET的bson反序列有个麻烦的地方,对于集合类型需要专门设置ReadRootValueAsArray的值为true。当时在这个地方折腾了不少时间,没找到好的解决方法,只能用反射实现,就是上面代码中的 typeof(T).GetTypeInfo().ImplementedInterfaces.Contains(typeof(IEnumerable)) 。

在解决了这些编译错误并在开发环境中测试通过之后,我们就将改造后的EnyimMemcached应用到“.NET的跨平台之旅”的示例站点(http://about.cnblogs.com/)上,调用代码如下:

public class TabNavService : ITabNavService
{
private ITabNavRepository _tabNavRepository;
private IMemcachedClient _memcachedClient; public TabNavService(
ITabNavRepository tabNavRepository,
IMemcachedClient memcachedClient)
{
_tabNavRepository = tabNavRepository;
_memcachedClient = memcachedClient;
} public async Task<IEnumerable<TabNav>> GetAll()
{
var result = await _memcachedClient.GetAsync<IEnumerable<TabNav>>(cacheKey);
if(!result.Success)
{
var tabNavs = await _tabNavRepository.GetAll();
await _memcachedClient.StoreAsync(StoreMode.Add, cacheKey, tabNavs, new TimeSpan(, , ));
return tabNavs;
}
return result.Value;
}
}

_memcachedClient是通过“依赖注入”注入的,但是在注入时,我们自己给自己挖了一个坑:

services.AddTransient<IMemcachedClient, MemcachedClient>();

加了memcached缓存功能之后,示例站点在一台测试服务器(只有当前一个请求,没有其它请求)上运行正常,缓存读写正常。

但是一发布到about.cnblogs.com的正式服务器上(有多个请求),请求发出后就一直处于等待状态,服务器无任何响应。在原以为大功告成的时刻却卡在了这个奇怪的问题上,这个滋味你懂的。

折腾了半天,实在找不到原因,找了个替罪羊——可能是corefox中System.Net.Sockets在Linux上的实现对并发请求的处理有问题,准备暂时放弃。

就在这一刻,突然想到,MemcachedClient的构造函数中有初始化socket pool的操作,每创建一个MemcachedClient的实例时都要创建好多到memcached服务器的socket连接。难道是在注入时忘了使用单例,造成每个请求都要创建MemcachedClient的实例,太多的socket连接让kestrel服务器不堪重负。想到这一点,立马跑到电脑前打开代码一看,立马发现自己的坑坑自己不浅。改为单例后,问题立马解决。

services.AddSingleton<IMemcachedClient, MemcachedClient>();

于是,运行在Linux服务器上的示例站点(http://about.cnblogs.com/)用上了memached缓存;于是,写了这篇博文分享自己坑自己的过程;于是,我们的.NET跨平台之旅迈上了一个新台阶。

.NET跨平台之旅:基于.NET Core改写EnyimMemcached,实现Linux上访问memcached缓存的更多相关文章

  1. .NET跨平台之旅:升级至ASP.NET 5 RC1,Linux上访问SQL Server数据库

    今天微软正式发布了ASP.NET 5 RC1(详见Announcing ASP.NET 5 Release Candidate 1),.NET跨平台迈出了关键一步. 紧跟这次RC1的发布,我们成功地将 ...

  2. 关于.net Core项目发布在Linux上的填坑

    本文主要记录.net Core项目发布在Linux服务器上面所遇到的问题,防止遗忘是 1.在发布文件中执行 dotnet xxxxxx.dll的时候提示如下错误: An assembly specif ...

  3. 基于.NET CORE微服务框架 -谈谈Cache中间件和缓存降级

    1.前言 surging受到不少.net同学的青睐,也提了不少问题,提的最多的是什么时候集成API 网关,在这里回答大家最近已经开始着手研发,应该在1,2个月内会有个初版API网关,其它像Token身 ...

  4. asp.net core项目 部署在 linux上

    第一步 安装 .net core https://www.microsoft.com/net/learn/get-started/linuxubuntu 第二步 运行你的asp.net core 项目 ...

  5. .Net Core 项目发布在IIS上 访问404 问题对应

    对策: 1.进入线程池画面,将当前程序的线程池设为"无托管代码"   2.修改配置文件 Web.config,加上配置   原因: 因为.NetCore 5.0 自带集成了Swag ...

  6. .NET跨平台之旅:ASP.NET Core从传统ASP.NET的Cookie中读取用户登录信息

    在解决了asp.net core中访问memcached缓存的问题后,我们开始大踏步地向.net core进军——将更多站点向asp.net core迁移,在迁移涉及获取用户登录信息的站点时,我们遇到 ...

  7. Linux上使用VIM进行.Net Core

    如何在Linux上使用VIM进行.Net Core开发 对于在Linux上开发.Net Core的程序员来说, 似乎都缺少一个好的IDE.Windows上有Visual Studio, Mac上有Vi ...

  8. Linux+.Net Core+Nginx(在Linux上使用Nginx反向代理.Net Core 项目)

    Linux+.Net Core+Nginx 之前的文章中有提到关于使用Nginx在linux来实现反向代理,今天我们继续加点料.在Centos7中部署.NetCore,然后使用Nginx进行反向代理! ...

  9. Linux上Core Dump文件的形成和分析

    原文: http://baidutech.blog.51cto.com/4114344/904419 Core,又称之为Core Dump文件,是Unix/Linux操作系统的一种机制,对于线上服务而 ...

随机推荐

  1. ASP.NET Core 中文文档 第二章 指南(4.5)使用 SQL Server LocalDB

    原文:Working with SQL Server LocalDB 作者:Rick Anderson 翻译: 魏美娟(初见) 校对: 孟帅洋(书缘).张硕(Apple).许登洋(Seay) Appl ...

  2. H5实现本地预览图片

    我们使用H5可以很容易的实现图片上传前对其进行预览的功能 Html代码如下: <!DOCTYPE html> <html lang="en"> <he ...

  3. docker4dotnet #5 使用VSTS/TFS搭建基于容器的持续交付管道

    在过去的几篇d4d系列中,我给大家介绍了如何使用docker来支持asp.net core的应用开发,打包的场景.Asp.net core的跨平台开发能力为.net开发人员提供了使用容器进行应用开发的 ...

  4. Uploadify 结合 Web API 2 上传问题

    最近使用jQuery.Uploadify和Web API配合来做上传,碰到问题,还木有办法解决,记录一下: 环境:jQuery 1.10.2,Uploadify 3.2.1,SWFObject 2.2 ...

  5. 制作CAB包

    制作CAB包 inf文件 INF是Device INFormation File的英文缩写,是Microsoft公司为硬件设备制造商发布其驱动程序推出的一种文件格式,INF文件中包含硬件设备的信息或脚 ...

  6. C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现

    ”摇一摇周边“是微信提供的一种新的基于位置的连接方式.用户通过“摇一摇”的“周边”页卡,可以与线下商户进行互动,获得商户提供的个性化的服务.微信4月份有一个赠送摇一摇设备的活动,我们有幸获得赠送资格, ...

  7. arcengine中自定义工具和自带工具条(ICommand)点击后和其他工具使用的冲突

    自己系统中本身对于放大缩小等功能直接是单独重写的,但是如果在加一个工具条具有相同功能的话两者之间会有一些冲突,为解决该冲突可以重写工具条的OnItemClick事件 该工具条命名为axTool 我本身 ...

  8. 让linux开机默认开启小键盘

     linux默认开机不开启数字键盘numberlock,每次输入开机密码还得劳烦自己去点亮指示灯,让此灯开机自动点亮,需要一个软件才行,就是numlockx了,可以通过yum安装:yuminstall ...

  9. springmvc<一>一种资源返回多种形式【ContentNegotiatingViewResolver】

    restful服务中一个重要的特性就是一种资源可以有多种表现形式,在springmvc中可以使用ContentNegotiatingViewResolver这个视图解析器来实现这种方式. 描述资源的三 ...

  10. Angular的自定义指令以及实例

    本文章已收录于:  AngularJS知识库  分类: javascript(55)  http://www.cnblogs.com/xiaoxie53/p/5058198.html   前面的文章介 ...