Caching 如何在ColdFusion中正确实现共享缓存?

Caching 如何在ColdFusion中正确实现共享缓存?,caching,coldfusion,scope,locking,Caching,Coldfusion,Scope,Locking,我构建了一个CFC,它被设计成一个动态的、老化的缓存,用于几乎所有值得缓存的东西。LDAP查询、函数结果、数组、对象等等。任何需要花费时间或资源来计算并且需要不止一次的东西。我希望能够做几件事: 在应用程序之间共享CFC 定义缓存的范围(仅限服务器/应用程序/会话/当前请求) 在同一请求中同时使用不同的缓存实例 使用缓存组件独立于CFC 通常遵循常识(解耦、封装、正交性、锁定) 我当然会为每个不同的任务使用不同的缓存实例,但我希望能够在应用程序之间使用相同的CFC。缓存本身(除此之外)是一个

我构建了一个CFC,它被设计成一个动态的、老化的缓存,用于几乎所有值得缓存的东西。LDAP查询、函数结果、数组、对象等等。任何需要花费时间或资源来计算并且需要不止一次的东西。我希望能够做几件事:

  • 在应用程序之间共享CFC
  • 定义缓存的范围(仅限服务器/应用程序/会话/当前请求)
  • 在同一请求中同时使用不同的缓存实例
  • 使用缓存组件独立于CFC
  • 通常遵循常识(解耦、封装、正交性、锁定)
我当然会为每个不同的任务使用不同的缓存实例,但我希望能够在应用程序之间使用相同的CFC。缓存本身(除此之外)是一个结构,是缓存实例的私有结构。当作用域本身可能发生更改时,如何正确实现缓存和锁定

对于锁定,我使用命名锁(
'CacheRead'
'CacheWrite'
)目前,这是安全的 但我觉得很奇怪。为什么我想要服务器范围的锁,比如说,只用于会话的操作?(是的,也许这是学术性的,但无论如何。)


当我需要应用程序级缓存时,将应用程序范围作为引用传递似乎也是错误的。有更好的方法吗?

好的-因为我最初误解了你的问题,所以我删除了以前的答案,以免引起进一步的混淆

要回答有关锁定的问题,请执行以下操作:

命名锁应该可以,因为它们不必总是具有相同的名称。您可以根据正在访问的缓存动态命名它们。当您需要访问私有结构的元素时,您可以做一些事情,比如让命名锁使用密钥作为其名称


通过这种方式,只有当某个对象试图通过名称访问同一个缓存时,锁才会起作用。

我理解您希望避免传递要缓存到的实际作用域结构,但您的选择是有限的。首先想到的是传递要存储缓存的作用域的名称(一个字符串),然后计算。就其性质而言,评估是低效的,应当避免。也就是说,我很好奇这是如何实现的。我没有你的代码,所以我只是在这里做了一个非常简单的“存储”抽象CFC(跳过缓存,因为它与我要测试的内容无关):

cache.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>

函数init(作用域){
variables.cacheScope=arguments.scope;
归还这个;
}
函数cacheWrite(键、值){
structInsert(evaluate(variables.cacheScope)、arguments.key、arguments.value、true);
归还这个;
}
函数cacheRead(键){
if(不存在structKeyExists(求值(variables.cacheScope)、arguments.key)){
返回“”;
}否则{
variables.cache=evaluate(variables.cacheScope);
返回variables.cache[arguments.key];
}
}   
以及测试它的一个视图:

<!--- 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一样链接方法调用。部分视图可以简单地写成:


有趣的是,这是可能的,但我可能不会在生产中使用它,因为评估的间接成本。我想说,这是一个足够有效的理由,可以传入要缓存到的范围

如果您仍然对此感到困扰(也许是正确的),您可以创建另一个CFC,从所需的作用域抽象读写,并将其作为存储位置传递到缓存CFC中(这是一项非常适合的任务),如果您决定将缓存移到另一个作用域中,您不必全部使用缓存CFC将“会话”传递到init来编辑300个页面,相反,您可以编辑1个CFC或ColdSpring配置


我不完全确定,当您有请求范围时,为什么要使用单请求缓存。如果您正在寻找一种方法来缓存当前请求的某些内容,并在不久之后使其消失,那么请求范围可能就是您想要的。当缓存跨越多个请求时,它通常更有价值。

感谢您的努力!:-)我认为我将采用这种方法,在创建组件时生成UUID作为锁名。通过这种方式,整个锁定功能变得更加独立。很高兴为您提供帮助。祝你好运,快乐!关于单个请求“用例”:另一个CFC中的一组递归函数使用我的缓存对象进行透明函数结果缓存。函数以正常的方式调用,在开始工作之前,它们会检查最近是否计算了当前参数的结果。我认为我描述的方法是将单独的作用域抽象cfc注入缓存cfc中,这是最好的方法。
<cfset rtn = createObject("component", "cache")
    .init("session")
    .cacheWrite("foo", "bar")
    .cacheRead("foo")/>