代码之家  ›  专栏  ›  技术社区  ›  Tomalak

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

  •  1
  • Tomalak  · 技术社区  · 17 年前

    我已经构建了一个CFC,它被设计成一个动态的、老化的缓存,用于几乎所有值得缓存的东西。LDAP查询、函数结果、数组、项目,您可以命名它。任何需要时间或资源来计算并且需要不止一次的东西。我想做一些事情:

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

    当然,我会为每个不同的任务使用不同的缓存实例,但我希望能够跨应用程序使用相同的CFC。缓存本身是一个结构,对缓存实例是私有的。当作用域本身发生变化时,如何正确地实现缓存和锁定?

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

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

    2 回复  |  直到 13 年前
        1
  •  1
  •   Adam Tuttle    17 年前

    我理解您希望避免传递您想要缓存到的实际范围结构,但是您的选择是有限的。首先要想到的是,只需传递希望将缓存存储在其中的作用域的名称(字符串)并进行计算。从本质上讲,评价是低效的,应该避免。也就是说,我很好奇它是如何完成的。我没有你的代码,所以我在这里做了一个非常简单的“存储”抽象cfc(跳过缓存,因为它与我想测试的内容无关):

    CCF.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,并将其作为存储位置(非常适合 ColdSpring ,这样,如果您决定将缓存移动到另一个作用域,就不必使用将“session”传递给init的缓存cfc来编辑300个页面,而是可以编辑1个cfc或coldspring配置。

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

        2
  •  2
  •   Darren user513543    13 年前

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

    要回答有关锁定的问题:

    命名锁应该很好,因为它们不必总是使用相同的名称。您可以根据访问的缓存动态命名它们。当需要访问私有结构的元素时,可以执行类似于使用命名锁这样的操作,使用键作为其名称。

    这样,一个锁产生效果的唯一时间就是某个对象试图按名称访问同一个缓存。