建设企业网站模板下载,安岳网站建设,不能上传图片到网站,重点实验室网站建设方案网站缓存这个话题并不新颖#xff0c;但是能否将它用好#xff0c;可是一门学问#xff0c;同一件工具在不同人的手中会做出不同的事情来。这里我来分享总结下我对于网站架构中缓存应用的一些看法和经验#xff0c;大家有好的想法可以补充 第一#xff1a;缓存的一些基…网站缓存这个话题并不新颖但是能否将它用好可是一门学问同一件工具在不同人的手中会做出不同的事情来。这里我来分享总结下我对于网站架构中缓存应用的一些看法和经验大家有好的想法可以补充 第一缓存的一些基本概念。 1缓存(CACHE)与缓冲(BUFFER)的区别我认为缓存可以在某种程序上理解成一级缓存(Primary Cache),数据全局共享。缓冲则属于二级缓存只对一部分对象共享数据,二级缓存在某种程序上主要是降低一级缓存组件的访问压力以及提高缓存的存取效率。 2缓存的一些基本属性命中率表示缓存命中的次数/总的请求数这是缓存设计的重要质量指标之一缓存执行效率例如GET,INSERT,DELETE等容量即缓存介质的容量最大值成本即开发成本部署成本软硬件成本。 3缓存的问题存储介质的选择往往左右缓存的设计缓存在不命中时往往会使性能下降。 第二网站中缓存的应用场景 1可以缓存整个页面的html提高访问响应能力 2针对局部页面元素进行缓存 3对复杂数据的结果进行缓存,例如一个查询需要结合多个数据集然后根据这些数据集进行相应的运算即使每个子集查询有缓存但还是需要额外的运算这种情况可以考虑缓存计算后的结果。 4对耗时的查询进行缓存,例如产品列表页的查询。 5和上下文相关的用户数据例如用户从订单埴写页进入到订单成功页或者是从产品列表页点击详细产品进行预订时的订单填写页此时这两个页面之间都需要传递大量的相关数值我们可以把所有的数值封装在一个类中然后通过缓存进行通信。 第三影响缓存命中率的因素。 1数据时实性每个业务系统都对自己的数据有相应的要求有些数据的实时性非常强像每日的股票信息这种情况如果设置了缓存缓存的命中率会特别低。 2缓存粒度问题一般来说是缓存的跨度太大即此时的KEY值包含的条件太多会出现缓存命中率特别低的情况。 第四提高缓存命中率的方法 1增大存储介质的容量 2对非常热点的数据进行捕捉可以采用实时更新缓存的方式来平衡缓存与实时性的问题,例如可以单独开启一个后台服务来定时做更新缓存的工作。 3调整缓存KEY值的算法尽量保证缓存KEY的细粒度KEY-VALUE就是很好的细粒度例子。 4根据业务调整缓存的过期策略。 第五如何实现缓存组件 1采用二级缓存架构即在web server上设置二级缓存这里的二级缓存即上面的提到的缓冲只对具体的webserver进行数据共享,二级缓存可以考虑采用微软企业库的缓存组件来完成。由于一级缓存的实现往往都是单独的服务器为了减少缓存服务器的压力在webserver上对数据进行缓冲在降低缓存服务器压力的情况下最大的好处在于缓存的存取速度上。 2一级缓存由单独的缓存服务器来完成至于缓存服务器采用哪种缓存方案可以根据不同的场景来决定。如果考虑到部署的方便性可以采用微软企业库来完成一级缓存如果服务器允许可以采用memcached来实现。 cache服务器采用微软企业库实现的优缺点 1优点开发以及部署都非常容易不需要安装第三方软件等等 2缺点需要自己开发客户端功能以实现分布式这里我们可以采用一致性hash算法来实现同时如果服务以WCF形式公布开在访问效率上也不是最优的比起memcached的通信方式要差一些。 memcached的优缺点 1优点通信方式比起wcf要高 2缺点需要第三方服务的支持需要在服务器上安装memcached服务这好像也不是什么重要的缺点。 最后贴出网站网页在数据访问上的流程图,供大家参考在下面的文章中我会把实现的方案分享出来。 上一篇我主要总结了网站缓存中的一些基本概念以及我对于网站架构缓存应用的架构实现思路这篇主要分享下如何利用微软企业库来实现一二级缓存的缓存服务。 为了能够有效的管理缓存需要对使用缓存方法上做一些规范即要想使用缓存组件提供的服务需要在指定的配置文件中按照一定的规则来配置缓存条目不允许在配置之处使用缓存。下面先展示下一条Cache条目的配置 Region nameMyBlog SubRegion namedefault Cache CacheModeLocalCacheOnlyMode KeyBlogListConfigKey BufferTypeAbsoluteTime BufferTimeSeconds300 CacheTypeAbsoluteTime CacheTimeMinutes30 CachePriorityNormal/ /SubRegion /Region 上面的代码中其实由三部分构成 1主分区:Regin,如果一个网站分很多子系统可以为每个子系统定义一个这样的主分区,例如食品频道Food,手机频道Mobile等 2子分区SubRegion,主分区下面的子分区即对子系统更加小的划分可以根据子系统的功能来划分,例如产品列表页List,详细页Detail等 3缓存条目Cache,指具体的一则缓存条目规则这里的缓存条目规则并不是指某一条缓存设置而是指一条缓存规则与具体的缓存条目是一对多的关系。 1:CacheMode,设置缓存模式是只有二级缓存还是即有一级缓存也有二级缓存,例如用户页面之间的信息沟通就只需要二级缓存即缓存在web server上。而产品列表页的数据属于全局数据就需要即采用二级缓存也需要一级缓存。 2:BufferType指二级缓存的过期方式分为绝对过期滑动过期文件依赖。 3:BufferTimeSeconds二级缓存Timespan中的秒。 4:CacheType,一级缓存的过期方式类型同BufferType. 5:CacheTimeMinutes,一级缓存Timespan中的分钟。 6:CachePriority,缓存的优先级。 二级缓存实现 第一IWebCacheProvider缓存提供者接口它公布了所有缓存组件需要的方法,接口之所以加上了ServeiceContract标签是由于下面的一级缓存WCF服务也继承此接口的原因。小提示WCF服务契约对于方法重载的实现和普通方式有小小区别请注意OperationContract标签的定义。 [ServiceContract] public interface IWebCacheProvider { [OperationContract(Name Add)] void Insert(string key, object value, string region, string subRegion); [OperationContract(Name AddByAbsoluteTime)] void Insert(string key, object value, string region, string subRegion, MyCacheItemPriority scavengingPriority, AbsoluteTimeCacheDependency absoluteTimeCacheDependency); [OperationContract(Name AddBySlidingTime)] void Insert(string key, object value, string region, string subRegion, MyCacheItemPriority scavengingPriority, SlidingTimeCacheDependency slidingTimeCacheDependency); [OperationContract(Name AddByFile)] void Insert(string key, object value, string region, string subRegion, MyCacheItemPriority scavengingPriority, FileCacheDependency fileCacheDependency); [OperationContract] void Delete(string key, string region, string subRegion); [OperationContract] object Get(string key, string region, string subRegion); [OperationContract] void Clear(string region); [OperationContract] int Count(string region); } 第二EntLibWebCacheProvider微软企业库实现缓存实现类。代码并不贴了基本就是利用企业库的缓存组件实现上面的接口。 第三MyWebCacheServiceClient提供缓存客户端的实例。包含了两个重要的属性一级缓存实例二级缓存实例。余下的就是调用EntLibWebCacheProvider来完成缓存的调用以及根据缓存规则选择操作一级缓存以及二级缓存。 说明下面代码中的GetMemcachedWebCacheProvider是下篇文章会提到的利用memcached实现一级缓存由于需要支持一级缓存在企业库以及memcached之间的切换才出现的逻辑。 //一级缓存 IWebCacheProvider PrimaryCacheProvider; //二级缓存 IWebCacheProvider SecondaryCacheProvider; /// summary /// 实例化二级缓存 /// /summary /// param nameconfigFilePath/param /// returns/returns private IWebCacheProvider GetSecondaryCacheProvider() { IWebCacheProvider provider null; provider WebCacheProviderFactory.GetEntLibWebCacheProvider(configFilePath); return provider; } /// summary /// 获取一级缓存 /// /summary /// param namehashKey/param /// param nameconfigFilePath/param /// returns/returns private IWebCacheProvider GetPrimaryCacheProvider(uint hashKey) { IWebCacheProvider provider null; string cacheType WebConfig.ChannelConfig[CacheType].ToString().ToLower(); switch (cacheType) { case memcached: provider WebCacheProviderFactory.GetMemcachedWebCacheProvider(configFilePath); break; case entlib: provider servicePool.GetServiceClient(hashKey) as IWebCacheProvider; break; } return provider; } 一级缓存的实现由于一级缓存也采用微软企业库实现而企业库本身是不具备分布式功能的就算是memcached本身也不具备分布式而在于客户端的实现所以企业库我们也可以实现分布式。 首先我们把实现了缓存的组件以WCF服务形式分布出来在多个服务器上部署形成一个服务器群 其实实现分布式的客户端让服务的请求均匀的分布到之前部署的WCF缓存服务器上。这里采用一致性hash算法实现主要是根据key的hash值以及服务器数量来选择存储的服务器,这里贴些主要的实现代码供参考 1:定义一个hash的服务器实例集合为每个服务器分配250个hash值,这里的值可以根据实际情况调整。 private Dictionaryuint, isRoc.Common.Cache.CacheProvider.IWebCacheProvider hostDictionary; 2:定义一个hash的服务实例key集合用来统计所有服务器实例中的hash key。 private uint[] hostKeysArray; 3:创建服务器列表的hash值。 /// summary /// 重新设置服务器列表 /// /summary /// param namehosts服务器列表/param internal void Setup(ListWebCacheServerInfo hosts) { hostDictionary new Dictionaryuint, isRoc.Common.Cache.CacheProvider.IWebCacheProvider(); ListisRoc.Common.Cache.CacheProvider.IWebCacheProvider clientList new ListisRoc.Common.Cache.CacheProvider.IWebCacheProvider(); Listuint hostKeysList new Listuint(); foreach (WebCacheServerInfo host in hosts) { //创建客户端 isRoc.Common.Cache.CacheProvider.IWebCacheProvider client ServiceProxyFactory.CreateisRoc.Common.Cache.CacheProvider.IWebCacheProvider(host .HostUri ); //Create 250 keys for this pool, store each key in the hostDictionary, as well as in the list of keys. for (int i 0; i 250; i) { uint key 0; switch (this.HashAlgorithm) { case EHashAlgorithm.KetamaHash: key (uint)Math.Abs(KetamaHash.Generate(host.HostUri - i)); break; case EHashAlgorithm.FnvHash32: key BitConverter.ToUInt32(new ModifiedFNV1_32().ComputeHash(Encoding.UTF8.GetBytes(host.HostUri - i)), 0); break; } if (!hostDictionary.ContainsKey(key)) { hostDictionary.Add(key, client); hostKeysList.Add(key); } } clientList.Add(client); } //Hostlist should contain the list of all pools that has been created. clientArray clientList.ToArray(); //Hostkeys should contain the list of all key for all pools that have been created. //This array forms the server key continuum that we use to lookup which server a //given item key hash should be assigned to. hostKeysList.Sort(); hostKeysArray hostKeysList.ToArray(); } 4如何根据key值来查找具体的缓存服务实例呢即具体的key存储在哪一台服务器上呢根据传入的key值计算出对应的hash值然后经过特定的算法计算出服务器实例地址。 internal isRoc.Common.Cache.CacheProvider.IWebCacheProvider GetServiceClient(uint hash) { //Quick return if we only have one host. if (clientArray.Length 1) { return clientArray[0]; } //New ketama host selection. int i Array.BinarySearch(hostKeysArray, hash); //If not exact match... if (i 0) { //Get the index of the first item bigger than the one searched for. i ~i; //If i is bigger than the last index, it was bigger than the last item use the first item. if (i hostKeysArray.Length) { i 0; } } return hostDictionary[hostKeysArray[i]]; } 总结本文简单的介绍了如何利用微软企业库来实现具有两级缓存的缓存组件上篇我提到过实现一级缓存也可以采用memcached采用memcached可以不用自己开发分布式客户端目前有两个成熟的解决方案1:Memcached.ClientLibrary2:EnyimMemcached。下篇我来介绍一级缓存如何通过memcached实现以及如何让组件在一级缓存上即支持企业库也支持memcached。 这篇来讲如何利用memcached实现一级缓存以及如何让一级缓存组件支持在企业库memcached或者其它第三方实施方案之间的切换。memcached本人并没有太多经验如果文中有说的不对的地方还希望批评指出且文中关于memcached的代码大多来自网络。 创建memcached实现类MemcachedWebCacheProvider,由它来继承缓存提供者接口IWebCacheProvider,主里memcached客户端我采用.NET memcached client library ,这个类库很久没有更新这过了没有和java版同步有部分功能目前没有实现。 1初始化memcached服务,这段初始化代码在程序中保证执行一次就够一般可以放在gloabl文件中或者是设置一个静态变量来存储服务的状态。 private void Setup() { String[] serverlist { 127.0.0.1:11211 }; this._pool SockIOPool.GetInstance(default); this._pool.SetServers(serverlist); //设置服务器列 //各服务器之间负载均衡的设置 this._pool.SetWeights(new int[] { 1 }); //socket pool设置 this._pool.InitConnections 5; //初始化时创建的连接数 this._pool.MinConnections 5; //最小连接数 this._pool.MaxConnections 250; //最大连接数 //连接的最大空闲时间下面设置为6个小时单位ms超过这个设置时间连接会被释放掉 this._pool.MaxIdle 1000 * 60 * 60 * 6; //通讯的超时时间下面设置为3秒单位ms.NET版本没有实现 this._pool.SocketTimeout 1000 * 3; //socket连接的超时时间下面设置表示连接不超时即一直保持连接状态 this._pool.SocketConnectTimeout 0; this._pool.Nagle false; //是否对TCP/IP通讯使用Nalgle算法.NET版本没有实现 //维护线程的间隔激活时间下面设置为60秒单位s设置为0表示不启用维护线程 this._pool.MaintenanceSleep 60; //socket单次任务的最大时间超过这个时间socket会被强行中断掉当前任务失败 this._pool.MaxBusy 1000 * 10; this._pool.Initialize(); } 2:获取一个memcached客户端。 private MemcachedClient GetClient() { MemcachedClient client new MemcachedClient(); client.PoolName default; return client; } 3根据memcached提供的功能实现IWebCacheProvider代码就不贴了大家可以自己去试试。 到此我们就利用memcached实现了一级缓存由于.NET memcached client library 实现了分布式我们只需要在多台服务器上安装上memcached服务在初始化memcached代码中增加了服务器相关配置即可。String[] serverlist { 127.0.0.1:11211 }; 如何让一级缓存组件支持多实现方案之间的切换。 MyWebCacheServiceClient:客户端缓存组件实例它来完成一级缓存与二级缓存之间的联系以及根据配置文件来选择一级缓存的实施方案。 第一CacheServiceMode根据它就可以决定缓存是只缓存二级缓存还是两级都缓存。 1:LocalCacheOnlyMode,只启用web server上的二级缓存。 2:BufferedLCacheServerMode,即启用web server上的二级缓存也启用cache server上的缓存。 3:Off,关闭缓存功能。 第二IWebCacheProvider service this .GetPrimaryCacheProvider(hashKey);方式决定了一级缓存的实施方案。 /// summary /// 获取一级缓存 /// /summary /// param namehashKey/param /// param nameconfigFilePath/param /// returns/returns private IWebCacheProvider GetPrimaryCacheProvider(uint hashKey) { IWebCacheProvider provider null; string cacheType WebConfig.ChannelConfig[CacheType].ToString().ToLower(); switch (cacheType) { case memcached: provider WebCacheProviderFactory.GetMemcachedWebCacheProvider(configFilePath); break; case entlib: provider servicePool.GetServiceClient(hashKey) as IWebCacheProvider; break; } return provider; } 插入缓存的逻辑原理就是根据配置文件中的CacheMode来完成缓存级别的判定以及一级缓存的方案。 public void Insert(string key, object value, string region, string subRegion, CacheItemConfig cacheItemConfig) { if (string.IsNullOrEmpty(key) || value null) return; //关闭模式不使用缓存 if (Options.CacheServiceMode ECacheServiceMode.Off) { return; } else if (Options.CacheServiceMode ECacheServiceMode.BufferedLCacheServerMode || Options.CacheServiceMode ECacheServiceMode.LocalAndCacheServerAndSql || Options.CacheServiceMode ECacheServiceMode.LocalCacheOnlyMode) {//使用带缓冲的模式 if (Options.BufferType ECacheDependencyType.SlidingTime) { SecondaryCacheProvider.Insert(key, value, region, subRegion, MyCacheItemPriority.Normal, Options.BufferSlidingTime); } else if (Options.BufferType ECacheDependencyType.AbsoluteTime) { SecondaryCacheProvider.Insert(key, value, region, subRegion, MyCacheItemPriority.Normal, Options.BufferAbsoluteTime); } if (Options.CacheServiceMode ECacheServiceMode.LocalCacheOnlyMode) {//只使用本地缓存 return; } } checkKey(key); uint hashKey hash(key); try { if (Options.CacheServiceMode ECacheServiceMode.CacheServerMode || Options.CacheServiceMode ECacheServiceMode.BufferedLCacheServerMode || Options.CacheServiceMode ECacheServiceMode.CacheServerAndSql || Options.CacheServiceMode ECacheServiceMode.LocalAndCacheServerAndSql) {//CacheServer模式使用Cache服务器保存Cache IWebCacheProvider service this .GetPrimaryCacheProvider(hashKey); byte[] byteValue SerializationHelper.SaveToBinaryBytes(value); var cachePriority ModelConverter.ToRefClass(cacheItemConfig.CachePriority); if (cacheItemConfig.CacheType ECacheDependencyType.AbsoluteTime) { AbsoluteTimeCacheDependency absTime new AbsoluteTimeCacheDependency(); absTime.AbsoluteTime DateTime.Now.AddMinutes(cacheItemConfig.CacheTimeMinutes); service.Insert(key, byteValue, region, subRegion, cachePriority, absTime); } else if (cacheItemConfig.CacheType ECacheDependencyType.SlidingTime) { SlidingTimeCacheDependency slTime new SlidingTimeCacheDependency(); slTime.SlidingTime new TimeSpan(0, cacheItemConfig.CacheTimeMinutes, 0); service.Insert(key, byteValue, region, subRegion, cachePriority, slTime); } } } catch (Exception ex) {//出现异常保存到数据库中 servicePool.ReplaceServiceClient(hashKey); this.SendLogEmail(ex); } } 客户端调用代码为了调用方便创建一个CacheHelper来帮助完成 public class CacheHelper { /// summary /// 主分区 /// /summary public const string REGION MyBlog; /// summary /// 子分区 /// /summary public const string SUB_REGION default; public const string BlogListConfigKey BlogListConfigKey; #region 页面间数据传递 /// summary /// 新增页面间传递数据到WebCache /// /summary /// returns返回PageKeyID用于页面间传递的键值/returns public static string InsertPageParams(string configKey, object obj,string pageKey) { string result null; MyWebCacheServiceClient cacheClient CacheClientFactory.GetWebCacheServiceClient(REGION, SUB_REGION, configKey); cacheClient.Insert( MyWebCacheServiceClient.BuildKey(configKey,pageKey), obj, REGION, SUB_REGION); return result; } /// summary /// 从Cache里获取页面传递Cache /// /summary /// param namekeyFlightCacheKey里的常量/param /// param namepageKeyID页面传递的键值/param public static object GetPageParams(string configKey, string pageKey) { object result null; MyWebCacheServiceClient cacheClient CacheClientFactory.GetWebCacheServiceClient(REGION, SUB_REGION, configKey); result cacheClient.Get( MyWebCacheServiceClient.BuildKey(configKey, pageKey), REGION, SUB_REGION); return result; } #endregion } 对于web系统中增加缓存服务使用起来还是挺方便的目前可采用的方案比较多有微软的企业库memcached等等。但如果需要很好的对项目中的缓存进行监控管理也不是一件特别容易的事情例如监控缓存服务器上都有哪些项目使用了缓存具体都有多少个key大小单个key的命中率以及过期时间等信息。有了这些信息就非常容易排查内存为什么快用完的问题如果再提供手动过期缓存的服务就更好了有的时候由于数据出错需要紧急让缓存失效此种办法影响最小。 这篇我来总结了针对memcached的缓存管理。 其实memcached本身也提供了一些缓存统计信息例如:当前总共的缓存数量使用的内存量,总获取次数,总的写入次数,总的命中次数等等但这种统计信息粒度太大: 1:无法具体到单个key如果我们想针对某一个key统计它的命中率情况就不好办了。 2:无法分析系统中都有哪些项目使用了key,哪个项目占用的key多,内存多。 3:无法实现手工过期这种需求某些特殊情况下也是很有帮助的。 既然memcached本身不提供我这里采用了一种变通的方式来记录我们特定的信息。 首先我们引进一个概念:分区,这个分区可以理解成电脑上的硬盘分区用户可以把不同的文件放在不同的分区上这样在管理上也容易些,同样分区底下有子分区就像电脑上的文件一样子分区下面就是具体的key了对于我们的cache后台管理可以这样理解一个项目可以分配为一个分区按项目功能模块可以分为不同的子分区子分区下来分散着N多key。 实现方案我们可以对每个key的访问记录下它的一些信息例如大小所属分区名过期时间访问命中率然后把这些信息在每个memcached 实例上创建一个特殊key,用于存储key的访问信息。 注意点 1:由于记录访问信息都需要更新特殊key,如果过于频繁会影响正常的cache性能所以可以考虑形成一个内存队列当数量达到多少后(如果key使用频率不高还可以设定时间,当过了这个时间即使数量不够也进行更新)统一更新特殊key内容。 2:由于memcached有单个key大小限制所以对于这种统计信息key,不能过大记录key访问信息时尽量以文本形式存储这样能保证最小。 3:每个实例中对应一个用于存储key访问信息的key这样可以统计更多的key。 监控视图通过上面的努力我们可以形成三个视图 第一memcached 实例视图以某个具体cache实例为单位呈现memcached服务本身所提供的统计信息还包含此实例中包含了多少个分区,即实例上包含了多少个项目使用的缓存。 第二分区视图根据分区名称集合所有节点的数据最终汇总出统计数据例如可以统计酒店项目总共使用了多少个key等,这对分析key的分布情况比较有帮助。 第三key视图呈现具体key的访问信息以及手工过期功能。 总结上面的方案虽然能实现需求但在实际生产环境中尽量不要打开这种监控功能需要的时候再打开尽量让cache的效率最高。 原文来自雨枫技术教程网 http://www.fengfly.com原文网址http://www.fengfly.com/plus/view-197176-1.html 转载于:https://www.cnblogs.com/lzjsky/archive/2012/09/05/2671464.html