获取缓存d时,可以校验 缓存a时间戳 + 缓存b时间戳 + 缓存c时间戳,abc任何一个时间戳发生变化,缓存d都需要重新加载,思路和上面的差不多,这里就不多赘述了。
guava 的妙用对于权限校验中使用频率高,但校验逻辑又不常变化的地方可以再加一层缓存。
例如一般都权限系统都有对外的接口,可以直接匿名访问,校验代码如下
// ant风格 url 匹配器 private AntPathMatcher matcher = new AntPathMatcher(); // 可以访问的匿名url集合,通常采用ant风格,例如 /open/api/** // 匿名url通常写在配置文件中,并且在bean初始化时加载到该集合中 private Set<String> anonymousUrlPatterns = new HashSet<String>(); // 判断url是否能匿名访问 public boolean couldAnonymous(String url) { for (String patternUrl : anonymousUrlPatterns) { if (matcher.match(patternUrl, url)) { isMatch = true; break; } } return isMatch; }可以看到,每一次url访问都会校验,可以通过加一层缓存来优化性能
用分布式缓存感觉有点大材小用,ehcache又有点太重量级,ConcurrentHashMap又不支持缓存策略,思来想去guava貌似是最好的选择,改造完后的代码如下:
// ant风格 url 匹配器 private AntPathMatcher matcher = new AntPathMatcher(); // 可以访问的匿名url集合,通常采用ant风格,例如 /open/api/** // 匿名url通常写在配置文件中,并且在bean初始化时加载到该集合中 private Set<String> anonymousUrlPatterns = new HashSet<String>(); // 匿名url访问权限缓存 private static Cache<String, Boolean> anonymousUrlCache = CacheBuilder.newBuilder() .maximumSize(5000) .initialCapacity(1000) .expireAfterAccess(1, TimeUnit.DAYS) // 设置cache中的的对象多久没有被访问后过期 .build(); // 判断url 是否能匿名访问 public boolean couldAnonymous(String url) { // 先从缓存中取,有的话直接返回 Boolean couldAnonymousAccess = anonymousUrlCache.getIfPresent(url); if (couldAnonymousAccess != null) { return couldAnonymousAccess; } boolean isMatch = false; for (String patternUrl : anonymousUrlPatterns) { if (matcher.match(patternUrl, url)) { isMatch = true; break; } } // 匹配结果放入缓存 anonymousUrlCache.put(url, isMatch); return isMatch; } localStorage 缓存localStorage 是 HTML5支持的新特性,可以把一些数据缓存放在客户端,减轻服务器的压力,例如可以把菜单数据放到客户端,菜单数据是否过期通过时间戳来判断,伪代码如下:
var timestamp = localStorage.getItem("timestamp" + userId); // 请求后台获取菜单接口,带上时间戳参数 timestamp // 后台校验时间戳是否变更,如果变更,返回新的菜单数据和新的时间戳,否则不需要返回菜单数据,仍旧返回旧的时间戳即可 // 后台接口返回数据格式 result = {menus:{},timestamp:""} var newTimestamp = result.timestamp; // 时间戳变更,把新的菜单数据和新的时间戳 放入 localStorage if (newTimestamp != timestamp) { localStorage.setItem("menus" + userId, JSON.stringify(result.menus)); localStorage.setItem("timestamp" + userId, newTimestamp); }有人担心把缓存放在localStorage中如果被修改会造成安全问题,其实这个担心是没必要的,因为权限校验是在服务器端做的,localStorage中的缓存只做展示使用,因此修改localStorage时没有任何意义的。
总结在不同的情况下,上述场景分别用了ehcache,redis,guava,localStorage做缓存,更加说明了没有最好的技术,只有最适合的技术。通过引入时间戳这种版本号的机制,解决了缓存更新问题。最终的目的只有一个,保证缓存数据一致性的同时,把性能做的极致,用户体验做到最好。