一些项目整理出的项目中引入缓存的架构设计方案,希望能帮助你更好地管理项目缓存,作者水平有限,如有不足还望指点。

一、基础结构介绍

项目中对外提供方法的是CacheProvider和MQProvider两个类,一切缓存或队列应用都从这里做入口,后期更换缓存或队列只需要更改后面的提供者即可

主要结构设计分为三部分:

1、Key管理(用于管理缓存Key、过期时间、是否启用、调用识别Key等)

Configs -> Cache -> KeyConfigList.xml(配置Key的具体信息)

Cache -> Key -> KeyEntity.cs(XML的序列化对象)

Cache -> Key -> KeyManager.cs(读取XML并监听XML文件的变更,如果变更重新读取)

Cache -> Key -> KeyNames.cs(Key名称的枚举,控制Key从这里集中管理,不会到处都是)

2、内部操作(对接的多个缓存实际提供技术比如Redis、Memcached、LocalCache等)

Cache -> Redis -> RedisManager.cs(Redis的连接对象及基本配置)

3、对外提供(对项目中应用缓存提供支持函数,如更改缓存提供技术只需从这里调整代码,不影响项目主体代码)

Cache -> CacheProvider.cs(项目中的缓存操作提供函数类)

MQ -> MQProvider.cs(项目中的队列操作提供函数类)

二、代码详细介绍

1、KeyConfigList.xml

用于存储缓存中数据的Key、有效时间、是否启用此缓存等配置信息

name:用来寻找此条Key信息的标识

key:缓存中存的Key

validTime:便于计算此缓存的有效时间,比如只缓存5分钟

enabled:是否启用此缓存,不启用则每次都读库

{0}、{1}、{2}:缓存Key的占位符用于区分某个类型的缓存其中的一个,比如商品缓存格式为Goods:{0},可能实际存储Key是Goods:1、Goods:2、Goods:3,这个1、2、3是商品Id来区分具体某个,如果量大禁用时会导致缓存雪崩,可以考虑再根据类型或其他来细分

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- name:程序key、key:缓存key(用:冒号来对缓存数据进行分级)、 validTime:有效时间(单位:分)、enabled:是否有效、{0}:占位符替换相关id等标识 -->
<list>
<!-- 一个占位符key -->
<item name="Admin_User_Session" key="Admin:User:Session:{0}" validTime="60" enabled="true"></item>
<!-- 无占位符key -->
<item name="Admin_User_List" key="Admin:User:List" validTime="30" enabled="true"></item>
<!-- 多个占位符key -->
<item name="Admin_User_Search" key="Admin:User:Search:{0}:{1}:{2}" validTime="5" enabled="true"></item>
</list>
</configuration>

 2、KeyEntity.cs

这个比较简单,就是把xml的内容读取出来序列化为对象,只是为了便于检索,name和key都小写化了

    /// <summary>
/// Key配置对象(公开)
/// Author:taiyonghai
/// CreateTime:2017-08-28
/// </summary>
public sealed class KeyEntity
{
private string name;
/// <summary>
/// Cache Name(Use for search cache key)
/// </summary>
public string Name
{
get { return name; }
set { name = value.Trim().ToLower(); }
} private string key;
/// <summary>
/// Cache Key
/// </summary>
public string Key
{
get { return key; }
set { key = value.Trim().ToLower(); }
} /// <summary>
/// Valid Time (Unit:minute)
/// </summary>
public int ValidTime { get; set; } /// <summary>
/// Enaled
/// </summary>
public bool Enabled { get; set; }
}

3、 KeyManager.cs

负责访问Key配置的XML文件,并将其缓存到静态Hashtable中,使用时直接从中检索到要用的信息,设置监听程序FileSystemWatcher如果文件发生变动则重置Hashtable使其重新读取,配置文件及名称可以自行变更或配置

还要提供根据KeyName获取Key配置对象的方法,这样就可以使用Key存到实际的缓存中,如果Key需要进行构造还可以传送Key的标识数组,从此方法中自动整合返回

    /// <summary>
/// 缓存Key管理
/// Author:taiyonghai
/// CreateTime:2017-08-28
/// </summary>
public static class KeyManager
{
//KeyName集合
private static Hashtable keyNameList;
//锁对象
private static object objLock = new object();
//监控文件对象
private static FileSystemWatcher watcher;
//缓存Key配置文件路径
private static readonly string configFilePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "Configs\\Cache\\";
//缓存Key配置文件名
private static readonly string configFileName = "KeyConfigList.xml"; /// <summary>
/// 静态构造只执行一次
/// </summary>
static KeyManager()
{
//创建对配置文件夹的监听,如果遇到文件更改则清空KeyNameList,重新读取
watcher = new FileSystemWatcher();
watcher.Path = configFilePath;//监听路径
watcher.Filter = configFileName;//监听文件名
watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.Size;//仅监听文件创建时间、文件变更时间、文件大小
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;//最后开启监听
} /// <summary>
/// 读取KeyName文件
/// </summary>
private static void ReaderKeyFile()
{
if (keyNameList == null || keyNameList.Count == )
{
//锁定读取xml操作
lock (objLock)
{
//获取配置文件
string configFile = String.Concat(configFilePath, configFileName);
//检查文件
if (!File.Exists(configFile))
{
throw new FileNotFoundException(String.Concat("file not exists:", configFile));
} //读取xml文件
XmlReaderSettings xmlSetting = new XmlReaderSettings();
xmlSetting.IgnoreComments = true;//忽略注释
XmlReader xmlReader = XmlReader.Create(configFile, xmlSetting);
//一次读完整个文档
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlReader);
xmlReader.Close();//关闭读取对象 //获取指定节点下的所有子节点
XmlNodeList nodeList = xmlDoc.SelectSingleNode("//configuration//list").ChildNodes; //获得一个线程安全的Hashtable对象
keyNameList = Hashtable.Synchronized(new Hashtable()); //将xml中的属性赋值给Hashtable
foreach (XmlNode node in nodeList)
{
XmlElement element = (XmlElement)node;//转为元素获取属性 KeyEntity entity = new KeyEntity();
entity.Name = element.GetAttribute("name");
entity.Key = element.GetAttribute("key");
entity.ValidTime = Convert.ToInt32(element.GetAttribute("validTime"));
entity.Enabled = Convert.ToBoolean(element.GetAttribute("enabled")); keyNameList.Add(entity.Name, entity);
}
}
}
} /// <summary>
/// 变更事件会触发两次是正常情况,是系统保存文件机制导致
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private static void OnChanged(object source, FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Changed)
{
if (e.Name.ToLower() == configFileName.ToLower())
{
keyNameList = null; //因为此事件会被调用两次,所以里面的代码要有幕等性,如果无法实现幕等性,
//则应该在Init()中绑定事件
//watcher.Changed += new FileSystemEventHandler(OnChanged);
//在OnChanged()事件中解绑事件
//watcher.Changed -= new FileSystemEventHandler(OnChanged);
}
}
} /// <summary>
/// 根据KeyName获取Key配置对象
/// </summary>
/// <param name="name">Key名称</param>
/// <returns></returns>
public static KeyEntity Get(KeyNames name)
{
return Get(name, null);
}
/// <summary>
/// 根据KeyName获取Key配置对象
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns></returns>
public static KeyEntity Get(KeyNames name, params string[] identities)
{
//检查Hash中是否有值
if (keyNameList == null || keyNameList.Count == )
KeyManager.ReaderKeyFile();
//检查Hash中是否有此Key
string tmpName = name.ToString().ToLower();
if (!keyNameList.ContainsKey(tmpName))
throw new ArgumentException("keyNameList中不存在此KeyName", "name");
var entity = keyNameList[tmpName] as KeyEntity;
//检查Key是否需要含有占位符
if (entity.Key.IndexOf('{') > )
{
//检查参数数组是否有值
if (identities != null && identities.Length > )
entity.Key = String.Format(entity.Key, identities);
else
throw new ArgumentException("需要此参数identities标识字段,但并未传递", "identities");
}
return entity;
} }

4、KeyNames.cs

用枚举类型是为了控制传递的KeyName能够被限制,不会随便传个string过来导致出错,实际还是使用了KeyNames.Admin_User_Session.ToString()来识别的,此处是根据枚举名查找KeyConfigList.xml中的name属性

    /// <summary>
/// KeyName枚举(公开)
/// Author:taiyonghai
/// CreateTime:2017-08-28
/// </summary>
public enum KeyNames
{
/// <summary>
/// 后台用户会话key
/// </summary>
Admin_User_Session, Admin_User_List, Admin_User_Search }

5、RedisManager.cs

这里可以是Redis也可以是Memcached主要就是提供缓存技术的管理,热门的dll有ServiceStack.Redis和StackExchange.Redis,可前者已经收费(免费使用有使用限额),无限额免费只能用4.0之前的版本,所以采用了后者

IConnectionMultiplexer是核心对象,此处使用单例模式创建连接对象,因为创建连接的资源消耗较高,后面有测试结果可以证明

在静态构造中绑定了几个异常事件,如果发生了错误可以写日志便于我们调试使用,GetDatabase()方法很轻量可以放心直接调用,配置文件可以采用其他方式

    /// <summary>
/// Redis缓存管理类
/// Author:taiyonghai
/// CreateTime:2017-08-28
/// </summary>
public static class RedisManager
{
//Redis连接对象
private static IConnectionMultiplexer redisMultiplexer;
//程序锁
private static object objLock = new object();
//Redis连接串(多个服务器用逗号隔开)"10.11.12.237:6379, password='',keepalive=300,connecttimeout=5000,synctimeout=1000"
private static readonly string connectStr = "10.11.12.237:6379"; /// <summary>
/// 静态构造用于注册监听事件
/// </summary>
static RedisManager()
{
//注册事件
GetMultiplexer().ConnectionFailed += ConnectionFailed;
GetMultiplexer().InternalError += InternalError;
GetMultiplexer().ErrorMessage += ErrorMessage;
}
/// <summary>
/// 连接失败
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void ConnectionFailed(object sender, ConnectionFailedEventArgs e)
{
//e.Exception
}
/// <summary>
/// 内部错误
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void InternalError(object sender, InternalErrorEventArgs e)
{
//e.Exception
}
/// <summary>
/// 发生错误
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void ErrorMessage(object sender, RedisErrorEventArgs e)
{
//e.Message
}
/// <summary>
/// 获得连接对象
/// </summary>
/// <returns></returns>
private static IConnectionMultiplexer GetMultiplexer()
{
if (redisMultiplexer == null || !redisMultiplexer.IsConnected)
{
lock (objLock)
{
//创建Redis连接对象
redisMultiplexer = ConnectionMultiplexer.Connect(connectStr);
}
}
return redisMultiplexer;
}
/// <summary>
/// 获得客户端对象
/// </summary>
/// <param name="db">选填指明使用那个数据库0-16</param>
/// <returns></returns>
public static IDatabase GetClient(int db = -)
{
return GetMultiplexer().GetDatabase(db);
} }

如果每次都ConnectionMultiplexer.Connect()一个连接对象的测试结果如下:

采用单例模式处理连接对象的测试结果如下:

6、CacheProvider.cs

对项目中提供的缓存操作类,提供多个方法,我只提供了String类型和Hash类型,Set集合类型我用不到就没有提供,需要的朋友可以自己添加

    /// <summary>
/// 缓存提供类
/// Author:taiyonghai
/// CreateTime:2017-08-28
/// </summary>
public class CacheProvider
{
#region Cache 删除 /// <summary>
/// 删除缓存
/// </summary>
/// <param name="name">Key名称</param>
/// <returns>True成功,Flase失败</returns>
public bool DelKey(KeyNames name)
{
return DelKey(name, null);
}
/// <summary>
/// 删除缓存[核心]
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>True成功,Flase失败</returns>
public bool DelKey(KeyNames name, params string[] identities)
{
var entity = KeyManager.Get(name, identities);
return RedisManager.GetClient().KeyDelete(entity.Key);
} #endregion #region Cache String 存取 #region 添加缓存 /// <summary>
/// 添加缓存,有过期时间(如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="value">不可为NULL,应对缓存穿透请用空</param>
/// <returns>True成功,Flase失败</returns>
public bool SetString<T>(KeyNames name, T value) where T : class, new()
{
return SetString<T>(name, value, null);
}
/// <summary>
/// 添加缓存,有过期时间(如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="value">不可为NULL,应对缓存穿透请用空</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>True成功,Flase失败</returns>
public bool SetString<T>(KeyNames name, T value, params string[] identities) where T : class, new()
{
//如果value为null直接缓存无需序列化
string tmpStr = null == value ? null : JsonSerializer.SerializeToString<T>(value);
return SetString(name, tmpStr, identities);
}
/// <summary>
/// 添加缓存,有过期时间(如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="value">不可为NULL,应对缓存穿透请用空</param>
/// <returns>True成功,Flase失败</returns>
public bool SetString(KeyNames name, string value)
{
return SetString(name, value, null);
}
/// <summary>
/// 添加缓存,有过期时间[核心](如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="value">不可为NULL,应对缓存穿透请用空</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>True成功,Flase失败</returns>
public bool SetString(KeyNames name, string value, params string[] identities)
{
//不可传值为NULL,应对缓存穿透请用空,NULL用来判断是否缓存有值
if (null == value)
return false;
var entity = KeyManager.Get(name, identities);
//有效时间的TimeSpan=(最小时间+有效时间)-最小时间
TimeSpan timeSpan = DateTime.MinValue.AddMinutes(entity.ValidTime) - DateTime.MinValue;
return RedisManager.GetClient().StringSet(entity.Key, value, timeSpan);
} #endregion #region 获取缓存 /// <summary>
/// 获取缓存
/// </summary>
/// <param name="name">Key名称</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public T GetString<T>(KeyNames name) where T : class, new()
{
return GetString<T>(name, null);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public T GetString<T>(KeyNames name, params string[] identities) where T : class, new()
{
string tmpStr = GetString(name, identities);
//如果tmpStr为null直接返回无需反序列化
return null == tmpStr ? default(T) : JsonSerializer.DeserializeFromString<T>(tmpStr);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="name">Key名称</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public string GetString(KeyNames name)
{
return GetString(name, null);
}
/// <summary>
/// 获取缓存[核心]
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public string GetString(KeyNames name, params string[] identities)
{
var entity = KeyManager.Get(name, identities);
//检查缓存是否启用,否则返回NULL
if (entity.Enabled)
{
return RedisManager.GetClient().StringGet(entity.Key);
}
else
{
return null;
}
} #endregion #endregion #region Cache Hash 存取 #region 添加缓存 /// <summary>
/// 添加缓存,无过期时间(如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="dict">不可为NULL,应对缓存穿透请用空对象</param>
public bool SetHash<T>(KeyNames name, Dictionary<string, T> dict) where T : class, new()
{
return SetHash<T>(name, dict, null);
}
/// <summary>
/// 添加缓存,无过期时间(如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="dict">不可为NULL,应对缓存穿透请用空对象</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
public bool SetHash<T>(KeyNames name, Dictionary<string, T> dict, params string[] identities) where T : class, new()
{
var tmpDict = new Dictionary<string, string>();
foreach (var item in dict)
tmpDict.Add(item.Key, null == item.Value ? null : JsonSerializer.SerializeToString<T>(item.Value));
return SetHash(name, tmpDict, identities);
}
/// <summary>
/// 添加缓存,无过期时间(如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="dict">不可为NULL,应对缓存穿透请用空对象</param>
public bool SetHash(KeyNames name, Dictionary<string, string> dict)
{
return SetHash(name, dict, null);
}
/// <summary>
/// 添加缓存,无过期时间[核心](如Key存在则更新值)
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="dict">不可为NULL,应对缓存穿透请用空对象</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
public bool SetHash(KeyNames name, Dictionary<string, string> dict, params string[] identities)
{
//不可传值为NULL,应对缓存穿透请用空对象,NULL用来判断是否缓存有值
if (null == dict)
return false;
var entity = KeyManager.Get(name, identities);
var hashEntryList = new List<HashEntry>();
foreach (var item in dict)
hashEntryList.Add(new HashEntry(item.Key, item.Value));
RedisManager.GetClient().HashSet(entity.Key, hashEntryList.ToArray());
return true;
} #endregion #region 获取缓存 /// <summary>
/// 获取缓存
/// </summary>
/// <param name="name">Key名称</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public Dictionary<string, T> GetHash<T>(KeyNames name) where T : class, new()
{
return GetHash<T>(name, null);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public Dictionary<string, T> GetHash<T>(KeyNames name, params string[] identities) where T : class, new()
{
var dict = GetHash(name, identities);
var tmpDict = new Dictionary<string, T>();
foreach (var item in dict)
tmpDict.Add(item.Key, null == item.Value ? default(T) : JsonSerializer.DeserializeFromString<T>(item.Value));
return tmpDict;
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="name">Key名称</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public Dictionary<string, string> GetHash(KeyNames name)
{
return GetHash(name, null);
}
/// <summary>
/// 获取缓存[核心]
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>无缓存或禁用缓存均返回NULL</returns>
public Dictionary<string, string> GetHash(KeyNames name, params string[] identities)
{
var entity = KeyManager.Get(name, identities);
//检查缓存是否启用,否则返回NULL
if (entity.Enabled)
{
var hashEntry = RedisManager.GetClient().HashGetAll(entity.Key);
var dict = new Dictionary<string, string>();
foreach (var item in hashEntry)
dict.Add(item.Name, item.Value);
return dict;
}
else
{
return null;
}
} #endregion #endregion }

7、MQProvider.cs

对项目中提供的消息队列操作类,我偷懒应用了Redis的List类型来提供消息队列的操作,少数据量的情况下比如msg在10k以下性能很好,大数据量时性能下降严重,有兴趣可以百度一下看看测试,但他没有事务级的能力所以小规模使用可以,需求高还是需要更专业的队列比如RabbitMQ等

    /// <summary>
/// 队列提供类
/// Author:taiyonghai
/// CreateTime:2017-08-31
/// </summary>
public class MQProvider
{
#region MQ 添加 /// <summary>
/// 添加一条消息到队列
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="msg">内容</param>
/// <returns></returns>
public long SetMsg<T>(KeyNames name, T msg) where T : class, new()
{
return SetMsg<T>(name, msg, null);
}
/// <summary>
/// 添加一条消息到队列
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="msg">内容</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns></returns>
public long SetMsg<T>(KeyNames name, T msg, params string[] identities) where T : class, new()
{
//如果value为null直接缓存无需序列化
string tmpMsg = null == msg ? null : JsonSerializer.SerializeToString<T>(msg);
return SetMsg(name, tmpMsg, identities);
}
/// <summary>
/// 添加一条消息到队列
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="msg">内容</param>
/// <returns></returns>
public long SetMsg(KeyNames name, string msg)
{
return SetMsg(name, msg, null);
}
/// <summary>
/// 添加一条消息到队列[核心]
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="msg">内容</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>添加消息后的队列长度</returns>
public long SetMsg(KeyNames name, string msg, params string[] identities)
{
var entity = KeyManager.Get(name, identities);
//向队列右侧插入新的消息
return RedisManager.GetClient().ListRightPush(entity.Key, msg);
} #endregion #region MQ 获取 /// <summary>
/// 从队列中获取一条消息,并将其在队列中移除
/// </summary>
/// <param name="name">Key名称</param>
/// <returns>没有消息返回NULL</returns>
public T GetMsg<T>(KeyNames name) where T : class, new()
{
return GetMsg<T>(name, null);
}
/// <summary>
/// 从队列中获取一条消息,并将其在队列中移除
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>没有消息返回NULL</returns>
public T GetMsg<T>(KeyNames name, params string[] identities) where T : class, new()
{
string tmpStr = GetMsg(name, identities);
return null == tmpStr ? default(T) : JsonSerializer.DeserializeFromString<T>(tmpStr);
}
/// <summary>
/// 从队列中获取一条消息,并将其在队列中移除
/// </summary>
/// <param name="name">Key名称</param>
/// <returns>没有消息返回NULL</returns>
public string GetMsg(KeyNames name)
{
return GetMsg(name, null);
}
/// <summary>
/// 从队列中获取一条消息,并将其在队列中移除[核心]
/// </summary>
/// <param name="name">Key名称</param>
/// <param name="identities">Key标识(用于替换Key中的{0}占位符)</param>
/// <returns>没有消息返回NULL</returns>
public string GetMsg(KeyNames name, params string[] identities)
{
var entity = KeyManager.Get(name, identities);
//从队列左侧队列头部取出消息
return RedisManager.GetClient().ListLeftPop(entity.Key);
} #endregion
}

三、项目调用代码

Redis如果遇到同样Key且同类型(String、Hash、List)时是直接覆盖值,如果不同类型的话就会报错了,我偷懒使用了同一个KeyNames就使用加前缀的方式来区分同类型不重复

CacheProvider cache = new CacheProvider();
MQProvider mq = new MQProvider();
//基础类型
cache.SetString(KeyNames.Cache_Admin_User_Session, "taiyonghai", "");
var str = cache.GetString(KeyNames.Cache_Admin_User_Session);
//Hash类型
var dict = new Dictionary<string, string>();
dict.Add("", "待处理");
dict.Add("", "处理中");
dict.Add("", "处理完成");
cache.SetHash(KeyNames.Cache_Hash_Admin_User_List, dict);
var tmpDict = cache.GetHash(KeyNames.Cache_Hash_Admin_User_List);
//List队列
mq.SetMsg(KeyNames.Msg_Admin_User_Search, "Hello");
mq.GetMsg(KeyNames.Msg_Admin_User_Search);

附录:配置参数解析

Redis缓存项目应用架构设计一的更多相关文章

  1. Redis缓存项目应用架构设计二

    一.概述 由于架构设计一里面如果多平台公用相同Key的缓存更改配置后需要多平台上传最新的缓存配置文件来更新,比较麻烦,更新了架构设计二实现了缓存配置的集中管理,不过这样有有了过于中心化的问题,后续在看 ...

  2. 《【面试突击】— Redis篇》--Redis Cluster及缓存使用和架构设计的常见问题

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>--Redis Cluster及缓存使用和架构设计的 ...

  3. python-django电商项目-需求分析架构设计数据库设计_20191115

    python-django电商项目需求分析 1.用户模块 1)注册页 注册时校验用户名是否已被注册. 完成用户信息的注册. 给用户的注册邮箱发送邮件,用户点击邮件中的激活链接完成用户账户的激活. 2) ...

  4. WCF项目的架构设计

    本文将介绍以WCF开发项目为主的架构设计,主要从类库的分类和代码的结构. 下面将以一个WCF实例做具体的介绍.此项目底层是一个Windows Service,WCF服务Hosted在此Windows ...

  5. 高可用Redis(十三):Redis缓存的使用和设计

    1.缓存的受益和成本 1.1 受益 1.可以加速读写:Redis是基于内存的数据源,通过缓存加速数据读取速度 2.降低后端负载:后端服务器通过前端缓存降低负载,业务端使用Redis降低后端数据源的负载 ...

  6. vue项目的架构设计完善详解

    vue项目构建vuex+mock层 vue项目添加jsBridge(与原生交互的) vue项目添加代码格式化

  7. MVC实用架构设计(三)——EF-Code First(5):二级缓存

    前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说 ...

  8. MVC实用架构设计(三)——EF-Code First(4):数据查询

    前言 首先对大家表示抱歉,这个系列已经将近一个月没有更新了,相信大家等本篇更新都等得快失望了.实在没办法,由于本人水平有限,写篇博客基本上要大半天的时间,最近实在是抽不出这么长段的空闲时间来写.另外也 ...

  9. MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

    前言 经过前面EF的<第一篇>与<第二篇>,我们的数据层功能已经较为完善了,但有不少代码相似度较高,比如负责实体映射的 EntityConfiguration,负责仓储操作的I ...

随机推荐

  1. 初遇.net

    初遇.net 为了自己的理想我选择了.net课程进行自我提升,想想以后能成为一位程序猿不由得有点兴奋呢,还有另一件高兴的事是我认识了十几位来自不同区县的老师同学,都说人脉就是财富,是不是我的财富有多了 ...

  2. 汇编指令-CMP、TEQ(5)

     cmp:(compare)指令进行比较两个操作数的大小  格式: cmp oprd1,oprd2 比较oprd1和oprd2操作数,然后通过助记符来实现想要的判断. teq: (test equal ...

  3. HTTPS静态服务搭建过程详解

    HTTPS服务对于一个前端开发者来说是一个天天打招呼的老伙计了,但是之前我跟HTTPS打交道的场景一直是抓包,自己没有亲自搭建过HTTPS服务,对HTTPS的底层知识也是一知半解.最近正好遇到一个用户 ...

  4. Linux下设置Tomcat虚拟路径

    问题描述:我在上传图片的位置不在Tomcat服务器下,用户无法访问 解决方案:配置Tomcat虚拟路径使用户可以访问图片 配置Tomcat # cd /usr/local/apache-tomcat- ...

  5. 结对编程1--基于GUI的四则运算题目生成器

    最终项目详细代码(coding地址)/李志强的博客/我的详细代码/方言的博客 一.需求分析 1.实现之前项目的功能,如:除了整数之外,还要支持真分数的四则运算,真分数的运算,并且要求能处理用户的输入, ...

  6. SNS团队第六次站立会议(2017.04.27)

    一.当天站立式会议照片 本次会议主要内容:汇报工作进度,根据完成情况调整进度 二.每个人的工作 成员 今天已完成的工作 明天计划完成的工作 罗于婕 导入相关词库数据  研究如何存取语音.图片文件 龚晓 ...

  7. C语言数据在内存分配

    一个由C/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.程序结束时由编译器自动释放 ...

  8. 201521123097 《JAVA程序设计》第七周学习总结

    1. 本周学习总结 总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 源代码: public boolean contains(Object ...

  9. 201521123009 《Java程序设计》第6周学习总结

    1. 本周学习总结 2. 书面作业 Q1:clone方法 1.1 Object对象中的clone方法是被protected修饰,在自定义的类中覆盖clone方法时需要注意什么? 用protected修 ...

  10. 201521123089《Java程序设计》第6周学习总结

    1. 本周学习总结 2. 书面作业 clone方法1.1 Object对象中的clone方法是被protected修饰,在自定义的类中覆盖clone方法时需要注意什么?                 ...