nginx+iis+redis 搭建的站点来解决session一致性的解决方案

上篇文章( http://blog.csdn.net/u010533180/article/details/52805792)中使用asp.net 自带的服务存储sesion 来解决sesion一致性的问题。那么这篇文章使用redis 来解决session一致性的问题。
1. 首先下载windows版本的redis。
Windows下Redis项目: https://github.com/MSOpenTech/redis
源代码: Redis官网下载页面: http://redis.io/download
我这里下载的是 Redis-x64-3.2.100.zip 这个版本的redis.
解压后找到放到一台机器上,我这里放到了192.168.164.133 这台服务器上面。我这里放到了C里面,如下图所示。
这里写图片描述

其中redis-server.exe 是运行到服务器上面。
redis-cli.exe是客户端,用来连接服务器的。
其余的可以不进行考虑。
2.修改redis.windows.conf文件

指定访问密码

requirepass foobared

requirepass redistest

设置最大堆内存限制,两者设置一个即可

maxheap

maxheap 512000000

设置最大内存限制, 两者设置一个即可

maxmemory

maxmemory 512000000

修改配置文件redis.windows.conf,如果有中文,请另存为UTF-8编码.

手工运行redis-server.exe ,出现类似如下的界面,表示启动成功,而且这个界面不能进行关闭,如果注册为服务,则可以进行关闭。
这里写图片描述

强烈建议把redis-server.exe注册为服务启动,这样启动机器时,把这个服务也启动,不用每次收到启动redis服务器了。注册命令如下,:
注册服务,可以保存为 service-install.bat 文件,运行这个文件:

redis-server.exe --service-install redis.windows.conf --loglevel verbose
redis-server --service-start

对应的卸载命令如下:
卸载服务, 可以保存为 uninstall-service.bat
redis-server –service-stop
redis-server –service-uninstall

上面的bat文件存放在redis-server.exe同一目录下面,如下图:
这里写图片描述

在服务列表中查看redis服务。其中启动类型为自动。如下图
这里写图片描述

服务注册成功后,redis-server.exe文件夹下面可能会有一个redis.windows-server.confi文件,如果需要修改redis服务器的配置,则修改这个文件即可。不需要修改redis.windows.config文件。

如果需要自行定义config文件或者需要知道config文件里面节点的含义,请读者自行查阅。

redis服务器所在的ip为192.168.164.133 端口号为6379. 我在192.168.164.130服务器上运行redis-cli.exe测试连接,需要指定IP 端口 和密码。 如下图:
这里写图片描述

这里建议写成bat文件,下次直接运行 便连接上redis服务器,不用再进行切换命令。bat文件代码如下:

start c:\redis\redis-cli.exe -h 192.168.164.133 -p 6379 -a redistest

这里写图片描述

3.接着修改项目中的web.config文件,使session存储在自定义的redis中。
这里写图片描述.
文件中的关键代码下:

     <httpRuntime/>
    <!--<sessionState mode="StateServer" stateConnectionString="tcpip=192.168.164.129:42424" timeout="60" stateNetworkTimeout="20"/>-->
    <sessionState timeout="60" mode="Custom" customProvider="RedisSessionStateProvider" cookieless="false">
      <providers>
        <add name="RedisSessionStateProvider" writeexceptionstoeventlog="false" type="RedisProvider.SessionProvider.CustomServiceProvider" 
             server="192.168.164.133" port="6379" password="redistest"/>
      </providers>

其中redis 部署在192.168.164.133 服务器上面,端口号是默认的端口号 6379 密码设置的为redistest.

4.在测试的项目中添加RedisProvider.SessionProvider类库, 因为这个是自定义session里面存储的那个节点名义。如上面的sesionstate里面的节点里面的节点里面的type属性的值。
在这个类库里面添加CustomServiceProvider类,自定义Session保存的位置必须实现System.Web.SessionState.SessionStateStoreProviderBase这个基类,并重新里面的方法,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections.Specialized;
using System.Web.SessionState;
using ServiceStack.Redis;
using System.Configuration;
using System.Configuration.Provider;
using System.Web.Configuration;
using System.IO;
using System.Xml;
namespace RedisProvider.SessionProvider
{

    #region Session Item Model
    [Serializable]
    public class SessionItem
    {

        #region Properties
        public DateTime CreatedAt { get; set; }
        public DateTime LockDate { get; set; }
        public int LockID { get; set; }
        public int Timeout { get; set; }
        public bool Locked { get; set; }
        public string SessionItems { get; set; }
        public int Flags { get; set; }
        #endregion Properties

    }
    #endregion Session Item Model

    public class CustomServiceProvider : System.Web.SessionState.SessionStateStoreProviderBase, IDisposable
    {
        /// <summary>
        /// 相关Redis的配置
        /// </summary>
        private CustomReaderRedisWebConfig redisCfg = CustomReaderRedisWebConfig.GetCustomerReaderRedisWebConfig;
        #region Properties
        private string ApplicationName
        {
            get
            {
                if (ConfigurationManager.AppSettings.AllKeys.Contains("Application.Name"))
                {
                    return ConfigurationManager.AppSettings["Application.Name"];
                }

                return string.Empty;
            }
        }

        private RedisClient RedisSessionClient
        {
            get
            {
                if (!string.IsNullOrEmpty(redisCfg.RedisPassword))
                {
                    return new RedisClient(redisCfg.RedisServer, redisCfg.RedisPort, redisCfg.RedisPassword);
                }

                return new RedisClient(redisCfg.RedisServer, redisCfg.RedisPort);
            }
        }
        #endregion Properties

        #region Private Methods
        /// <summary>
        /// Prepends the application name to the redis key if one exists. Querying by application name is recommended for session
        /// </summary>
        /// <param name="id">The session id</param>
        /// <returns>Concatenated string applicationname:sessionkey</returns>
        private string RedisKey(string id)
        {
            return string.Format("{0}{1}", !string.IsNullOrEmpty(this.ApplicationName) ? this.ApplicationName + ":" : "", id);
        }
        #endregion Private Methods

        #region Constructor

        public CustomServiceProvider()
        {
        }
        #endregion Constructor

        #region Overrides
        public override void Dispose()
        {

        }

        public override void Initialize(string name, NameValueCollection config)
        {

            // Initialize values from web.config.
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (name == null || name.Length == 0)
            {
                name = "RedisSessionStateStore";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Redis Session State Provider");
            }

            // Initialize the abstract base class.
            base.Initialize(name, config);
        }

        public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
        {
            return true;
        }

        public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
        {
            using (RedisClient client = this.RedisSessionClient)
            {
                // Serialize the SessionStateItemCollection as a string.
                string sessionItems = Serialize((SessionStateItemCollection)item.Items);

                try
                {
                    if (newItem)
                    {
                        SessionItem sessionItem = new SessionItem();
                        sessionItem.CreatedAt = DateTime.UtcNow;
                        sessionItem.LockDate = DateTime.UtcNow;
                        sessionItem.LockID = 0;
                        sessionItem.Timeout = item.Timeout;
                        sessionItem.Locked = false;
                        sessionItem.SessionItems = sessionItems;
                        sessionItem.Flags = 0;

                        client.Set<SessionItem>(this.RedisKey(id), sessionItem, DateTime.UtcNow.AddMinutes(item.Timeout));
                    }
                    else
                    {
                        SessionItem currentSessionItem = client.Get<SessionItem>(this.RedisKey(id));
                        if (currentSessionItem != null && currentSessionItem.LockID == (int?)lockId)
                        {
                            currentSessionItem.Locked = false;
                            currentSessionItem.SessionItems = sessionItems;
                            client.Set<SessionItem>(this.RedisKey(id), currentSessionItem, DateTime.UtcNow.AddMinutes(item.Timeout));
                        }
                    }
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
        {
            return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions);
        }

        public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
        {
            return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actionFlags);
        }

        private SessionStateStoreData GetSessionStoreItem(bool lockRecord,
          HttpContext context,
          string id,
          out bool locked,
          out TimeSpan lockAge,
          out object lockId,
          out SessionStateActions actionFlags)
        {

            // Initial values for return value and out parameters.
            SessionStateStoreData item = null;
            lockAge = TimeSpan.Zero;
            lockId = null;
            locked = false;
            actionFlags = 0;

            // String to hold serialized SessionStateItemCollection.
            string serializedItems = "";

            // Timeout value from the data store.
            int timeout = 0;

            using (RedisClient client = this.RedisSessionClient)
            {
                try
                {
                    if (lockRecord)
                    {
                        locked = false;
                        SessionItem currentItem = client.Get<SessionItem>(this.RedisKey(id));

                        if (currentItem != null)
                        {
                            // If the item is locked then do not attempt to update it
                            if (!currentItem.Locked)
                            {
                                currentItem.Locked = true;
                                currentItem.LockDate = DateTime.UtcNow;
                                client.Set<SessionItem>(this.RedisKey(id), currentItem, DateTime.UtcNow.AddMinutes(redisCfg.SessionTimeOut));
                            }
                            else
                            {
                                locked = true;
                            }
                        }
                    }

                    SessionItem currentSessionItem = client.Get<SessionItem>(this.RedisKey(id));

                    if (currentSessionItem != null)
                    {
                        serializedItems = currentSessionItem.SessionItems;
                        lockId = currentSessionItem.LockID;
                        lockAge = DateTime.UtcNow.Subtract(currentSessionItem.LockDate);
                        actionFlags = (SessionStateActions)currentSessionItem.Flags;
                        timeout = currentSessionItem.Timeout;
                    }
                    else
                    {
                        locked = false;
                    }

                    if (currentSessionItem != null && !locked)
                    {
                        // Delete the old item before inserting the new one
                        client.Remove(this.RedisKey(id));

                        lockId = (int?)lockId + 1;
                        currentSessionItem.LockID = lockId != null ? (int)lockId : 0;
                        currentSessionItem.Flags = 0;

                        client.Set<SessionItem>(this.RedisKey(id), currentSessionItem, DateTime.UtcNow.AddMinutes(redisCfg.SessionTimeOut));

                        // If the actionFlags parameter is not InitializeItem,
                        // deserialize the stored SessionStateItemCollection.
                        if (actionFlags == SessionStateActions.InitializeItem)
                        {
                            item = CreateNewStoreData(context, 30);
                        }
                        else
                        {
                            item = Deserialize(context, serializedItems, timeout);
                        }
                    }
                }

                catch (Exception e)
                {
                    throw e;
                }
            }

            return item;
        }

        public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
        {

            using (RedisClient client = this.RedisSessionClient)
            {
                SessionItem currentSessionItem = client.Get<SessionItem>(this.RedisKey(id));

                if (currentSessionItem != null && (int?)lockId == currentSessionItem.LockID)
                {
                    currentSessionItem.Locked = false;
                    client.Set<SessionItem>(this.RedisKey(id), currentSessionItem, DateTime.UtcNow.AddMinutes(redisCfg.SessionTimeOut));
                }
            }
        }
        public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
        {
            using (RedisClient client = this.RedisSessionClient)
            {
                // Delete the old item before inserting the new one
                client.Remove(this.RedisKey(id));
            }
        }

        public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
        {
            using (RedisClient client = this.RedisSessionClient)
            {
                SessionItem sessionItem = new SessionItem();
                sessionItem.CreatedAt = DateTime.Now.ToUniversalTime();
                sessionItem.LockDate = DateTime.Now.ToUniversalTime();
                sessionItem.LockID = 0;
                sessionItem.Timeout = timeout;
                sessionItem.Locked = false;
                sessionItem.SessionItems = string.Empty;
                sessionItem.Flags = 0;

                client.Set<SessionItem>(this.RedisKey(id), sessionItem, DateTime.UtcNow.AddMinutes(timeout));
            }
        }

        public override SessionStateStoreData CreateNewStoreData(System.Web.HttpContext context, int timeout)
        {
            return new SessionStateStoreData(new SessionStateItemCollection(),
                SessionStateUtility.GetSessionStaticObjects(context),
                timeout);
        }

        public override void ResetItemTimeout(HttpContext context, string id)
        {

            using (RedisClient client = this.RedisSessionClient)
            {
                try
                {
                    // TODO :: GET THIS VALUE FROM THE CONFIG
                    client.ExpireEntryAt(id, DateTime.UtcNow.AddMinutes(redisCfg.SessionTimeOut));
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        public override void InitializeRequest(HttpContext context)
        {
            // Was going to open the redis connection here but sometimes I had 5 connections open at one time which was strange
        }

        public override void EndRequest(HttpContext context)
        {
            this.Dispose();
        }
        #endregion Overrides

        #region Serialization
        /// <summary>
        /// Serialize is called by the SetAndReleaseItemExclusive method to
        /// convert the SessionStateItemCollection into a Base64 string to
        /// be stored in MongoDB.
        /// </summary>
        private string Serialize(SessionStateItemCollection items)
        {
            using (MemoryStream ms = new MemoryStream())
            using (BinaryWriter writer = new BinaryWriter(ms))
            {
                if (items != null)
                    items.Serialize(writer);

                writer.Close();

                return Convert.ToBase64String(ms.ToArray());
            }
        }

        private SessionStateStoreData Deserialize(HttpContext context, string serializedItems, int timeout)
        {
            using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(serializedItems)))
            {
                SessionStateItemCollection sessionItems = new SessionStateItemCollection();

                if (ms.Length > 0)
                {
                    using (BinaryReader reader = new BinaryReader(ms))
                    {
                        sessionItems = SessionStateItemCollection.Deserialize(reader);
                    }
                }

                return new SessionStateStoreData(sessionItems,
                  SessionStateUtility.GetSessionStaticObjects(context),
                  timeout);
            }
        }
        #endregion Serialization
    }
}


using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Configuration;
using System.Xml;

namespace RedisProvider.SessionProvider
{
    public class CustomReaderRedisWebConfig
    {
        private static CustomReaderRedisWebConfig customerReaderWebConfig = null;

        // 定义一个标识确保线程同步
        private static readonly object locker = new object();

        static ConfigXmlDocument doc = new ConfigXmlDocument();
        public static CustomReaderRedisWebConfig GetCustomerReaderRedisWebConfig
        {
            get
            {
                if (customerReaderWebConfig == null)
                {
                    lock (locker)
                    {
                        customerReaderWebConfig = new CustomReaderRedisWebConfig();
                        doc.Load(HttpContext.Current.Server.MapPath("web.config"));
                    }
                }
                return customerReaderWebConfig;
            }
        }
        /// <summary>
        /// Sessio过期时间,默认是20分钟
        /// </summary>
        public int SessionTimeOut
        {
            get
            {
                int sessionTimeOut = 20;
                XmlNode node = doc.SelectSingleNode("configuration/system.web / sessionState");
                if (node == null || node.Attributes["timeout"] == null)
                    return sessionTimeOut;
                if (!int.TryParse(node.Attributes["timeout"].Value, out sessionTimeOut))
                {
                    sessionTimeOut = 20;
                }
                return sessionTimeOut;
            }
        }
        public bool CookieLess
        {
            get
            {
                XmlNode node = doc.SelectSingleNode("configuration/system.web / sessionState");
                if (node == null || node.Attributes["cookieless"] == null)
                    return false;
                return node.Attributes["cookieless"].Value.ToUpper().Equals("TRUE");
            }
        }
        /// <summary>
        /// Redis连接的名称
        /// </summary>
        public string CustomProvider
        {
            get
            {
                XmlNode node = doc.SelectSingleNode("configuration/system.web / sessionState");
                if (node == null || node.Attributes["customProvider"] == null)
                {
                    throw new ConfigurationErrorsException("customProvider node not found in sessionstate ");
                }
                return node.Attributes["customProvider"].Value;
            }
        }
        /// <summary>
        /// 是否向Redis服务器书写错误日志
        /// </summary>
        public bool WriteExceptionsToEventLog
        {
            get
            {
                XmlNode node = doc.SelectSingleNode("configuration/system.web/ sessionState/providers/add[@name='" + CustomProvider + "']");
                if (node == null || node.Attributes["writeExceptionsToEventLog"] == null)
                {
                    return false;
                }
                return node.Attributes["writeExceptionsToEventLog"].Value.ToUpper().Equals("TRUE");
            }
        }
        /// <summary>
        /// 获取Redis服务器地址
        /// </summary>
        public string RedisServer
        {
            get
            {
                XmlNode node = doc.SelectSingleNode("configuration/system.web/ sessionState/providers/add[@name='" + CustomProvider + "']");
                if (node == null || node.Attributes["server"] == null)
                {
                    return "localhost";
                }
                return node.Attributes["server"].Value;
            }
        }
        /// <summary>
        /// Redis端口号
        /// </summary>
        public int RedisPort
        {
            get
            {
                int port = 6379;
                XmlNode node = doc.SelectSingleNode("configuration/system.web/ sessionState/providers/add[@name='" + CustomProvider + "']");
                if (node == null || node.Attributes["port"] == null)
                {
                    return port;
                }
                if (!int.TryParse(node.Attributes["port"].Value, out port))
                {
                    port = 6379;
                }
                return port;
            }
        }
        /// <summary>
        /// Redis端口号
        /// </summary>
        public string RedisPassword
        {
            get
            {
                XmlNode node = doc.SelectSingleNode("configuration/system.web/ sessionState/providers/add[@name='" + CustomProvider + "']");
                if (node == null || node.Attributes["password"] == null)
                {
                    return "";
                }
                return node.Attributes["password"].Value;
            }
        }
        private CustomReaderRedisWebConfig()
        {

        }


    }
}

把这个类库引用到测试的web项目中。其中using ServiceStack.Redis; 这个dll需要进行下载,我这里使用vs自带的下载。按照下面的操作进行下载即可。(要求项目的Framework 为4.5以上,因为它们官方提供的是4.5,以前的版本我没有找到)。
这里写图片描述

在搜索框中输入redis,如下图:

这里写图片描述

在你的项目中根文件下下面会有packages的文件里面有如下下载的文件:
这里写图片描述
找到对应的dll引入即可。

其中用到的default.aspx页面代码如下:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using RedisProvider.SessionProvider;
namespace NginxWebProject
{
    public partial class _default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            int port = Request.Url.Port;
            if (port == 8081)
            {
                Response.Write("第一个页面<br/>");
            }
            else if (port == 8082)
            {
                Response.Write("第二个页面<br/>");
            }
            else
            {
                Response.Write(port.ToString() + "<br/>");
            }

            Response.Write("请求开始时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "<br/>");
            Response.Write("服务器名称:" + Server.MachineName + "<br/>"); //服务器名称  
            Response.Write("服务器IP地址:" + Request.ServerVariables["LOCAL_ADDR"] + "<br/>"); //服务器IP地址  
            Response.Write("HTTP访问端口:" + Request.ServerVariables["SERVER_PORT"]);//HTTP访问端口"
            Response.Write(".NET解释引擎版本:" + ".NET CLR" + Environment.Version.Major + "." + Environment.Version.Minor + "quot;." + Environment.Version.Build + "." + Environment.Version.Revision + "<br/>"); //.NET解释引擎版本  
            Response.Write("服务器操作系统版本:" + Environment.OSVersion.ToString() + "<br/>");//服务器操作系统版本  
            Response.Write("服务器IIS版本:" + Request.ServerVariables["SERVER_SOFTWARE"] + "<br/>");//服务器IIS版本  
            Response.Write("服务器域名:" + Request.ServerVariables["SERVER_NAME"] + "<br/>");//服务器域名  
            Response.Write("虚拟目录的绝对路径:" + Request.ServerVariables["APPL_RHYSICAL_PATH"] + "<br/>");//虚拟目录的绝对路径  
            Response.Write("执行文件的绝对路径:" + Request.ServerVariables["PATH_TRANSLATED"] + "<br/>");//执行文件的绝对路径  
            Response.Write("虚拟目录Session总数:" + Session.Contents.Count.ToString() + "<br/>"); //虚拟目录Session总数  
            Response.Write("虚拟目录Application总数:" + Application.Contents.Count.ToString() + "<br/>");//虚拟目录Application总数  
            Response.Write("域名主机:" + Request.ServerVariables["HTTP_HOST"] + "<br/>");//域名主机  
            Response.Write("服务器区域语言:" + Request.ServerVariables["HTTP_ACCEPT_LANGUAGE"] + "<br/>");//服务器区域语言  
            Response.Write("用户信息:" + Request.ServerVariables["HTTP_USER_AGENT"] + "<br/>");
            Response.Write("CPU个数:" + Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS") + "<br/>");//CPU个数  
            Response.Write("CPU类型:" + Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER") + "<br/>");//CPU类型  
            Response.Write("请求来源地址:" + Request.Headers["X-Real-IP"] + "<br/>");
            if (Session["port"] == null)
            {
                Response.Write("Session为null<br/>");
                SessionItem si = new SessionItem();
                si.CreatedAt = DateTime.Now;
                si.SessionItems = port.ToString();
                //LoginSession loginSession = new LoginSession();
                //loginSession.LoginPort = port;
                //loginSession.LoginTime = DateTime.Now;
                Session["port"] = si;

            }
            else
            {
                CustomServiceProvider custom = new CustomServiceProvider();
                SessionItem loginSession = Session["port"] as SessionItem;
                if (loginSession != null)
                {
                    Response.Write("port:" + loginSession.SessionItems + ";createTime:" + loginSession.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss") + "</br>");
                    if (loginSession.Locked)
                    {
                        Response.Write("lockID:" + loginSession.LockID + ";" + loginSession.LockDate.ToString("yyyy-MM-dd HH:mm:ss") + "</br>");
                    }
                    Response.Write("flags:" + loginSession.Flags + ";timeout" + loginSession.Timeout + "</br>");
                    Response.Write("SessionId:" +Session.SessionID+ "</br>");
                    custom.RemoveItem(this.Context, Session.SessionID, null, null);
                }
                else
                {
                    Response.Write("不知道何种原因从Session里面取到的LoginSession 为null");
                }
            }
        }

    }
}

最终的运行界面如下:
这里写图片描述
这里写图片描述
这里写图片描述
至于nginx的搭建,详见:http://blog.csdn.net/u010533180/article/details/52784696

如果想要删除Session 则调用如下的方法:
CustomServiceProvider custom = new CustomServiceProvider();
custom.RemoveItem(this.Context, Session.SessionID, null, null);
删除Session之后的界面如下:
这里写图片描述

本次测试的代码如下:
链接: http://pan.baidu.com/s/1pKR3VrP 密码: 3anx

参考文献:
http://www.codeceo.com/article/nginx-iis-load-balance.html

http://www.cnblogs.com/yanweidie/archive/2015/08/17/4678095.html

https://github.com/MSOpenTech/redis/releases

https://github.com/cncounter/cncounter/blob/master/cncounter/src/test/resources/Redis%E6%9C%AC%E5%9C%B0%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md

http://huangliangbao.iteye.com/blog/2217717

http://www.cnblogs.com/Drama/p/4159364.html

http://blog.sina.com.cn/s/blog_60fb0b3f0100szkd.html

备注:以上项目使用的是redis 4.0以上的版本,这个版本有个问题就是商业化后,每小时6000次的访问量。如下图的错误:这里写图片描述

解决方案在一篇文中进行详细解决。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值