一般动作
登录
Wiki: BROPEN ▼: Document Index
»
Space: FAQ ▼: Document Index
»
Page: 缓存使用说明
搜索
zh
页面动作
导出 ▼: 以PDF格式导出 以RTF格式导出 以HTML格式导出
更多动作 ▼: 打印预览 察看源码
欢迎进入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上被李小翔创建

- 评论 （0）
- 附件 （0）
- 记录
- 信息

# 快速链接

- 首页
- 新闻动态
- FAQ

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