如何在 ColdFusion 中正确实现共享缓存?
-
03-07-2019 - |
题
我构建了一个 CFC,旨在用作动态、老化的缓存,几乎可以用于所有值得缓存的内容。LDAP 查询、函数结果、数组、对象,应有尽有。无论什么都需要时间或资源来计算,并且需要多次。我希望能够做一些事情:
- 在应用程序之间共享 CFC
- 定义缓存的范围(仅服务器/应用程序/会话/当前请求)
- 在同一个请求中同时使用不同的缓存实例
- 使用缓存组件独立于 CFC
- 一般遵循常识(解耦、封装、正交、锁定)
当然,我会为每个不同的任务使用不同的缓存实例,但我希望能够跨应用程序使用相同的 CFC。缓存本身是(还有什么)一个结构,是缓存实例私有的。当范围本身发生变化时,如何正确实现缓存和锁定?
对于锁定,我使用命名锁('CacheRead'
, 'CacheWrite'
) 目前,这是安全的
但我觉得很奇怪。为什么我需要一个服务器范围的锁来进行仅会话操作?(是的,也许这个 是 学术性的,但无论如何。)
当我想要应用程序级缓存时,传递 APPLICATION 范围作为参考似乎也是错误的做法。有没有更好的办法?
解决方案
我理解您希望避免传递要缓存的实际范围结构,但您的选择是有限的。首先想到的就是传递您想要存储缓存的范围的名称(字符串)并进行评估。就其本质而言,评估效率低下,应该避免。也就是说,我很好奇它是如何实现的。我没有你的代码,所以我只是在这里做了一个非常简单的“存储”抽象CFC(跳过缓存,因为它与我想要测试的内容无关):
缓存.cfc:
<cfcomponent>
<cfset variables.cacheScope = "session" /><!--- default to session --->
<cfset variables.cache = ""/>
<cfscript>
function init(scope){
variables.cacheScope = arguments.scope;
return this;
}
function cacheWrite(key, value){
structInsert(evaluate(variables.cacheScope),arguments.key,arguments.value,true);
return this;
}
function cacheRead(key){
if (not structKeyExists(evaluate(variables.cacheScope), arguments.key)){
return "";
}else{
variables.cache = evaluate(variables.cacheScope);
return variables.cache[arguments.key];
}
}
</cfscript>
</cfcomponent>
以及测试它的视图:
<!--- clear out any existing session vars --->
<cfset structClear(session)/>
<!--- show empty session struct --->
<cfdump var="#session#" label="session vars">
<!--- create storage object --->
<cfset cacher = createObject("component", "cache").init("session")/>
<!--- store a value --->
<cfset cacher.cacheWrite("foo", "bar")/>
<!--- read stored value --->
<cfset rtn = cacher.cacheRead("foo")/>
<!--- show values --->
<cfdump var="#rtn#">
<cfdump var="#session#" label="session vars">
无关:我喜欢编写 setter 函数来返回“this”[如上所示],以便我可以像 jQuery 一样链接方法调用。视图的一部分可以很容易地写成:
<cfset rtn = createObject("component", "cache")
.init("session")
.cacheWrite("foo", "bar")
.cacheRead("foo")/>
有趣的是,这是可能的,但由于评估的间接成本,我可能不会在生产中使用它。我想说,这是传入您想要缓存的范围的充分理由。
如果您仍然对此感到困扰(也许确实如此?),您可以创建另一个 CFC,从所需范围中抽象读取和写入,并将其传递到缓存 CFC 作为存储位置(这项任务非常适合 冷泉),这样,如果您决定将缓存移至另一个范围,则不必全部使用缓存 CFC 并传入“会话”进行初始化来编辑 300 个页面,而是可以编辑 1 个 CFC 或您的 ColdSpring 配置。
我不完全确定为什么当你有请求范围时你会想要单请求缓存。如果您正在寻找一种为当前请求缓存某些内容并使其在不久后消失的方法,那么请求范围可能就是您想要的。当跨越多个请求时,缓存通常更有价值。
其他提示
好吧 - 因为我最初误解了你的问题,所以我删除了之前的答案,以免引起任何进一步的混乱。
回答您有关锁定的问题:
命名锁应该没问题,因为它们不必总是具有相同的名称。您可以根据您正在访问的缓存动态命名它们。当您需要访问私有结构的元素时,您可以执行诸如让命名锁使用密钥作为其名称之类的操作。
这样,只有当某些东西试图通过名称访问相同的缓存时,锁才会起作用。