原文参考:https://www.codeproject.com/Articles/5299328/LRU-Cache-CLOCK-2-hand-Implementation-In-NodeJS
在文章的开始我们需要了解什么是缓存?缓存是预先根据数据列表准备一些重要数据。没有缓存的话,系统的吞吐量就取决于存储速度最慢的数据,因此保持应用程序高性能的一个重要优化就是缓存。web应用程序中有两项很重要的工作,分别是文件和视频Blob的缓存和快速访问页面模板。而在NodeJS中,非异步功能操作的延迟会决定系统什么时候为其他客户端提供服务,尽管操作系统有自己的文件缓存机制,但是同一个服务器中有多个web应用程序同时运行,且其中一个应用正在传输大量视频数据的时候,其他应用的缓存内容就可能会频繁失效,此时程序效率会大幅降低。
而针对应用程序资源的LRU算法能有效解决这个问题,使应用程序不被同一服务器中的其他应用程序缓存所影响。考虑到存储速度最慢数据决系统吞吐量的这一点,LRU缓存的存在能将系统性能提高2倍至100倍;同时,异步LRU会隐藏全部高速缓存未命中的延迟。
接下来我们一起来看具体实现的内容。
代码展示
首先构建一个用来构造LRU对象模块的文件:
1 'use strict'; 2 let Lru = function(cacheSize,callbackBackingStoreLoad,elementLifeTimeMs=1000){ 3 let me = this; 4 let maxWait = elementLifeTimeMs; 5 let size = parseInt(cacheSize,10); 6 let mapping = {}; 7 let mappingInFlightMiss = {}; 8 let buf = []; 9 for(let i=0;i<size;i++) 10 { 11 let rnd = Math.random(); 12 mapping[rnd] = i; 13 buf.push({data:"",visited:false, key:rnd, time:0, locked:false}); 14 } 15 let ctr = 0; 16 let ctrEvict = parseInt(cacheSize/2,10); 17 let loadData = callbackBackingStoreLoad; 18 this.get = function(key,callbackPrm){ 19 20 let callback = callbackPrm; 21 if(key in mappingInFlightMiss) 22 { 23 setTimeout(function(){ 24 me.get(key,function(newData){ 25 callback(newData); 26 }); 27 },0); 28 return; 29 } 30 31 if(key in mapping) 32 { 33 // RAM speed data 34 if((Date.now() - buf[mapping[key]].time) > maxWait) 35 { 36 if(buf[mapping[key]].locked) 37 { 38 setTimeout(function(){ 39 me.get(key,function(newData){ 40 callback(newData); 41 }); 42 },0); 43 } 44 else 45 { 46 delete mapping[key]; 47 48 me.get(key,function(newData){ 49 callback(newData); 50 }); 51 } 52 } 53 else 54 { 55 buf[mapping[key]].visited=true; 56 buf[mapping[key]].time = Date.now(); 57 callback(buf[mapping[key]].data); 58 } 59 } 60 else 61 { 62 // datastore loading + cache eviction 63 let ctrFound = -1; 64 while(ctrFound===-1) 65 { 66 if(!buf[ctr].locked && buf[ctr].visited) 67 { 68 buf[ctr].visited=false; 69 } 70 ctr++; 71 if(ctr >= size) 72 { 73 ctr=0; 74 } 75 76 if(!buf[ctrEvict].locked && !buf[ctrEvict].visited) 77 { 78 // evict 79 buf[ctrEvict].locked = true; 80 ctrFound = ctrEvict; 81 } 82 83 ctrEvict++; 84 if(ctrEvict >= size) 85 { 86 ctrEvict=0; 87 } 88 } 89 90 mappingInFlightMiss[key]=true; 91 let f = function(res){ 92 delete mapping[buf[ctrFound].key]; 93 buf[ctrFound] = 94 {data: res, visited:false, key:key, time:Date.now(), locked:false}; 95 mapping[key] = ctrFound; 96 callback(buf[ctrFound].data); 97 delete mappingInFlightMiss[key]; 98 }; 99 loadData(key,f); 100 } 101 }; 102 }; 103 104 exports.Lru = Lru;