一、高可用的Session服务器场景简介

1.1 应用服务器的无状态特性

  应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性

PS:提到无状态特性,不得不说下Http协议。我们常常听到说,Http是一个无状态协议,同一个会话的连续两个请求互相不了解,他们由最新实例化的环境进行解析,除了应用本身可能已经存储在全局对象中的所有信息外,该环境不保存与会话有关的任何信息。之所以我们在使用ASP.NET WebForm开发中会感觉不到Http的无状态特性,完全是因为Microsoft帮我们实现了ViewState,它是ASP.NET WebForm中保存页面信息的基本单位,本质是一个HTML中的隐藏域,回调时会将这个隐藏域中的数据提交到服务器端。

  在很多场景中,用户都需要和我们的网站系统进行多次的信息交互,这时就需要一种解决方案来克服无状态特性所带来的困境。还好,在巨人的肩膀上,我们已经有了很好的解决方案,那就是浏览器端的Cookie服务器端的Session。在一般的单机开发中(这里一般是指只有一台Web服务器的情况),服务器端我们通常使用Session来存储用户登录状态(一般是一个自定义对象实例),在多数的管理信息系统开发中(毕竟内部系统用户量不多,一台Web服务器既提供Web服务又存储Session对象内存还算是够用的)这是很常见的。

  但是,在大用户量下,单机版的Session就会显得效率低下,甚至会拖累Web服务器的性能。这是因为:每个用户的Http请求发到服务器端后,每台Web服务器的服务器软件(例如:IIS、Tomcat等)都会为该请求创建一个线程来进行处理和响应,但是一台服务器同一时间可以接收的请求数毕竟是有限的(这个根据服务器的配置而定,例如CPU中i3、i5和i7类型分别可以创建的线程数都各不相同),当某个时间段出现高并发请求数的时候(比如:网购秒杀系统中经常同一时间会出现海量的并发数),那这台应用服务器将会接收前所未有的请求负载,最终可能会因为承受不了高负载而导致宕机,网站不得不停止服务。

  这时,又想起了那句话:当一头牛拉不动车的时候,不要去寻找一头更强壮的牛,而是用两头牛来拉车。于是,我们可以采用服务器集群的技术来对Web服务器进行改进,增加N台Web服务器部署相同的Web应用构成Web服务器集群来对外提供服务,通过负载均衡设备或软件将海量的并发请求数平均地分摊到每台Web服务器,例如:假设某系统在促销活动期间同一时刻涌入了10万个请求,而服务器集群中有5台Web服务器同时提供服务,这时负载均衡设备就将这个10万请求通过某种算法较为均衡地分配给其中的Web服务器,平均下来每台服务器最多就只承担2万个请求。

  通过服务器集群,已经较好地解决了请求负载问题,这时新的问题又来了:由于Session默认是属于进程内(InProc)的,也就是说它是存储在Web服务器的内存里边的。当构建好集群之后,用户的Session会建立在负载均衡设备所分配的其中一台Web服务器里边。但是当用户下一次访问或者访问系统中的其他子系统(比如:我首先在百度百科进行登录了,然后访问百度贴吧),由于Session会话还存储在上一次提供服务的Web服务器里边,系统校验规则(现在这台Web服务器里边检测到没有该用户的Session)会造成用户的重复登录(比如:都是在百度的网页,它却让你登录好几次,你爽吗?很明显,不爽吧)。这时,就需要我们解决Web服务器集群的Session管理,下面我们就来看看如何进行Web服务器集群的Session管理。

1.2 应用服务器集群的Session管理

  我们现在来看看在集群环境中,Session管理的几种常见手段:

  ①Session复制:该方案简单易行,集群中的几台服务器之间同步Session对象,任何一台服务器宕机都不会导致Session对象的丢失,服务器也只需要从本机获取即可。但是,该方案只适合集群规模较小的情况下。当规模较大时,大量的Session复制操作会占用服务器和网络的大量资源,系统不堪重负

  ②Session绑定:利用负载均衡的源地址Hash算法,总是将源于同一IP地址的请求分发到同一台服务器上。这样的话,在整个会话期间,用户所有的请求都在同一台服务器上进行处理,即Session绑定在某台特定服务器上,保证Session总能在这台服务器上获取。(这种方案又叫做会话粘滞)。

  但是,这种方案不符合高可用的需求。因为一旦某台服务器宕机,那么该机器上得Session也就不复存在了,用户请求切换到其他机器后因为没有Session而无法完成业务处理。因此,很少有网站采用此方案进行Session管理。

  ③Cookie记录Session:利用浏览器支持的Cookie记录Session简单易行,可用性高,并且支持服务器的线性伸缩,因此,许多网站都或多或少地使用了Cookie来记录Session。但是Cookie记录Session有缺点:比如受Cookie大小限制、每次请求响应都要传输Cookie影响性能、用户关闭了Cookie会造成访问不正常等。

  ④Session服务器:利用独立部署的Session服务器(集群)统一管理Session,应用服务器每次读写Session时,都访问Session服务器。这种方案实际上是将应用服务器的状态分离,分为无状态的应用服务器有状态的Session服务器

    

  从上面的几种方式来看,各有利弊,但Session服务器是最符合高可用需求的方案,也是企业中经常用到的方案。那么,对于有状态的Session服务器,一种较简单的方法是利用分布式缓存(如Memcached、Redis等,有关Redis的简单介绍可以阅读我的博文:NoSQL初探之人人都爱Redis)、数据库等,在这些产品的基础上进行封装,使其符合Session的存储和访问要求。综合上述介绍,我们今天就采用Memcached来构建我们的Session服务器,解决Web服务器集群的Session的共享访问。

PS:为什么要采用分布式缓存方案而不采用数据库来存储Session?这个就得要分析一下数据访问的性能瓶颈了,一般来说,磁盘IO读写的速度是最慢的,因为数据库数据其实是存储在文件中的,虽然目前大多数的数据库都采用了B+树结构,读取一条数据最多都还是需要4次的数据读写(三次磁盘访问获得数据索引及行ID,一次数据文件读操作,终于知道数据库操作多麻烦了)。而分布式缓存例如Memcached是以Key/Value这种简单的形式存储在服务器的内存里边的,内存的随机读写速度是完爆磁盘IO的,因此内网+内存的双内模式是比较完美的方案。

磁盘又分为两种类型:

  ①机械硬盘:通过马达驱动磁头臂,带动磁头到指定的磁盘位置访问数据。它能够实现快速顺序读写,慢速随机读写

  ②固态硬盘(又称SSD):无机械装置,数据存储在可持久记忆的硅晶体上,因此可以像内存一样快速随机访问

  在目前的网站应用中,大部分应用访问数据都是随机的,这种情况下SSD具有更好的性能表现,但是性价比有待提升(蛮贵的,么么嗒)。

二、Memcached实现Session的分布式存储

2.0 案例总体预览

  (1)模拟的登录案例场景

    假设我们有一个基于ASP.NET的信息系统,这个系统使用一个统一的系统登录页面进行用户登录,登陆后默认跳转到一个用户中心主页,并显示:欢迎您,{用户账号名称}。

    ①系统登录页面效果:

    ②用户主页效果:

  (2)模拟的技术体系选择

    ASP.Net MVC+EF Code First+MySQL+Memcached

2.1 初始准备工作

  (1)新建一个ASP.NET MVC4的空项目,视图引擎选择为“Razor”即可;

  (2)在项目中新建一个文件夹,取名为“Lib”,主要存放一些必要的DLL文件;

  (3)在项目中添加对这几个DLL的引用,注意这里引入EntityFramework.dll是为了支持后面的CodeFirst开发方式,EF版本必须在4.1及以上。PS:你也可以通过Package Manager来安装EntityFramework。到此,我们的准备工作就做好了,接下来就可以开始正式的工作了。

2.2 借助EF CodeFirst生成MySQL数据库

  首先,EF是一种ORM(Object-relational mapping)框架,它能把我们在编程时使用对象映射到底层的数据库结构。ORM框架负责把从数据库传回的记录集转换为对象,也可以依据对象当前所处的具体状态生成相应的SQL命令发给数据库,完成数据的存取工作(常见的数据存取操作可简称为CRUD:Create、Read、Update、Delete)。

  EF给数据库应用系统开发带来了更高的效率,使用它能更容易地写出易维护、易扩展的系统,而且性能虽然比不上ADO.NET,但也足够好,能满足大多数开发场景的需求。与ADO.NET不一样,EF的抽象层次较高它把数据库映射为DbContext,把数据库中存取的数据直接映射为实体(Entity)对象,屏蔽了底层的数据库内部结构,无需直接使用下层数据存取引擎所提供的底层对象(比如ADO.NET所提供的DbConnection,DbCommands等)完成CRUD。

  EF支持三种开发模式:Code First、Database First和Model First。这里我们使用Code First模式,它能帮助我们实现快速开发迭代的目标。最后,EF不是本文的重点,如果你还不了解EF或者Code First,可以参阅金旭亮老师的《EF走马观花》系列文章,这里就不再赘述了。

  (1)在Models文件夹新建一个类,取名为“UserInfo”。它作为我们的实体类,映射到MySQL数据库中的UserInfo表(这里MySQL数据库中还未创建这样的数据表)

    [Serializable]
public class UserInfo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; } [Required]
[MaxLength()]
public string UserName { get; set; } [Required]
[MaxLength()]
public string UserPwd { get; set; }
}

  (2)再在Models文件夹新建一个类,取名为“MyDbContext”,使其继承于DbContext,作为EF操作的数据库上下文使用。需要注意的是:这里的name=MySqlDemo,MySqlDemo为数据库的名字。而这里this.Database.CreateIfNotExists()方法则用于判断MySqlDemo这个数据库是否已存在?如果不存在,那么就创建一个。

    public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=MySqlDemo")
{
this.Database.CreateIfNotExists();
} public virtual DbSet<UserInfo> UserInfo { get; set; }
}

  (3)在Web.config中新增一个数据库连接字符串,database设置为“MySqlDemo”,与上面的MyDbContext中的构造函数中的name保持一致。

 <connectionStrings>
<!--设置对MySql的链接字符串(注意:charset=utf8是为了解决MySql中保存中文的时候出现的乱码的问题) -->
<add name="MySqlDemo" connectionString="server=127.0.0.1;user id=root;password=1234;persist security info=True;database=MySqlDemo;charset=utf8;" providerName="MySql.Data.MySqlClient" />
</connectionStrings>

  至此,因为我们只用到UserInfo这一个表,所以现在我们有关数据库与EF的代码就到此结束。

2.3 自己封装Memcached帮助类对外提供服务接口

  (1)在Web.config中新增一个AppSetting,保存Memcached服务器的地址列表:

    <!-- 设置Memcached服务器集群列表 -->
<add key="MemcachedServers" value="192.168.80.10:11211,192.168.80.11:11211"/>

  (2)在Models中新建一个类,取名为“MemcachedHelper”,我们将其设置为静态类,所以方法全是静态的,我们无需实例化便可直接调用。可以看到,我们这里使用了静态构造函数来初始化全局静态对象,它不属于任何一个实例,所以这个构造函数只会被执行一次,而且是在创建此类的第一个实例或引用任何静态成员之前,由.NET自动调用。

    /// <summary>
/// 自己封装Memcached的帮助类
/// </summary>
public static class MemcacheHelper
{
#region 全局静态对象
// 全局Socket连接池对象
private readonly static SockIOPool sockIOPool;
public static SockIOPool CurrentPool
{
get
{
return sockIOPool;
}
}
// 全局Memcached客户端对象
private static MemcachedClient mc;
#endregion #region 静态构造函数
/// <summary>
/// 静态构造函数
/// </summary>
static MemcacheHelper()
{
// 初始化Memcached服务器列表
string[] serverList = ConfigurationManager.AppSettings["MemcachedServers"].Split(',');
// 初始化Socket连接池
sockIOPool = SockIOPool.GetInstance("MemPool");
sockIOPool.SetServers(serverList);
sockIOPool.Initialize();
// 初始化Memcached客户端
mc = new MemcachedClient();
mc.PoolName = "MemPool";
mc.EnableCompression = false;
}
#endregion #region 封装对外方法
/// <summary>
/// Set-新增或修改
/// </summary>
/// <param name="key">键</param>
/// <param name="value">值</param>
/// <returns>是否成功</returns>
public static bool Set(string key, object value)
{
if (!mc.KeyExists(key))
{
return mc.Add(key, value);
}
else
{
return mc.Set(key, value);
}
} /// <summary>
/// Set-新增或修改
/// </summary>
/// <param name="key">键</param>
/// <param name="value">值</param>
/// <param name="expiry">过期时间</param>
/// <returns>是否成功</returns>
public static bool Set(string key, object value, DateTime expiry)
{
if (!mc.KeyExists(key))
{
return mc.Add(key, value, expiry);
}
else
{
return mc.Set(key, value, expiry);
}
} /// <summary>
/// Get-获取
/// </summary>
/// <param name="key">键</param>
/// <returns>具体数据</returns>
public static object Get(string key)
{
if (!mc.KeyExists(key))
{
return null;
}
else
{
return mc.Get(key);
}
}
#endregion
}

  这里我们没有对SockIOPool进行一列的配置,采用其默认的配置即可。但是要注意的是:SockIOPool的PoolName和MemcacheClient的PoolName必须保持一致。

2.4 用户登录时调用Memcached帮助类接口存储用户登录状态

  (1)在Controller中新建一个控制器,取名为“LogonController”。主要用来显示系统登陆页和进行用户验证的AJAX操作(将用户Session存入Memcached也在此操作中)。至于登录页面的HTML和JS脚本在此就不再赘述,请自行下载Demo文件查看。

    public class LogonController : Controller
{
//
// GET: /Logon/
public ActionResult Index()
{
return View();
} [HttpPost]
public JsonResult ValidateUserInfo(string userName, string userPwd)
{
MyDbContext dbContext = new MyDbContext();
var loginUser = dbContext.UserInfo.Where(u => u.UserName.Equals(userName)).FirstOrDefault(); JsonObject jsonReturn = new JsonObject();
if (loginUser == null)
{
jsonReturn.success = false;
jsonReturn.msg = "您输入的用户名不存在!";
}
else
{
if (!loginUser.UserPwd.Equals(userPwd))
{
jsonReturn.success = false;
jsonReturn.msg = "您输入的密码错误!";
return Json(jsonReturn, JsonRequestBehavior.DenyGet);
} // 申请了一个模拟的Guid:SessionId
Guid sessionId = Guid.NewGuid();
// 把sessionid写到客户端浏览器里面去累
Response.Cookies["sessionId"].Value = sessionId.ToString();
// 将用户实体对象写入到Memcache中
MemcacheHelper.Set(sessionId.ToString(), loginUser, DateTime.Now.AddMinutes());
// 设置成功响应消息
jsonReturn.success = true;
jsonReturn.msg = "恭喜您,登录成功!";
} return Json(jsonReturn, JsonRequestBehavior.DenyGet);
}
}

  现在来看下这个控制器的核心代码:

  ①Index这个Action主要用于显示登录视图,页面代码不再贴出来,只看看下面这个AJAX请求代码:借助JQuery AJAX向ValidateUserInfo这个Action提交用户名和密码,如果服务器端校验成功则返回一个JSON对象,浏览器端判断success属性是否为true,如果为true则跳转到系统主页。

    $.ajax({
type: "POST",
contentType: "application/json;charset=utf-8",
url: "/Logon/ValidateUserInfo",
data: "{ userName:'" + txtUserName + "',userPwd:'" + txtUserPwd + "' }",
dataType: "json",
beforeSend: function () {
showMsg("操作提示", "正在验证中,请稍候...");
},
success: function (resultInfo) {
if (resultInfo != null && resultInfo.success) {
window.location.href = "/Home/Index";
}
else {
showMsg("登录失败", resultInfo.msg);
}
},
error: function () {
showMsg('错误提示', '发生未知错误');
}
});

  ②ValidateUserInfo这个Action则是根据浏览器提交过来的AJAX请求,判断用户名和密码是否正确。这里用到了我们刚刚写的MyDbContext,首先对它进行实例化。(这一步非常重要,这时我们的MySQL数据库中还木有MySqlDemo这个数据库,当第一次实例化MyDbContext时,EF会帮我们在MySQL中创建MySqlDemo这个数据库,其本质其实就是帮我们生成一串:crate database mysqldemo;之类的SQL语句),然后使用Lambda表达式这种优美的语法去进行校验。如果你对Lambda表达式不熟悉,可以参阅MSDN的《Lambda表达式(C#编程指南)》一文来学习下。

  ③就是最核心的部分,我们采用Guid(保证作为Key的唯一性)作为SessionId写入浏览器端的Cookie中,并以此作为Key存入Memcached分布式缓存中,还给它设置了默认失效时间(这里为20分钟)。之后,每次浏览器向服务器端提交请求时,在HTTP报文中都会附带上这个Cookie,服务器端就可以通过这个Cookie作为Key去Memcached服务器中查找Session对象。

PS:一般来说,Memcached的数据Key的命名是有讲究的,这里传智的老马推荐了一个命名规则:{命名空间}-{部门名称}-{项目名称}。我们这里就简单一点,使用Guid来作为Key,因为每次生成的Guid是唯一的。

2.5 封装BaseController解决Action触发前的校验规则

  (1)在以往的信息系统项目开发中,我们在系统里边会做一个全局的校验器,判断用户的每次操作请求是否具有相应的权限,这里我们主要是校验用户是否登录,以便我们在具体的模块中获取用户的Session对象。单机情况下,我们一般就存入本机进程内的Session,因此就主要判断Session中是否存在登录状态。这里我们使用Memcached来存储Session对象,那么我们就在每个Action执行前加一段规则:判断Memcached中是否有当前用户的登录状态,如果有,则继续执行Action。如果木有,那么对不起,请进行登录

  (2)在WebForm中我们可以通过写一个BasePage,使其继承于Page,重写OnLoad事件,再让其他页面继承于BasePage,就可以对用户是否登录这个校验应用到所有继承了BasePage的Page类中。那么,在MVC模式中,请求对象不再是xxx.aspx页面类型,而是/ControllerName/ActionName的路由,因此我们需要寻找一种针对Action的全局过滤方法。额,在此我们不禁想到了一个高大上的英文:AoP(Aspect Oriented Programming),它是软件开发中一个较为热门的话题,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。因此,我们对Action增加全局校验规则就是将Action的校验方法与Action的业务处理相分离,使其耦合度降低,也提高了Action的校验方法的重用性,符合了AoP的思想。那么,扯了大半天,在ASP.NET MVC中到底如何实现呢?别担心,我们可以像写BasePage一样,写一个BaseController来进行处理,使其继承于Controller,然后将其他需要使用用户登录状态校验的Controller都继承于BaseController即可。

    public class BaseController : Controller
{
// 全局当前用户对象
protected UserInfo CurrentUser { get; private set; } // 全局Action执行前的验证操作:类似于AoP的思想,面向切面编程
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext); // 从cookie中获取登录的sessionId
string sessionId = Request["sessionId"];
if (string.IsNullOrEmpty(sessionId))
{
filterContext.Result = new RedirectResult("/Logon/Index");
}
else
{
object obj = MemcacheHelper.Get(sessionId);
CurrentUser = obj as UserInfo;
if (CurrentUser == null)
{
filterContext.Result = new RedirectResult("/Logon/Index");
}
// 再次记录到Memcache分布式缓存中:实现Session的滑动窗口机制
MemcacheHelper.Set(sessionId, CurrentUser, DateTime.Now.AddMinutes());
}
}
}

  (3)现在来看看上面这段代码:

  ①从浏览器提交过来的HTTP请求报文中取得Cookie(也就是SessionId),如果没有Cookie那么肯定还没有登录,将进行重定向到登陆页面;

  ②如果有Cookie那么就去Memcached服务器中查询是否有这个Key的数据内容,如果没有没有内容,那么还是重定向到登陆页面;如果有内容,则返回这个对象,由于返回的是Object类型,所以需要我们转换一下赋给全局的用户对象实例。

  ③获取到UserInfo对象之后,再次将其重新存储到Memcached中,这里其实是将Session延长失效时间,实现了一个Session的滑动时间机制的效果。

PS:因为我们在登录验证的Action里面给这个缓存设置的过期时间是一个绝对的时间,而非滑动过期时间。所谓绝对时间是指到了指定时间以后便会失效,而滑动时间是指在指定时间内无访问请求便失效

2.6 测试运行用户登录与Session存储

  (1)到此,大部分的代码都已完毕。这里我们需要进行一个小测试,新建一个Controller,取名为“HomeController”,将Index页面用作登陆后的主页,显示Session对象中的UserName属性。代码很简单,只有两行:

        public ActionResult Index()
{
// 从Memcached中获取Session并传给View
ViewBag.UserName = CurrentUser.UserName;
return View();
}

  至于View,请参考Demo文件,此处就不在赘述了,就一堆HTML而已。

  (2)①开启虚拟机中的两台Windows Server服务器,确保Memcached服务都已成功开启。(如果你是装在本机的,直接开启服务即可)

      ②开启MySQL数据库服务,这里我的MySQL是装在本机的,所以只需要开启服务即可。

  (3)点击调试,开始运行IIS Express服务器,进行测试吧骚年(这里需要说明的是:你需要首先随便输入一个账号和密码进行数据库的创建,因为我们采用的是Code First的方式,然后再进入MySQL添加几行测试用户数据,这里我已经添加了几行,开始进行测试。)我在数据库里边添加了一行{账号:edisonchou,密码:123456},因此我输入账号和密码,点击登录,会出现一个友好的提示:“正在验证中,请稍候...”(如果你是第一次运行,那么需要创建数据库,这个时间会比较长,不要担心)。

  (4)等待系统验证完成后,便会跳转到系统主页,显示欢迎界面。可以看到,下图中的两处是根据Session的UserName来显示的。

  (5)现在我们手动修改URL,输入/Logon/Index,跳转到登陆页面。然后,再修改URL为/Home/Index,看看是否需要重新登录,由于我们已经将Session存入了Memcached,所以再次进入/Home/Index这个Action时会进行判断,通过浏览器端传过去的Cookie去Memcached中取Session,如存在则继续执行。这里,我们之前已经登录了,所以不再需要重新登录。下面是再次跳转/Home/Index的HTTP报文信息,可以看出登录之后已经写入了Cookie,之后的请求都会带上Cookie传递到服务器端,服务器端也就可以通过Cookie作为Key获取Session对象。

  (6)新开一个浏览器,再使用另一个用户数据进行登录:

  (7)登陆后,查看HTTP报文,获取Cookie中的sessionId:

  (8)根据两个用户的sessionId作为Key,在Memcached服务器中查看存储情况:经查看,两个Session都存储到了192.168.80.11这台服务器里边。为何没有平均分配,这是因为算法是根据计算Hash值来的,刚好这两个sessionId计算后的Hash值都是要分配到192.168.80.11这台服务器里边,所以就都存到了这里边。(需要注意的是:这里仅仅是在我的机器上跑的测试结果,网友们的测试结果可能会跟我的不一样,这是正常的)

  至此,我们的小测试到此结束,我们的案例也到此为止。

三、学习小结

  本篇我首先通过花大力气对Session服务器场景的简介引出分布式缓存对于构建Session服务器的可行性,然后使用ASP.NET MVC+EF Code First+MySQL+Memcached来模拟了一个信息系统登录场景,其中借助了Memcached来存储Session对象,并通过Action的过滤器来实现对用户登录状态的校验。最后通过一个小测试,来验证Memcached是否存储了我们的Session对象。

  当然,此案例只是一个玩具级别的Demo,还需要经过很多次性能测试和优化才能应用到实际开发中。但是,我们可以从中了解到一些有益的思想,理解这些思想对于我们的实际开发能力有所裨益的。最后,希望我的文章能够给更多的像我一样的菜鸟开发者带来一点光明,如果你觉得有用,那就麻烦点一下“推荐”吧,谢谢!

参考文献

   (1)李智慧,《大型网站技术架构-核心原理与案例分析》,http://item.jd.com/11322972.html

  (2)马伦,《Memcached公开课》,http://bbs.itcast.cn/thread-14836-1-1.html

  (3)旋风,《Memcached分布式缓存替代Session解决方案》,http://www.cnblogs.com/xuanfeng/archive/2009/06/04/1494735.html

  (4)金旭亮,《Entity Framework走马观花》,http://blog.csdn.net/bitfan/article/details/12779517

  (5)MSDN,《静态类和静态成员(C#编程指南)》,http://msdn.microsoft.com/zh-cn/library/79b3xss3.aspx

  (6)痞子一毛,《Memcached+Cookie替代Session解决方案(MVC版)》,http://www.cnblogs.com/piziyimao/archive/2013/01/29/2882236.html

  (7)瞌睡龙飞,《.NET中Cache的用法》,http://blog.csdn.net/ttotcs/article/details/7476234

  (8)lulu Studio,《ASP.NET MVC:Action过滤器(Filtering)》,http://www.cnblogs.com/QLeelulu/archive/2008/03/21/1117092.html

附件下载

  (1)MemcachedMvcDemo:http://pan.baidu.com/s/1gd3RvKB

转自:http://www.cnblogs.com/edisonchou/p/3866819.html

【转】 Key/Value之王Memcached初探:三、Memcached解决Session的分布式存储场景的应用的更多相关文章

  1. Key/Value之王Memcached初探:三、Memcached解决Session的分布式存储场景的应用

    一.高可用的Session服务器场景简介 1.1 应用服务器的无状态特性 应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性. PS:提到无状态特性 ...

  2. Key/Value之王Memcached初探:一、掀起Memcached的盖头来

    一.Memcached是何方神圣? 在数据驱动的Web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的HttpRuntim ...

  3. 【转】Key/Value之王Memcached初探:一、掀起Memcached的盖头来

    一.Memcached是何方神圣? 在数据驱动的Web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的HttpRuntim ...

  4. Memcached初探

    一.Memcached是什么 Memcached是由Danga Interactive开发的,高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度. Memcached基于 ...

  5. Memcached(三)Memcached配置参数初解

    一.基本参数在我们第一次安装Memcached时,一般都是用过这个命令: memcached -m 512 -u root -d -l 127.0.0.1 -p 11211 我们先来解释这几个参数的含 ...

  6. [转]memcached+magent实现memcached集群

    From : http://www.cnblogs.com/happyday56/p/3461113.html 首先说明下memcached存在如下问题 本身没有内置分布式功能,无法实现使用多台Mem ...

  7. 二十一.构建memcached服务、LNMP+memcached、PHP的本地Session信息、PHP实现session共享

    proxy client web1 web2   1.构建memcached服务 ]# yum -y install memcached ]# cat /etc/sysconfig/memcached ...

  8. memcached+magent实现memcached集群

    首先说明下memcached存在如下问题 本身没有内置分布式功能,无法实现使用多台Memcache服务器来存储不同的数据,最大程度的使用相同的资源:无法同步数据,容易造成单点故障.(memagent代 ...

  9. memcached 系列2:memcached实例(转载)

    在上一篇文章,我们讲了,为什么要使用memched做为缓存服务器(没看的同学请点 这里).下面让我们以memcached-1.2.1-win32版本的服务组件(安装后是以一个windows服务做dae ...

随机推荐

  1. 数据库优化实践【MS SQL优化开篇】

    数据库定义: 数据库是依照某种数据模型组织起来并存在二级存储器中的数据集合,此集合具有尽可能不重复,以最优方式为特定组织提供多种应用服务,其数据结构独立于应用程序,对数据的CRUD操作进行统一管理和控 ...

  2. Angular ngClick 阻止冒泡和默认行为

    这其实是一个很简单的问题,如果你认真查看过Angular官方的API文档,本来不想记录的.但是这个问题不止一次的被人问起,所以今天在记录在这里. 在Angular中已经对一些ng事件如ngClick, ...

  3. Java多线程系列--“JUC锁”06之 Condition条件

    概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...

  4. 爱上MVC3~在控制器或Action上动态设定模板页(Layout)

    回到目录 很多境况下,我们需要设置自己模块的layout,即它的布局页面,在MVC2中叫它模板页面,你可以在return view方法时设置它,当然,这不是一种好方法,因为我不想每个action都去设 ...

  5. EF架构~在Linq to Entity中使用日期函數

    回到目录 眾所周知,在linq to entity的查询语句中,不允许出现ef不能识别的关键字,如Trim,Substring,TotalDays等.net里的关键字,在EF查询里都是不被支持的,它的 ...

  6. Atitit Server Side Include  ssi服务端包含规范 csi  esi

    Atitit Server Side Include  ssi服务端包含规范 csi  esi 一.CSI (Client Side Includes)  1 1.1. 客户端包含1 1.2. Ang ...

  7. fir.im Weekly - iOS开发中的Git流程

    本期 fir.im Weekly 收集了微博上的热转资源,包含 Android.iOS 开发工具.源码等好用的轮子,还有一些 APP 设计的 Tips,希望对你有用. 精仿知乎日报 iOS 端 @我偏 ...

  8. PHP实现RESTful风格的API实例(二)

    接前一篇PHP实现RESTful风格的API实例(一) Response.php :包含一个Request类,即输出类.根据接收到的Content-Type,将Request类返回的数组拼接成对应的格 ...

  9. 关于js中的同步和异步

    最近看到前端面试问到js中的同步和异步,这个问题该怎么回答? 梳理一下,js对于异步的处理,很多人的第一反应是ajax,这只能说是对了一半. 1.个人觉得,js中,最基础的异步是setTimeout和 ...

  10. How Google TestsSoftware - Part One

    This is the firstin a series of posts on this topic.The one question I get morethan any other is &qu ...