2

我正在创建一个调用两个不同 CFC 的简单电子邮件服务器状态页面。

状态页要求:

  1. 通过 CFC 查询 MariaDB 数据库表并从两个字段返回数据:server_name(即 MyServerName)和 server_domain(即 mail.domain.com)。目前,数据库表中有 4 行需要拉取。
  2. 将步骤 1 中的数据库数据交给 CFC,检查端口 25 是否正在侦听。如果 CFC 可以到达端口 25,则结果为真,否则结果为假。这一步需要穿线。
  3. 将步骤 2 中的布尔结果通过循环传递以打印 server_name 和布尔结果。

输出与此类似的内容:
MyServerName - <up arrow>
MyServerName2 - <up arrow>
MyServerName3 - <up arrow>
MyServerName4 -<down arrow>

编码:

    RetrieveEmailServers = APPLICATION.selectQueries.RetrieveEmailServers()
    if (RetrieveEmailServers.recordCount) {
        for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
            LOCAL.theDomains = RetrieveEmailServers.check_servers_domain[i];
            LOCAL.theNames = RetrieveEmailServers.check_servers_name[i];
            thread action="run" name="thread#i#" theDomains="#LOCAL.theDomains#" theNames="#LOCAL.theNames#" {
                VARIABLES.theServers = APPLICATION.emailCheck.checkSMTPServer('#domains#',25,'','');
            }
        }
        thread action="join" timeout="6000"{}

        for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
            VARIABLES.theResult = cfthread["thread#i#"];
            if (VARIABLES.theResult.theServers) {
                LOCAL.theStatus = "<i class='fad fa-angle-double-up text-success fs-1'></i>"
            }
            else {
                LOCAL.theStatus = "<i class='fad fa-angle-double-down text-danger fs-1'></i>"
            } 
            writeOutput(ATTRIBUTES.theNames & " - " & LOCAL.theStatus & "<br>");
        }
    }
    else {
        writeOutput("No servers listed at this time.")
    }

错误:键 [THESERVERS] 不存在,结构为空

审议:

  1. 我知道我的代码不是很好,我知道它可以写得更好。我正在努力改进。
  2. 我不是全职编码员,但多年来我一直在断断续续地编码。我仍然认为自己是 CFML 的新手,所以很多方法都超出了我的想象。
  3. 上面的代码大部分都有效,但我很难理解如何在 CFTHREAD 之外传递信息以用于页面的其余部分,尤其是在处理 CFLOOP 时。
  4. 看了很多遍,还是不完全明白,如何正确使用线程局部作用域、线程作用域和属性作用域。
  5. 上面的代码有一个简单的任务,检查端口,但最终目标是在我的应用程序的其他部分使用类似的代码。我知道有更好的监控工具可用;这是一个帮助我理解和学习的练习。
  6. 具体到 Lucee,我知道threadData()['thread#i#'].status;可能需要对cfthread[].
4

1 回答 1

5

属性

Attributes范围仅用于保存传递线程的值。因此,作用域是短暂的,只存在于一个线程中。每个线程都有自己的“属性”范围,该范围在该线程运行之前或完成之后不存在。

例如,这个片段传入一个名为“theDomains”的属性。该变量Attributes.theDomains仅存在于线程内部。

 thread action="run" name="thread1" theDomains="example.com" {
     writeDump( attributes.theDomains );
 }
 
 thread action="join" name="thread1" {};
 
 writeOutput( thread1.output );

线程本地

“线程局部”是另一个短期作用域,其目的是保存在线程内使用的变量。每个线程都有自己的私有“本地”范围,与所有其他线程分开。与attributes作用域一样,它仅在线程执行时存在,并在线程完成时被清除。

例如,此代码段创建一个名为“MyLocalVar”的局部变量。显示线程output说明变量存在于线程中

thread action="run" name="thread1" {
    // Un-scoped v
    myLocalVar = "foo";
    writeOutput( "myLocalVar ="& myLocalVar );
}

thread action="join" name="thread1" {};
writeOutput( thread1.output );

但是在线程完成后尝试访问它会导致错误

// fails with error "key [MYLOCALVAR] doesn't exist"
writeOutput( "myLocalVar ="& thread1.myLocalVar );

Thread范围

示波器的Thread使用寿命更长。它旨在存储“..thread-specific variables and metadata about the thread ...”。更重要的是,这个作用域可用于将信息传递回调用页面(甚至其他线程)。

例如,此代码段创建了一个线程范围变量,该变量对调用页面可见,即使在线程完成执行后也是如此:

 thread action="run" name="thread1" {
    // use scope prefix "thread."
    thread.myThreadVar = "foo";
 }
 
 thread action="join" name="thread1" {};
 
 writeOutput( "thread1.myThreadVar="& thread1.myThreadVar );
 writeDump( thread1 );

问题:键 [THESERVERS] 不存在

当您在几天内一直在查看错误时,很容易忘记基础知识:) 处理未定义错误的第一件事是转储对象,并在尝试使用它之前查看是否实际包含您所期望的内容。

for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
    VARIABLES.theResult = cfthread["thread#i#"];
    writeDump( variables.theResult );

    /* temporarily comment out rest of code 
    ...
    */
}

转储VARIABLES.theResult显示线程实际上因不同的错误而失败

线程 1 的 CFDump

由于线程内的属性名称错误。应该是attributes.theDomains,不是domains

thread ...{
    APPLICATION.emailCheck.checkSMTPServer( attributes.theDomains, ... );
}

好的,我修好了。仍然得到“key [THESERVERS] 不存在”,现在怎么办?

线程的另一个转储显示错误消息没有说谎。theServers由于范围不正确,线程实际上不包含名为 的变量。使用thread范围,而不是`变量。

 thread ...{
     thread.theServers = ....;
 }

线程 2 的 CFDump

又一个错误?!变量 [ATTRIBUTES] 不存在

即使修复了前两个问题,您仍然会在这里遇到另一个错误

for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
   ...
   writeOutput(ATTRIBUTES.theNames & " - " & LOCAL.theStatus & "<br>");
}

请记住,attributes范围仅存在于线程中。所以很明显一旦线程完成就不能使用了。也可以存储theNamesthread范围中,或者由于您正在循环查询,请使用查询列值RetrieveEmailServers.the_query_column_name[ i ].

加入线程

最后一个潜在问题。join 语句实际上并没有等待您创建的线程。它只是等待 6000 毫秒。如果由于某种原因任何线程花费的时间比这更长,则在尝试检索线程结果时会出现错误。要真正等待创建的线程,您必须使用thread action="join" name=(list of thread names) {}. 我将把它作为练习留给读者(:

Tbh,还有其他可以清理和/或改进的东西,但希望这个冗长的杂乱无章的线程解释了为什么首先发生错误以及如何在将来使用线程时避免它们

于 2021-11-04T21:11:24.993 回答