.NET下实现分布式缓存系统Memcached(转)
在Web应用程序中,数据通常保存在RDBMS中,应用服务器从数据库中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负载加重、数据库响应变慢、网站显示延迟等重大影响。为了缓解数据库的压力,提升Web应用程序的响应速度,人们提出了Web缓存的概念,这里缓存的概念不同于计算机硬盘控制器上的一块内存芯片。Web缓存位于Web服务器(1 个或多个,内容源服务器)和客户端之间(1个或多个),缓存会根据进来的请求保存输出内容的副本,例如html页面, 图片,文件(统称为副本),然后,当下一个请求来到的时候,如果是相同的URL,缓存直接使用副本响应访问请求,而不是向内容源服务器再次发送请求。使用 缓存可以减少相应延迟,因为请求在缓存服务器(离客户端更近)而不是源服务器被相应,这个过程耗时更少,让web服务器看上去相应更快;当副本被重用时还 可以减少网络带宽消耗。
缓存的工作方式
缓存的工作方式如下图所示:
![](http://image4.it168.com/2009/9/7/fa9b1916-8b11-4353-8666-45ccefd49772.jpg)
上图中的缓存服务器维护一个集中缓存并在其中存放经常被请求的对象,任何 Web 浏览器客户端都可以访问该缓存。来自内存/磁盘缓存的对象所需的处理资源比来自其他网络的对象所需的处理资源要少得多。因此,这可以提高客户端浏览器性 能、缩短用户响应时间并减少Internet 连接所消耗的带宽。
上图1到6标示了当用户请求对象时,缓存服务器是如何响应的,主要经过下列步骤:
第一个用户(客户端 1)请求 Web 对象。
缓存服务器检查该对象是否存在于缓存中。由于该对象不存在于缓存服务器缓存中,因此,缓存服务器从Internet中的服务器请求该对象。
Internet上的服务器将该对象返回给缓存服务器计算机。
缓存服务器将该对象的一个副本保留在其缓存中,并将对象返回给客户端 1。
客户端 2请求相同的对象。
缓存服务器从其缓存中返回该对象,而不是从Internet中获取该对象。
一般在组织的Web服务器前面部署缓存服务器。Web服务器是主持商业 Web 业务或可由业务合作伙伴访问的服务器。使用传入的Web请求,缓存服务器可以充当外部环境的 Web 服务器,并通过其缓存来完成客户端对Web内容的请求。只有在缓存无法处理请求时,缓存服务器才将请求转发到Web服务器。
目前实现缓存的机制有几种,Memcached就是其中的一种。本文介绍了Memcached的概念,基本原理以及工作机制,并介绍了如何在ASP.NET中使用Memcache进行开发Web应用程序以提升Web应用程序的性能。
Memcached介绍
Memcached是一个高性能的分布式的内存对象缓存系统。Memcached是为了加快http://www.livejournal.com/访 问速度而诞生的一个项目,由Danga Interactive开发的,它通过在内存里维护一个统一的巨大的hash表来存储各 种格式的数据,包括图像、视 频、文件以及数据库检索的结果等。虽然最初为了加速 LiveJournal 访问速度而开发的,但是后来被很多大型的网站采用。它的缓存是一种分布式的,也就是可以允许不同主机上的多个用户同时访问这个缓存系统,这种方法不仅解决 了共享内存只能是单机的弊端,同时也解决了数据库检索的压力,最大的优点是提高了访问获取数据的速度,Memcached用于在动态应用中减少数据库负 载,提升访问速度,但是用来加速Web应用、降低数据库负载时比较多。Memcached也完全可以用到其他地方,比如分布式数据库,分布式计算等领域。
Memcache的特点
Memcached作为高速运行的分布式缓存服务器,具有以下的特点。
协议简单。Memcached的服务器客户端通信并不使用复杂的XML等格式, 而使用简单的基于文本行的协议。因此,通过telnet也能在memcached上保存数据、取得数据。
基于libevent的事件处理,libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
内置内存存储方式。为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。 由于数据仅存在于内存中,因此重启memcached、重启操作系统会 导致全部数据消失。 另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。 memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。 在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。 但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比memcached进程本身还慢。
memcached不互相通信的分布式。memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通 信以共享信息。那么,怎样进行分布式呢?这完全取决于客户端的实现。Cache::Memcached的分布式方法简单来说,就是“根据服务器台数的余数 进行分散”。 求得键的整数哈希值,再除以服务器台数,根据其余数来选择服务器。
Memcached的缓存是一种分布式的,可以让不同主机上的多个用户同时访问, 因此解决了共享内存只能单机应用的局限,更不会出现使用数据库做类似事情的时候,磁盘开销和阻塞的发生。
Memcached使用了libevent(如果可以的话,在linux下使用epoll)来均衡任何数量的打开链接,使用非阻塞的网络I/O, 对内部对象实现引用计数(因此,针对多样的客户端,对象可以处在多样的状态), 使用自己的页块分配器和哈希表, 因此虚拟内存不会产生碎片并且虚拟内存分配的时间复杂度可以保证为O(1)。
许多语言都实现了连接memcached的客户端,其中以Perl、PHP为主。 仅仅memcached网站上列出的语言就有Perl、PHP、Python、Ruby、C#、C/C++等等。
Memcached的工作机制
Memcached通过在内存中开辟一块区域来维持一个大的hash表来加快页面访问速度,虽然和数据库是独立的,但是目前主要用来缓存数据库的数据。 允许多个server通过网络形成一个大的hash,用户不必关心数据存放在哪,只调用相关接口就可。存放在内存的数据通过LRU算法进行淘汰出内存。同 时可以通过删除和设置失效时间来淘汰存放在内存的数据。
Memcached在.NET中的应用
一.Memcached服务器端的安装(此处将其作为系统服务安装)
下载文件:memcached 1.2.1 for Win32 binaries (Dec 23, 2006)
下载地址:http://jehiah.cz/projects/memcached-win32/files/memcached-1.2.1-win32.zip
1.解压缩文件到c:\memcached
2.命令行输入 c:\memcached\memcached.exe -d install'
3.命令行输入 c:\memcached\memcached.exe -d start ,该命令启动 Memcached ,默认监听端口为 11211
通过 memcached.exe -h 可以查看其帮助,查看memcache状态,telnet 192.168.0.98 11211。输入stats查询状态
stats
STAT pid 8601
STAT uptime 696
STAT time 1245832689
STAT version 1.2.0
STAT pointer_size 64
STAT rusage_user 0.007998
STAT rusage_system 0.030995
STAT curr_items 1
STAT total_items 1
STAT bytes 76
STAT curr_connections 2
STAT total_connections 4
STAT connection_structures 3
STAT cmd_get 1
STAT cmd_set 1
STAT get_hits 1//命中次数
STAT get_misses 0 //失效次数
STAT bytes_read 97
STAT bytes_written 620
STAT limit_maxbytes 134217728
END
-d选项是启动一个守护进程
-m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB
-u是运行Memcache的用户
-l是监听的服务器IP地址,如果有多个地址的话,我这里假定指定了服务器的IP地址为本机ip地址
-p是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口
-c选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定
-P是设置保存Memcache的pid文件
二..NET memcached client library(memcached的.NET客户端类库)
下载memcached的.NET客户端类库,下载地址:https://sourceforge.net/projects/memcacheddotnet/里面有.net1.1 和 .net2.0的两种版本,里面还有.NET应用memcached的例子。
三.应用
1.新建ASP.NET站点,将Commons.dll,ICSharpCode.SharpZipLib.dll,log4net.dll,Memcached.ClientLibrary.dll添加到web引用。
2.为了进行后续的测试,我们创建两个aspx页面,memcache.aspx和nomemcache.aspx,memcache.aspx是使用 MemcacheClient类加入了缓存机制的页面。nomemcache.aspx是没有加入缓存机制的页面,直接连接的数据库。一会我们通过观察数 据库事件和进行压力测试来测试在压力测试的情况下应用程序的性能。
3.memcache.aspx.cs中添加对Memcached.ClientLibrary.dll的引用,即:using Memcached.ClientLibrary;Page_Load()中加入如下代码。
protected void Page_Load(object sender, EventArgs e)
{
string[] serverlist = new string[] { "127.0.0.1:11211" };
string poolName = "MemcacheIOPool";
SockIOPool pool = SockIOPool.GetInstance(poolName);
//设置连接池的初始容量,最小容量,最大容量,Socket 读取超时时间,Socket连接超时时间
pool.SetServers(serverlist);
pool.InitConnections = 1;
pool.MinConnections = 1;
pool.MaxConnections = 500;
pool.SocketConnectTimeout = 1000;
pool.SocketTimeout = 3000;
pool.MaintenanceSleep = 30;
pool.Failover = true;
pool.Nagle = false;
pool.Initialize();//容器初始化
//实例化一个客户端
MemcachedClient mc = new MemcachedClient();
mc.PoolName = poolName;
mc.EnableCompression = false;
string key = "user_info";//key值
object obj = new object();
if (mc.KeyExists(key)) //测试缓存中是否存在key的值
{
obj = mc.Get(key);
User user2 = (User)obj;
Response.Write("
" + user2.Name + "," + user2.Pwd + "
");
}
else {
string conStr = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(conStr);
conn.Open();
string sql = "Select * From T_User Where id=1";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader dr = cmd.ExecuteReader();
User user = new User();
while (dr.Read())
{
user.Name = dr["name"].ToString();
user.Pwd = dr["pwd"].ToString();
}
dr.Close();
conn.Close();
mc.Set(key, user, System.DateTime.Now.AddMinutes(2)); //存储数据到缓存服务器,这里将user这个对象缓存,key 是"user_info1"
Response.Write("
姓名:" + user.Name + ",密码:" + user.Pwd + "
");
}
}
4.nomemcache.aspx是没有加缓存机制的直接连接数据库的页面。nomemcache.aspx.cs中的代码:
protected void Page_Load(object sender, EventArgs e)
{
string conStr = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(conStr);
conn.Open();
string sql = "Select * From T_User Where id=2";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader dr = cmd.ExecuteReader();
User user = new User();
while (dr.Read())
{
user.Name = dr["name"].ToString();
user.Pwd = dr["pwd"].ToString();
}
dr.Close();
conn.Close();
Response.Write("
姓名:" + user.Name + ",密码:" + user.Pwd + "
");
}
}
5.测试
测试memcache.aspx页面,该测试的主要目的是访问memcache.aspx页面,看是否看该页面走的是memcached而非访问的数据 库(第一次是访问数据库)。首先,我们运行memcache.aspx页面,通过Sql Server Profiler来查看运行memcache.aspx页面对数据库的操作情况。第一次运行memcache.aspx的时候,Sql Server Profiler里面显示memcache.aspx对数据库的操作,即执行了Select * From T_User Where id=1。
![](http://image4.it168.com/2009/9/7/864c9df1-7d23-43da-afa5-e4f3b576085c.jpg)
memcache.aspx页面上第一次运行的时候显示如下信息:
通过读取数据库得到的数据:
姓名:lucy,密码:lucy。
![](http://image4.it168.com/2009/9/7/33a2bd6b-337a-40b5-b74e-71cc76d4c7c1.jpg)
接着我们刷新memcache.aspx页面,页面上还显示上述信息:
通过读取缓存得到的数据:
姓名:lucy,密码:lucy
但从Sql Server Profiler观察,memcache.aspx页面没有对数据库进行任何操作。说明我们第二次访问该页面是读取的缓存,接着不停的刷新页面,都显示的是通过读取缓存得到的数据。直到到达缓存失效时间。
测试nomemcache.aspx,我们运行nomemcache.aspx页面,每刷新一次(包括第一次),Sql Server Profiler都记录了,nomemcache.aspx页面对数据库进行的操作,即之行了Select * From T_User Where id=2语句。说明每访问一次该页面,都要进行一次数据库的访问操作。
![](http://image4.it168.com/2009/9/7/543584a2-5110-46c8-8565-c1932ad1158a.jpg)
这里我们使用Microsoft Web Application Stress Tool对web进行压力测试,Microsoft Web Application Stress Tool 是由微软的网站测试人员所开发,专门用来进行实际网站压力测试的一套工具。透过这套功能强大的压力测试工具,您可以使用少量的客户端计算机仿真大量用户上 线对网站服务所可能造成的影响,在网站实际上线之前先对您所设计的网站进行如同真实环境下的测试,以找出系统潜在的问题,对系统进行进一步的调整、设置工 作。就是因为这些特性,才使它具备了DOS轰炸的功能。
1、工具简单设置
打开Web Application Stress Tool,很简洁的一个页面,上面是工具栏,左下方是功能选项,右下方是详细设置选项。在对目标Web服务器进行压力测试之前,先对它进行一些必要的设置。
(1).在“settings”的功能设置中(如下图),一个是Stress level (threads)这里是指定程序在后台用多少线程进行请求,也就是相当于模拟多少个客户机的连接,更加形象的就是说设置多少轰炸的线程数。一般填写 500~1000,因为这个线程数是根据本机的承受力来设置的,如果你对自己的机器配置有足够信心的话,那么设置的越高,轰炸的效果越好。
![](http://image4.it168.com/2009/9/7/456c5ad8-6eba-4c23-8bb8-6c97cf0af989.jpg)
(2).在“Test Run Time”中来指定一次压力测试需要持续的时间,分为天、小时、分、秒几个单位级别,你根据实际情况来设置吧!这里面设置测试时间为1分钟。
(3).其余的选项大家可以根据自己的情况设置。
2、压力测试
步骤1:在工具中点右键,选择Add命令,增加了一个新的测试项目:memcache,对它进行设置,在主选项中的server中填写要测试的服务器的 IP地址,这里我们是在本机上进行测试,所以填写localhost。在下方选择测试的Web连接方式,这里的方式Verb选择 get,path选择要测试的Web页面路径,这里填写/WebMemCache/memcache.aspx,即加入缓存的memcache.aspx 页面(如下图)。
![](http://image4.it168.com/2009/9/7/1d3a326e-8cd1-4d61-834a-214f446e0ad8.jpg)
步骤2:在“Settings”的功能设置中将Stress level (threads)线程数设置为500。完毕后,点工具中的灰色三角按钮即可进行测试(如下图)。
![](http://image4.it168.com/2009/9/7/0e3995f4-0b31-4b5b-8b45-25213f044a04.jpg)
同理,我们在建一个nomemcach的项目用来测试nomemcache.aspx页面。Memcach和nomemcach测试完毕后,点击工具栏上的Reports按钮查看测试报告:
![](http://image4.it168.com/2009/9/7/e531d14e-dc8a-469c-b43b-1f052dca35c3.jpg)
3.性能分析
Memcache.aspx的测试报告:
Overview
================================================================================
Report name: 2009-7-20 10:52:00
Run on: 2009-7-20 10:52:00
Run length: 00:01:12
Web Application Stress Tool Version:1.1.293.1
Number of test clients: 1
Number of hits: 2696
Requests per Second: 44.93
Socket Statistics
--------------------------------------------------------------------------------
Socket Connects: 3169
Total Bytes Sent (in KB): 646.80
Bytes Sent Rate (in KB/s): 10.78
Total Bytes Recv (in KB): 2019.37
Bytes Recv Rate (in KB/s): 33.65
Socket Errors
--------------------------------------------------------------------------------
Connect: 0
Send: 0
Recv: 0
Timeouts: 0
RDS Results
--------------------------------------------------------------------------------
Successful Queries: 0
Page Summary
Page Hits TTFB Avg TTLB Avg Auth Query
================================================================================
GET /WebMemCache/memcache.aspx 2696 1.94 1.95 No No
Nomemcache.aspx的测试报告:
Overview
================================================================================
Report name: 2009-7-20 10:54:01
Run on: 2009-7-20 10:54:01
Run length: 00:01:12
Web Application Stress Tool Version:1.1.293.1
Number of test clients: 1
Number of hits: 2577
Requests per Second: 42.95
Socket Statistics
--------------------------------------------------------------------------------
Socket Connects: 2860
Total Bytes Sent (in KB): 589.32
Bytes Sent Rate (in KB/s): 9.82
Total Bytes Recv (in KB): 1932.75
Bytes Recv Rate (in KB/s): 32.21
Socket Errors
--------------------------------------------------------------------------------
Connect: 0
Send: 0
Recv: 0
Timeouts: 0
RDS Results
--------------------------------------------------------------------------------
Successful Queries: 0
Page Summary
Page Hits TTFB Avg TTLB Avg Auth Query
================================================================================
GET /WebMemCache/nomemcache.aspx 2577 4.75 4.79 No No
从测试报告上看出memcache.aspx页面在一分钟内的Hits(命中次数)2696,平均TTFB是(Total Time to First Byte)1.94,平均TTLB(Total Time to Last Byte)是1.95。这些参数都低于nomemcache.aspx。另外memcache.aspx的Requests per Second(每秒请求的次数)是 44.93高于nomemcache.aspx页面的42.95.这些参数都说明memcache.aspx页面的执行性能要高于 nomemcache.aspx页面。缓存起到了提高性能的作用。当然我这里面进行的测试只是模拟500个用户在1分钟内的访问对Web服务器性能的影 响。
总结
本文简单介绍了Memcached的基本原理,特点以及工作方式,接下来介绍了 Windows下Memcached服务器端程序的安装方法、在.NET应用程序中使用.NET memcached client library。接下来通过运行分析程序来了解memcached的工作原理机制,最后通过压力测试工具对没有加入Memcached机制的页面和加入 Memcached页面进行了压力测试,对比加入Memcached机制前后Web应用程序的性能。了解Memcached内部构造, 就能知道如何在应用程序中使用memcached才能使Web应用的速度更上一层楼。提升web应用程序的性能和访问速度。