Memcache+Cookie解决分布式系统共享登录状态

Memcached高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度。Memcached能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。

Memcached的特点
Memcached的缓存是一种分布式的,可以让不同主机上的多个用户同时访问, 因此解决了共享内存只能单机应用的局限,更不会出现使用数据库做类似事情的时候,磁盘开销和阻塞的发生。

memcache的优点:

1.解决了高并发访问数据库造成的死锁

2.实现了多客户端的共享缓存

3.读写性快,1s:读取可以1w次,写:10w

4.超简单集群搭建Cluster

5.开源代码

6.学习成本低、入门容易、丰富的成功案例

缺点:

没有提供主从复制功能,也没有提供容灾等功能,数据存在缓存中(例如服务器重启就会丢失数据),所有的代码基本只是考虑性能最佳。

Memcache 基础原理

底层通讯:Socket 

数据:键值对存储,key最大255个字符,item最大1MB,当然key/item最好都别太大,最长过期时间是30天 

内存处理的算法:

本质就是一个大的哈希表。key最大长度是255个字符。

内存模型:Memcache预先将可支配的内存空间进行分区(Slab),每个分区里再分成多个块(Chunk)大小1MB,但同一个分区里:块的长度(bytes)是固定的。

插入数据:查找适合自己长度的块,然后插入,会有内存浪费。

LRU,闲置>过期 >最少访问

惰性删除:它并没有提供监控数据过期的机制,而是惰性的,当查询到某个key数据时,如果过期那么直接抛弃。

集群搭建原理:

Memcache服务器端并没有提供集群功能,但是通过客户端的驱动程序实现了集群配置。 客户端实现集群的原理:首先客户端配置多台集群机器的ip和端口的列表。然后客户端驱动程序在写入之前,首先对key做哈希处理得到哈希值后对总的机器的个数进行取余然后就选择余数对应的机器。

Windows下使用Memcache

下载Memcache

安装服务:以管理员的身份打开cmd进入memcache所在文件夹,输入安装命令:Memcached.exe -d install

打开服务会看到memcached已安装

打开服务监控窗口可以查看服务是否启动。 启动服务:cmd→Memcached.exe -d start(restart重启,stop关闭服务)

检查服务是否启动:

1.开启telnet 

2.在控制台输入命令:telnet 192.168.1.22 11211(ip地址  端口号:默认为11211)

 通过 memcached.exe -h 可以查看其帮助

3.在控制台输入命令检查当前服务状态:stats

卸载服务:Memcached.exe -d uninstall(先关闭服务)

遇到问题:win8下安装服务。无法启动此程序,因为计算机中丢失 MSVCR71.dll。尝试重新安装该程序以解决此问题。下载dll地址:http://www.dll-files.com/dllindex/dll-files.shtml?msvcr71

二   .NET memcached client library
   下载文件:https://sourceforge.net/projects/memcacheddotnet/

   里面有.net1.1  和 .net2.0的两种版本  还有一个不错的例子。

三  应用

  1 将Commons.dll,ICSharpCode.SharpZipLib.dll,log4net.dll,Memcached.ClientLibrary.dll 放到lib目录,如果项目中已存在log4net.dll则不需要再引用
  2 项目右击添加引用
  3 代码

using Memcached.ClientLibrary;
using System;

namespace CZBK.ItcastOA.MemcaheDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //ip地址:端口号
            string[] serverlist = { "192.168.1.95:11211", "10.0.0.132:11211" };

            //初始化池
            SockIOPool pool = SockIOPool.GetInstance();
            pool.SetServers(serverlist);

            pool.InitConnections = 3;
            pool.MinConnections = 3;
            pool.MaxConnections = 5;

            pool.SocketConnectTimeout = 1000;
            pool.SocketTimeout = 3000;

            pool.MaintenanceSleep = 30;
            pool.Failover = true;

            pool.Nagle = false;
            pool.Initialize();

            // 获得客户端实例
            MemcachedClient mc = new MemcachedClient();
            mc.EnableCompression = false;

            Console.WriteLine("------------测  试-----------");
            mc.Set("test", "my value");  //存储数据到缓存服务器,这里将字符串"my value"缓存,key 是"test"

            if (mc.KeyExists("test"))   //测试缓存存在key为test的项目
            {
                Console.WriteLine("test is Exists");
                Console.WriteLine(mc.Get("test").ToString());  //在缓存中获取key为test的项目
            }
            else
            {
                Console.WriteLine("test not Exists");
            }

            Console.ReadLine();

            mc.Delete("test");  //回车移除缓存中key为test的项目

            if (mc.KeyExists("test"))
            {
                Console.WriteLine("test is Exists");
                Console.WriteLine(mc.Get("test").ToString());
            }
            else
            {
                Console.WriteLine("test not Exists");
            }
            Console.ReadLine();

            SockIOPool.GetInstance().Shutdown();  //关闭池, 关闭sockets
        }
    }
}

 新建同一过滤器类BaseController,在控制器中把继承的Controller换成BaseController

BaseController类过滤器代码

using CZBK.ItcastOA.Common;
using CZBK.ItcastOA.Model;
using System;
using System.Web.Mvc;

namespace CZBK.ItcastOA.WebApp.Controllers
{
    public class BaseController : Controller
    {
        /// <summary>
        /// 记录登陆用户
        /// </summary>
        public UserInfo LoginUser { get; set; }
        /// <summary>
        /// 执行控制器前执行该方法
        /// </summary>
        /// <param name="filterContext"></param>
        protected override void OnActionExecuting(ActionExecutingContext filterContext)

        {
            base.OnActionExecuting(filterContext);
            bool isExt = false;
            //if (Session["userInfo"]==null)
            if(Request["sessionId"]!=null)
            {
                //接收从cookie中传过来的Memcache的key
                string sessionId = Request.Cookies["sessionId"].Value;
                //根据key从Memcache中获取用户的信息
                object obj = MemcacheHelper.Get(sessionId);
                if (obj!=null)
                {
                    UserInfo userInfo = SerializerHelper.DeserializeToObject<UserInfo>(obj.ToString());
                    LoginUser = userInfo;
                    isExt = true;
                    //模拟滑动过期时间
                    MemcacheHelper.Set(sessionId,obj.ToString(),DateTime.Now.AddMinutes(30));
                }
               
            }
            if (!isExt)
            {
                //此跳转会执行完index的方法返回结果才去跳转到登录页面,所以不能用这种方法跳转
                //filterContext.HttpContext.Response.Redirect("/Login/Index");
                //此方法直接跳转到登录页面,后面的代码不会执行
                filterContext.Result = Redirect("/Login/Index");
            }
        }
    }
}
View Code

登录页面代码

using CZBK.ItcastOA.Common;
using CZBK.ItcastOA.IBLL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace CZBK.ItcastOA.WebApp.Controllers
{
    public class LoginController : Controller
    {
        IUserInfoService UserInfoService { get; set; }
        // GET: Login
        public ActionResult Index()
        {
            //登录时判断Cookie的信息
            CheckCookieInfo();
            return View();
        }

        #region 获取验证码
        public ActionResult GetCodeImage()
        {
            ValidateCode vlidatecode = new ValidateCode();
            //产生验证码
            string code = vlidatecode.CreateValidateCode(4);
            //存到session中验证
            Session["validatecode"] = code;
            //将验证码画到画布上,返回文件
            byte[] buffer = vlidatecode.CreateValidateGraphic(code);
            //返回文件
            return File(buffer, "image/jpeg");
        }
        #endregion

        #region 用户登录
        public ActionResult UserLogin()
        {
            //获取验证码验证是否正确
            //验证码为空
            string validatecode = Session["validatecode"] != null ? Session["validatecode"].ToString() : string.Empty;
            if (string.IsNullOrEmpty(validatecode))
            {
                return Content("no:验证码错误!");
            }

            //验证码不为空
            //清空session
            Session["validatecode"] = null;
            //获取前台输入的code
            string txtcode = Request["vCode"].ToString();
            //比较验证码忽略大小写
            if (!validatecode.Equals(txtcode, StringComparison.InvariantCultureIgnoreCase))
            {
                return Content("no:验证码错误!");
            }

            //验证用户名和密码
            string userName = Request["LoginCode"];
            string userPwd = Request["LoginPwd"];
            var userInfo = UserInfoService.LoadEntities(a => a.UName == userName && a.UPwd == userPwd).FirstOrDefault();
            if (userInfo != null)
            {
                //记住登录账号
                //Session["userInfo"] = userInfo;

                //用memcache存储用户信息
                //作为Memcache的key
                string sessionId = Guid.NewGuid().ToString();
                //使用Memcache代替Session解决数据在不同Web服务器之间共享的问题。
                MemcacheHelper.Set(sessionId, SerializerHelper.SerializeToString(userInfo), DateTime.Now.AddMinutes(30));
                //将Memcache的key以cookie的形式返回到浏览器端的内存中,当用户再次请求其它的页面请求报文中会以Cookie将该值再次发送服务端。
                Response.Cookies["sessionId"].Value = sessionId;

                //记住登录账号
                if (!string.IsNullOrEmpty(Request["checkMe"]))
                {
                    HttpCookie cookie1 = new HttpCookie("cp1", userInfo.UName);
                    //HttpCookie cookie2 = new HttpCookie("cp2", WebCommon.GetMd5String(userInfo.UPwd));
                    HttpCookie cookie2 = new HttpCookie("cp2", userInfo.UPwd);
                    cookie1.Expires = DateTime.Now.AddDays(3);
                    cookie2.Expires = DateTime.Now.AddDays(3);
                    Response.Cookies.Add(cookie1);
                    Response.Cookies.Add(cookie2);
                }
                return Content("ok:登录成功!");
            }
            return Content("no:登录失败!");
        }
        #endregion

        #region 登录时判断Cookie的信息
        private void CheckCookieInfo()
        {
            if (Request.Cookies["cp1"] != null && Request.Cookies["cp2"] != null)
            {
                string userName = Request.Cookies["cp1"].Value;
                string userPwd = Request.Cookies["cp2"].Value;
                //判断Cookie中存储的用户名和密码是否正确
                var userInfo = UserInfoService.LoadEntities(a => a.UName == userName && a.UPwd == userPwd).FirstOrDefault();
                if (userInfo != null)
                {
                    //再次记录到Memcache中,30分钟过期
                    string sessionId = Guid.NewGuid().ToString();
                    MemcacheHelper.Set(sessionId, SerializerHelper.SerializeToString(userInfo), DateTime.Now.AddMinutes(30));
                    Response.Cookies["sessionId"].Value = sessionId;
                    Response.Redirect("/home/Index");
                }
                //Response.Redirect表示重定向页面,如果执行Response.Redirect代码则下两句代码不执行
                //存在cookies但是错误则设置过期
                Response.Cookies["cp1"].Expires = DateTime.Now.AddDays(-1);
                Response.Cookies["cp2"].Expires = DateTime.Now.AddDays(-1);
            }
        }
        #endregion

        #region 退出登录
        public ActionResult Logout()
        {
            if (Request.Cookies["sessionId"] != null)
            {
                string key = Request.Cookies["sessionId"].Value;
                Common.MemcacheHelper.Delete(key);
                Response.Cookies["cp1"].Expires = DateTime.Now.AddDays(-1);
                Response.Cookies["cp2"].Expires = DateTime.Now.AddDays(-1);
            }
            return Redirect("/Login/Index");
        }
        #endregion
    }
}
View Code

关于用户登录状态存session,cookie还是数据库或者memcache的优劣

memcached参考链接:

http://www.cnblogs.com/zjneter/archive/2007/07/19/822780.html

http://www.cnblogs.com/xulb597/archive/2012/11/21/2780506.html

原文地址:https://www.cnblogs.com/shiruina/p/9288164.html