欢迎进入Wiki » FAQ » 缓存使用说明?

缓存使用说明?

在2014-08-04 19:23上被李小翔修改
评论 (0) · 附件 (0) · 记录 · 信息

BroToolkit/BroFramework的缓存解决方案有多个插件与API组成,涉及到浏览器缓存、页面缓存、数据库缓存、缓存集群同步等多个方面。

浏览器缓存

浏览器缓存是通过设置 response 的 http 头来设置的。

  • 所有控制器操作中,可以直接调用 nocache() 方法来禁用浏览器缓存
  • 附件下载时,会自动设置长期有效的浏览器缓存
  • 插件 cache-headers 提供了多个设置 http 头的 API 和配置,详见 插件首页
  • 插件 assets-pipeline 提供了图片、js、css等资源缓存的解决方案(自动在 url 后面拼上一个文件唯一标示)
  • 在浏览器端,关于 AJAX 缓存的问题,可以参考 AJAX 缓存概述

数据库缓存

Hibernate 的默认缓存解决方案是 EhCache,因此,BroToolkit 中内置了 cache-ehcache 插件作为缓存实现方案。

  • BroTooklie 的 DataSource.groovy 模板中,默认配置了下面几个缓存参数,启用了查询缓存、二级缓存等
    hibernate {
        cache.use_second_level_cache = true
        cache.use_query_cache = true
        cache.region.factory_class = 'org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory'
    }
  • 此外,Grails 的动态查阅API、Domain类中都有相关的配置,详见 Caching Strategy

页面缓存

当一个用户访问某个页面(GSP、控制器操作)时,插件 cache 提供了将渲染结果缓存下来的功能,以便下次访问时,直接从缓存中读出渲染结果返回给浏览器,同时,我们采用了 cache-ehcache 插件作为缓存的实现。

cache 的文档详见:Cache Plugin,常用的功能简介如下。

缓存、清空控制器操作的渲染结果,如下例所示:

import grails.plugin.cache.CacheEvict
import grails.plugin.cache.Cacheable

class TestController {
   // 缓存到名为 message 的缓存中
   @Cacheable('message')
   def lookup() {
        println "called 'lookup'"
   }
   
   // 清空名为 message 的缓存
   @CacheEvict(value='message', allEntries=true)
   def evict() {
        println "called 'evict'"
   }
}

需注意的是,这里没有指定缓存的 key,默认会使用 params 来作为key,比如:

  • 用户访问 /test 时,会生成一个 key 为空的缓存条目
  • 用户再次访问 /test 时,会直接访问缓存中的数据
  • 如果用户访问 /test?foo=bar,由于参数不同,则会创建一个新的缓存条目

但是,当 params 一致,但是由不同用户访问时,由于权限等各种原因,看到的内容应该是不一样的,这里就必须为不同用户设置不同的key,因此,我们扩展了一下注解 @Cachable 的 key 参数,如下例所示:

@Cacheable(value="foobar", key="#bropen.toolkit.web.cache.UserKeyGenerator")
def test() {
   ......
}

即,当key为 #bropen.toolkit.web.cache.UserKeyGenerator 时,缓存的 key 中会自动拼上当前用户信息,从而实现为不同用户缓存不同的内容。另外,还可以设置 key 为 #bropen.toolkit.web.cache.SessionKeyGenerator,在 key 中拼上当前的 Session 信息,用户注销后缓存就不再可用了。

除了控制器缓存外,cache插件还提供了gsp标签来缓存视图中的一部分或者一个视图模板,下面贴两个官方示例:

<cache:block>
   <!-- Any valid markup may be included here, including dynamic expressions, invoking other tags, etc.... -->
</cache:block>

<cache:block key="${currentUser.id}">
   <!-- Any valid markup may be included here, including dynamic expressions, invoking other tags, etc.... -->
</cache:block>

<cache:render template="myTemplate" model="[name: 'Some Value']"/>

<cache:render template="myTemplate" model="[name: 'Some Value']" key="${currentUser.id}"/>

此外,cache插件的功能并非仅限于此,详见官方文档。

缓存API

cache插件提供的API,开发不是方便,且缺乏API文档,因此,BroToolkit 提供了一个基于 EhCache 的 bropen.toolkit.utils.grails.CacheUtils 的工具类,主要如下:

  • Ehcache createCache
    创建缓存,一般用不到,get、put、doWithBlockingCache时都会自动创建缓存
  • Object get(String cacheName, Serializable key)
    从缓存中取一个值,如果key所对应的缓存条目存在且未过期,则返回缓存值,否则返回null
    println CacheUtils.get( "foobar", "a key")
  • boolean put(String cacheName, Serializable key, Object value, boolean blocking=false)
    将键值对缓存,如果老缓存存在,会自动更新
    CacheUtils.put( "foobar", "a key", "the value" )
  • boolean evict(String cacheName, Serializable key)
    从缓存中删除 key 对应的缓存条目
  • flushCache(String cacheName)
    清空名为 cacheName 的缓存中的所有条目
  • Object doWithBlockingCache(String cacheName, Serializable key, Closure closure)
    执行闭包参数 closure 中的代码,并将其结果缓存下来。
    下次调用此 API 时,如果 key 在缓存中存在,则直接返回缓存中的值,而不执行闭包。
    BTW:人组组织树都是采用这种方式缓存的
    示例如下:
    // 将闭包执行的结果缓存下来
    CacheUtils.doWithBlockingCache( "foobar", "a key", {
       return "the value"
    })

当然,cache 的 key 或 value 除了字符串外,还可以为任何可以序列化(implements Serializable)的 Java Bean。

此外,cache等插件还在系统中注册了一些 spring bean,常见的如 ehcacheCacheManager,即ehcache的管理器,通过它可以获得各个缓存,并进行操作,如:

def cache = ctx.ehcacheCacheManager.getEhcache("osm")
println cache.getSize()
println cache.getKeys()
println cache.getCacheConfiguration()
cache.remove("a key")

缓存配置

ehcache 的配置详情可以参考官方文档 Cache EhCache。

BroToolkit 中,通过 BroToolkitCacheConfig 配置了:

  • 一些缓存默认值
    • 最长发呆期(timeToIdleSeconds):run-app模式下设为 30 秒,war包模式下设为 30 分
    • 最长存活期(timeToLiveSeconds):run-app模式下设为 30 秒,war包模式下设为 4 小时
    • 内存中的最大条目数(maxElementsInMemory):10000
  • 磁盘缓存位置(diskStore)
    • 根据当前主机和应用信息,在系统临时文件夹下生成唯一的缓存文件夹
    • 系统启动时会在日志中打印出上述文件夹的位置
  • 同时设置了两个缓存监听器工程,用于集群环境下的缓存同步配置,默认为异步方式:
    • cacheEventListenerFactorySync:同步
    • cacheEventListenerFactoryAsync:异步
  • 通过外部配置文件 clusters.properties,设置集群的同步地址信息

通过阅读 BroToolkitCacheConfig、BroFrameworkCacheConfig、BroBPMCacheConfig 中的示例,以及官方文档中的示例,缓存配置本身非常简单,如下例所示:

// 在下面的文件夹下新建一个以 CacheConfig 结尾的配置文件
// grails-app/conf/MyCacheConfig.groovy

// 最后加载(大于 BroBPMCacheConfig 中的值)
order  = 2000

config = {
   
   // osm相关的缓存:30'
   cache {
        name                "osm"
        timeToLiveSeconds    (application.warDeployed ? 30*60 : 60)
        timeToIdleSeconds    0        // 没有idle时间,缓存创建时间+live时间后就过期。详细可以参考Element方法getExpirationTime的源码
       maxElementsInMemory  100
   }
   
   // SSO缓存:10',同步
   cache {
        name                "sso"
        timeToLiveSeconds    10*60
        timeToIdleSeconds    0
        cacheEventListenerFactoryName    'cacheEventListenerFactorySync'
        maxElementsInMemory  50
   }
   
}

集群的情况下,如果是水平集群,并且主机名映射在对外IP地址上(不是127.xxx),则无须任何配置即可支持缓存的集群同步,否则,需要在每个集群节点(JVM)下部署 clusters.properties 文件。该文件的部署位置默认为 {user.dir}/bropen/clusters.properties,可以通过 Config 配置 bropen.toolkit.cluster.config.file 改变其位置。示例如下:

# 节点1的 clusters.properties 文件
# 主机地址、应用服务器端口
server.host = 192.168.1.11
server.port = 8080

# 节点2的 clusters.properties 文件
# 主机地址、应用服务器端口
server.host = 192.168.1.12
server.port = 8080

需要注意的是,如果是垂直集群,需要将不同的JVM节点绑定到不同的IP地址上。

标签: BroToolkit cache
在2014-08-04 19:21上被李小翔创建

Copyright © 2013 北京博瑞开源软件有限公司
京ICP备12048974号