那些年我们一起追过的缓存写法(四)

阅读目录: 

  1. 介绍
  2. 进程缓存
  3. 通信方式
  4. 速度对比
  5. 总结

介绍

之前有童鞋问到关于首次为空的问题,这里简单补充下:

  • 一般来说并发量小、缓存数据量小的网站让用户自行触发页面让其缓存即可。
  • 大点网站都会多台部署,用负载均衡路由。常见的策略是在每台机器发布应用时,节点从负载均衡节点集合中移除,发布结束后,首次访问通过人工或自动请求下页面让其缓存,也包括预编译。
  • 还有一种是并发量大,缓存数据量大的情况,这也是本文的主题,下面会详细介绍。

如果缓存数据量大的情况下,预热就麻烦些。比如LZ公司单在内存中的缓存大小都过G,每次预热都需要数分钟,假设放在应用进程内,对运维工作非常不方便的。如果有意外导致进程池回收,对用户来说就是灾难性的。所以需要把应用进程的数据缓存给单独抽离出来存放,与应用解耦。

关于高并发的解决方案包括缓存更新策略可参见前几篇博客的介绍。

进程缓存

在网站架构演化中,这个阶段就需要引入分布式缓存了,比如memcached、redis。好处就不多讲了,坏处就是速度慢。这里速度慢是与本机内存缓存相比,跨机器通信跟直接读内存差的不是一数量级,对于并发量高、操作频繁的数据就不适用了。

所以把应用进程缓存的数据抽离出来,放在单独进程中,给应用提供一层缓存。缓存的业务逻辑、并发处理在独立进程中做,使用进程通信进行交互。这样不但解决了数据量大预热的麻烦,还能解耦部分应用的业务。

另外单独的进程也可以供外部使用,比如以WCF服务的方式提供给其他子系统使用。

缺点是跨进程读取的速度比进程内读取要稍慢。

通信方式

独立进程与应用进程的几种常见通信方式:

Namedpipe

Namedpipe一种相对高效的进程通信方式,支持局域网内通信。

Service端:

        var txt = File.ReadAllText("b.txt");
        ServerPipeConnection PipeConnection = new ServerPipeConnection("MyPipe", 512, 512, 5000, false);
        Console.WriteLine("listening..");
        while (true)
        {
            try
            {
                PipeConnection.Disconnect();
                PipeConnection.Connect();
                string request = PipeConnection.Read();
                if (!string.IsNullOrEmpty(request))
                {
                    PipeConnection.Write(txt);
                    if (request.ToLower() == "break") break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                break;
            }
        }
        PipeConnection.Dispose();
        Console.Write("press any key to exit..");
        Console.Read();  

client端:

IInterProcessConnection clientConnection = new ClientPipeConnection("MyPipe", "."); clientConnection.Connect(); var val = clientConnection.Read(); clientConnection.Close();

Wcf Namedpipe

Wcf在原生namedpipe包装了一下,使用起来更为简单方便。
Service端:

ServiceHost host = new ServiceHost( typeof (CacheService)); var NamePipe = new NetNamedPipeBinding(); host.AddServiceEndpoint(typeof(ICacheService), NamePipe, "net.pipe://localhost/CacheService"); host.Open(); Console.WriteLine("服务可用");Console.ReadLine(); host.Close(); 

Client端:

ChannelFactory pipeFactory = new ChannelFactory(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/CacheService"));
ICacheService pipeProxy = pipeFactory.CreateChannel();
var obj=pipeProxy.GetVal();

SharedMemory

共享内存是进程间通信最快的一种方式,数据无需在进程间复制传输,直接开辟一块公共内存,供其他进程进行读写。

service端:

 var mmf = MemoryMappedFile.CreateFromFile(@"a.txt", FileMode.Open, "cachea");
Console.ReadLine();
mmf.Dispose();

Client端:

var mmf = MemoryMappedFile.OpenExisting("cachea");
var accessor = mmf.CreateViewAccessor(0, 2000000);accessor.ReadChar(1000);
accessor.Dispose();
mmf.Dispose();

WCF TCP方式

使用WcfTcp的方式,可以供外部网络使用。

Service端:

 var ServiceHost host = new ServiceHost( typeof (CacheService));
 host.AddServiceEndpoint(typeof (ICacheService), new NetTcpBinding(), "net.tcp://192.168.0.115:8057/CacheService/");
host.Open();
Console.WriteLine("服务可用");
Console.ReadLine();
host.Close();

Client端:

 ChannelFactory NetcpFactory = new ChannelFactory(netTcpBindingBinding,new EndpointAddress("net.tcp://192.168.0.115:8057/CacheService/"));
 ICacheService tcpProxy= NetcpFactory.CreateChannel();
 var obj=tcpProxy.GetVal();

速度对比


上图是在windows7 i5-3230CPU上跑的,13M和1M文本数据各100次传输测试的均值。
其中原生namedpipe相较已经非常快了,在可以接受的范围,共享内存的方式速度会更快些。

测试结果表明Wcf的namedpipe要慢于wcf-tcp的方式,这个让人有些意外。

WcfTcp绑的是保留地址:

 new ChannelFactory(netTcpBindingBinding, new EndpointAddress("net.tcp://192.168.0.115:8057/CacheService/"));

WcfTcp localhost绑的是127.0.0.1:

new ChannelFactory(netTcpBindingBinding, new EndpointAddress("net.tcp://localhost:8057/CacheService/"));

总结

在大型网站开发中,缓存是个永远避免不了的话题,也不存在一种方案能解决所有的问题。

而缓存开发过程经常碰到的问题:过期策略(惰性)、缓存更新(独立)、多级缓存、分布 式缓存(分片)、高可用(单点)、高并发(雪崩)、命中率(穿透)、缓存淘汰(LRU)等。

其多级缓存方案的层级关系大都是由浏览器->cdn->反向代理缓存->线程级->内存级->进程级->文件(静态资源)->分布式(redis)->Db结果。

多数内容在LZ前面博文中有过介绍,有兴趣的童鞋可以看看。

参考资源

[1]  http://www.codeproject.com/Articles/7176/Inter-Process-Communication-in-NET-Using-Named-Pip

时间: 2024-08-14 23:14:41

那些年我们一起追过的缓存写法(四)的相关文章

那些年我们一起追过的缓存写法(三)

目录 一:分析设计 二:O(1)LRU实现 三:过期删除策略 四: 总结 一:分析设计 假设有个项目有一定并发量,要用到多级缓存,如下: 在实际设计一个内存缓存前,我们需要考虑的问题: 1:内存与Redis的数据置换,尽可能在内存中提高数据命中率,减少下一级的压力. 2:内存容量的限制,需要控制缓存数量. 3:热点数据更新不同,需要可配置单个key过期时间. 4:良好的缓存过期删除策略. 5:缓存数据结构的复杂度尽可能的低. 关于置换及命中率:我们采用LRU算法,因为它实现简单,缓存key命中率

那些年我们一起追过的缓存写法(二)

引言 感谢园子里的同学对上一篇的支持,很高兴楼主的一些经验及想法能够对大家有一些帮助. 上次主要讨论缓存读写这块各种代码实现.本篇是就上次的问题接着来,继续看那些年我们各种缓存用法. 目录 一:缓存预热 二:多级缓存 2.1 介绍 2.2 线程缓存 2.3 内存缓存 2.4 文件缓存 2.5 分布式缓存 2.6 DB缓存 三:多层缓存 四:总结 一:缓存预热 上次有同学问过.在第一次加载时,我们的缓存都为空,怎么进行预热. 单机Web情况下,一般我们使用RunTimeCache.相对于这种情况下

那些年我们一起追过的缓存写法(转)

出处:http://www.codeceo.com/article/cache-01.html ------------------------------------------------------------------------------ 介绍 本篇主要说下楼主平常项目中缓存使用经验和遇到过的问题. 目录 一: 基本写法 二:缓存雪崩 1:全局锁,实例锁 2:字符串锁 三:缓存穿透 四:再谈缓存雪崩 五:总结 一:基本写法 为了方便演示,我们用Runtime.Cache做缓存容器,

那些年我们一起追过的缓存写法(一)

介绍 本篇主要说下楼主平常项目中缓存使用经验和遇到过的问题. 目录 一: 基本写法 二:缓存雪崩 1:全局锁,实例锁 2:字符串锁 三:缓存穿透 四:再谈缓存雪崩 五:总结 一:基本写法 为了方便演示,我们用Runtime.Cache做缓存容器,并定义个简单操作类.如下: public class CacheHelper { public static object Get(string cacheKey) { return HttpRuntime.Cache[cacheKey]; } publ

缓存写法总结

基本写法 为了方便演示,这里使用Runtime.Cache做缓存容器,并定义个简单操作类.如下: 1 2 3 4 5 6 7 8 9 10 11 12 public class CacheHelper    {        public static object Get(string cacheKey)        {            return HttpRuntime.Cache[cacheKey];        }        public static void Add(

.net core 2.x - 缓存的四种方式

其实这些微软docs都有现成的,但是现在的人想对浮躁些,去看的不会太多,所以这里就再记录下 ,大家一起懒一起浮躁,呵呵. 0.基础知识 通过减少生成内容所需的工作,缓存可以显著提高应用的性能和可伸缩性. 缓存对不经常更改的数据效果最佳. 缓存生成的数据副本的返回速度可以比从原始源返回更快. 在编写并测试应用时,应避免依赖缓存的数据.ASP.NET Core 支持多种不同的缓存. 最简单的缓存基于 IMemoryCache,它表示存储在 Web 服务器内存中的缓存. 在包含多个服务器的服务器场上运

浅谈缓存写法(一):缓存的雪崩和穿透

基本写法 为了方便演示,这里使用Runtime.Cache做缓存容器,并定义个简单操作类.如下: <pre class="brush:csharp;gutter:true;" style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word;"> public class CacheHelper { public static object Get(str

hibernate缓存,四种状态

FlushMode.AUTO:Hibernate判断对象属性有没有改变,是默认的清理模式 FlushMode.COMMIT:在事务结束之前清理Session的缓存,其他任何时候都不清理缓存 FlushMode.NEVER:除非强制调用Session.flush(),或则永不清理缓存.所作的修改只限与内存 FlushMode.ALWAYS:在每一个查询语句前都调用flush()进行缓存管理,这种模式经常不必要并且低效的. 1.瞬时状态 也就是实体对象处于new的状态 它是在session缓存区中不

【转载】HTTP 缓存的四种风味与缓存策略

原文地址:https://segmentfault.com/a/1190000006689795 HTTP Cache 通过网络获取内容既缓慢,成本又高:大的响应需要在客户端和服务器之间进行多次往返通信,这拖延了浏览器可以使用和处理内容的时间,同时也增加了访问者的数据成本.因此,缓存和重用以前获取的资源的能力成为优化性能很关键的一个方面.每个浏览器都实现了 HTTP 缓存! 我们所要做的就是,确保每个服务器响应都提供正确的 HTTP 头指令,以指导浏览器何时可以缓存响应以及可以缓存多久.服务器在