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

时间:2020-03-06 14:57:49  来源:igfitidea点击:

我建立了一个CFC,旨在用作几乎所有值得缓存的动态,老化的缓存。 LDAP查询,函数结果,数组,对象都可以命名。任何需要花费时间或者资源进行计算的东西,都需要多次。我希望能够做一些事情:

  • 在应用程序之间共享CFC
  • 定义缓存的范围(仅服务器/应用程序/会话/当前请求)
  • 在同一请求中同时使用不同的缓存实例
  • 使用缓存组件独立于CFC
  • 通常遵循常识(去耦,封装,正交,锁定)

我当然会为每个不同的任务使用不同的缓存实例,但是我希望能够在应用程序中使用相同的CFC。缓存本身是Struct(对缓存实例来说是私有的)(还有其他)。当范围本身发生变化时,如何正确实现缓存和锁定?

对于锁定,我目前使用命名锁('CacheRead',''CacheWrite'),这是安全的
但让我感到奇怪。为什么我要针对例如仅会话的操作使用服务器范围的锁? (是的,也许这是学术性的,但是无论如何。)

当我想要应用程序级缓存时,将APPLICATION范围作为参考传递也似乎是错误的事情。有没有更好的办法?

解决方案

好的,因为我最初误解了问题,所以我删除了之前的答案,以免造成任何进一步的混乱。

要回答有关锁定的问题:

命名锁应该很好,因为它们不必总是具有相同的名称。我们可以根据要访问的缓存来动态命名它们。当我们需要访问私有结构的元素时,我们可以执行一些操作,例如让命名锁使用密钥作为其名称。

这样,只有当某项试图按名称访问同一缓存时,锁才会生效。

我了解我们希望避免传入要缓存到的实际范围结构,但是选择受到限制。首先想到的只是传递要存储缓存的作用域的名称(字符串),然后求值。就其性质而言,评估效率低下,应避免使用。就是说,我很好奇它如何实现。我没有代码,所以我在这里制作了一个简单的"存储"抽象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>

并对其进行测试:

<!--- 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")/>

有趣的是,这是可能的,但是由于Evaluate的间接费用,我可能不会在生产中使用它。我要说的是,这足以传递我们要缓存到的范围的正当理由。

如果我们仍然对此感到不安(也许是对的?),则可以创建另一个CFC,该CFC从所需的作用域中抽象读写操作,并将其作为存储位置传递到缓存的CFC中(非常适合ColdSpring) ,这样,如果我们决定将缓存移到另一个作用域,则无需使用传递"会话"到init的缓存CFC来编辑全部300页,而是可以编辑1 CFC或者ColdSpring配置。

我不确定要在拥有请求范围时为何要进行单请求缓存。如果我们要寻找的是一种为当前请求缓存某些内容并使其很快死掉的方法,则请求范围可能就是我们想要的。当缓存跨越多个请求时,缓存通常更有价值。