【问题标题】:Duplicate a ColdFusion Session so Threads Live On After Sign Out复制 ColdFusion 会话,以便在注销后线程继续存在
【发布时间】:2014-04-04 12:21:21
【问题描述】:

我需要在普通用户会话中运行某些 ColdFusion 代码块(例如:用户单击打印并生成 PDF)。该网页是交互式的,用户可以立即获得 PDF。

但我还需要在一个循环中多次运行相同的代码块(超过 1000 次迭代)。为此,我们告诉用户他们将在完成后收到一封电子邮件。我生成了一个低优先级的<cfthread> 并循环了相同的代码。问题是正在运行的代码严重依赖会话范围内的变量。因此,如果用户退出,线程会在遇到会话 var 时出错并终止。对于有问题的代码,我知道我可以将变量更改为不使用会话范围,然后线程将完成没有问题,但我们无法更改该代码。所以,我在几年前尝试了一种可行的方法,但我从来没有对此感觉良好,并且想知道是否有人能想到更好的方法。这就是黑客...

<cfapplication name="backgroundThread" sessionTimeout="#CreateTimeSpan(0, 0, 600, 0)#" sessionManagement="Yes">

<!--- Duplicate the entire session structure --->
<cflock scope="session" timeout="30" type="Exclusive">
    <cfloop collection="#this.session#" item="session_element">
        <cfset new_session_element = evaluate("this.session." & "#session_element#")>
        <cfset "session.#session_element#" = new_session_element>
    </cfloop>
</cflock>

<cfthread action="run" priority="low" name="#this.threadid#" session=session>
  <!---
  Normal code can be included here and it will run.
  Even if the user who initiated the background process signs out.
  --->

所以基本上我创建了一个全新的应用程序/会话,然后将所有会话变量复制到这个新的应用程序/会话。所以如果用户退出它不会对后台进程产生影响,需要会话变量的代码仍然会运行。

这已经工作了很长时间。偶尔我会研究它,但还没有找到更好的方法。在这样的应用程序中创建应用程序对我来说似乎不合适。有没有其他方法可以不使用使用&lt;cfapplication&gt; 标签生成新会话?

我考虑过调用application.cfc-&gt;onSessionStart,但这就是 Adob​​e 所说的:如果您明确调用此方法,ColdFusion 不会启动会话。我只需要生成一个新会话的能力,分开从当前用户,然后将他的大部分会话变量复制到它。

2014 年 2 月 24 日更新:我尝试了这个建议,但它仍然对我不起作用(测试是在 CF10 盒子上完成的)...

<cfthread action="run" name="testingSession" session="#session#" priority="LOW">
    <cfset form.session = session>  <!--- add this dirty trick --->
    <cftry>
        <cfloop from="1" to="200" index="i">
            <cfscript>sleep(3000);</cfscript>
            <cfquery name="q1" datasource="#session.sv.ds#">
                select sysdate thedate from dual
            </cfquery>
            <cflog file="threadTest6" text="thread running... #i# ... #q1.thedate#">
        </cfloop>
        <cfcatch type="any">
            <cflog file="threadTest6" text="error: #cfcatch.detail# /// #cfcatch.message#">
        </cfcatch>
    </cftry>
</cfthread>

我让该页面运行几秒钟,然后单击“退出”链接。我的退出过程只做这个...

<!---End session--->
<cfset StructDelete(Session, 'dev')>
<cfset StructDelete(Session, 'sv')>
<cfset StructDelete(Session, 'act')>
<cfset StructClear(Session)>
<!---I cannot use `<cfset sessionInvalidate()>` because not all our customers are up to CF10.--->

发生这种情况时,我希望线程中的所谓重复会话继续进行,并且只结束原始用户的会话。但事实并非如此。这是threadTest6.log的输出...

"Severity","ThreadID","Date","Time","Application","Message"
"Information","cfthread-1","02/24/14","16:26:04",,"C:\ColdFusion10\cfusion\logs\threadTest6.log initialized"
"Information","cfthread-1","02/24/14","16:26:04","CMPRO","thread running... 1 ... 2014-02-24 16:26:04.0"
"Information","cfthread-1","02/24/14","16:26:07","CMPRO","thread running... 2 ... 2014-02-24 16:26:07.0"
"Information","cfthread-1","02/24/14","16:26:10","CMPRO","thread running... 3 ... 2014-02-24 16:26:10.0"
"Information","cfthread-1","02/24/14","16:26:13","CMPRO","thread running... 4 ... 2014-02-24 16:26:13.0"
"Information","cfthread-1","02/24/14","16:26:16","CMPRO","thread running... 5 ... 2014-02-24 16:26:16.0"
"Information","cfthread-1","02/24/14","16:26:19","CMPRO","thread running... 6 ... 2014-02-24 16:26:19.0"
"Information","cfthread-1","02/24/14","16:26:22","CMPRO","error: The value of the attribute datasource, which is currently '', is invalid. /// Attribute validation error for tag CFQUERY."

【问题讨论】:

  • 您不能将会话范围内的任何内容复制到线程本地范围内吗?
  • 否,因为包含在线程中的代码在 1000 多个地方使用了 session.whatever。这就是我所指的我们无法更改的代码。
  • 抱歉,解决方法对您不起作用。我记录了会话,尽管会话过期后似乎一切都在那里,但出于某种奇怪的原因,只能通过使用form.session.sv.dssession.sv['DS'] 访问session.sv.ds。我认为这与 CF 编译器对会话范围进行优化有关,但不适用于 form 中我们肮脏的 hacky session。这很脏是有原因的,它对你的情况来说太脏了。如果可能的话,我会重构现有代码以不直接引用Session。您可以cfparam 并默认使用Session
  • 是的。哦,好吧,我现在将继续使用我的 cfapplication hack。谢谢你的尝试,@Henry。
  • 如果你可以破解一个全新的应用程序并复制会话数据,我不明白为什么你不能将实际代码更改为不使用会话。看来您可以创建一个排队系统来存储相关数据并从中生成 PDF。

标签: multithreading session coldfusion


【解决方案1】:

在您当前引用 session.[whatever] 的代码中替换“session”的所有实例。用“我的结构”。以及所有带有“myStruct[”的“session[”实例。然后在需要这些会话变量的代码块的顶部,写下这一行:

<cfparam name="myStruct" type="struct" default="#session#" />

会话结构将通过引用(而不是通过值)传递给本地 myStruct 变量。在 myStruct 中设置值的任何代码也将在会话范围内设置这些值。并且由于它在请求中被声明为局部变量,因此 JRE 将继续在结构上拥有一个活动句柄,并且不会被垃圾收集(直到请求完成之后),即使 ColdFusion 可能会从其清除会话队列。

如果您有引用会话范围的自定义标签,则需要将此行添加到标签中:

<cfparam name="attributes.myStruct" type="struct" default="#session#" />

然后在循环中的标记调用中指定局部变量。

如果您有引用会话范围的自定义函数或 CFC,那么您需要为访问会话范围的任何方法添加一个可选参数:

<cfargument name="myStruct" type="struct" default="#session#" />

有什么我错过的吗?我认为这些是唯一需要改变的事情。我认为我不必解决onApplicationStartonApplicationEnd 事件中不存在的会话范围的问题,因为如果您在这些事件中使用此代码,您可能已经遇到过这个问题。所以我认为这应该是你所需要的。

即使对会话有数千个引用,即使在数千个文件中,这也可以相对容易地完成。完成修改后,它只需要几个多文件搜索和替换操作和一些测试。我以前做过这样的修改,只需要一两个小时。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-31
    • 2015-04-02
    • 2017-12-10
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多