使用 CFTHREAD 和 return 异步响应处理外部 HTTP 请求
Process external HTTP request with CFTHREAD and return asynchronous response
我构建了一个 API 外部服务器可以在其中 post JSON 数据到我们的 ColdFusion 服务器。 ColdFusion 服务器然后处理这些数据(遍历数组并将数据插入数据库等),然后向外部服务器响应一切是否正常。
但我宁愿运行进程在一个CFTHREAD中,因为将来负载会变得更重。我怎样才能执行这种异步操作,并且仍然响应发出请求的服务器?
我研究过事件网关,但这似乎不是可行的方法。
我也考虑过让外部服务器进行循环调用,比如 "are you done yet"、"are you done yet",但这并不是最终的解决方案。
我可以让管理外部服务器的人按照我们想要的方式调用我们的 ColdFusion 服务器,所以至少我不受此约束。
这是我的 index.cfm 接收呼叫的简化版本:
<cftry>
<cfinclude template="udf.cfm" />
<cfset _run()>
<cfcatch type="any">
<cfcontent type="application/json; charset=utf-8" reset="yes">
<cfoutput>{"success":0,"message":"#cfcatch.Message# - #cfcatch.Detail#"}</cfoutput>
</cfcatch>
</cftry>
这是我的 udf.cfm,它处理调用(又是它的简化版本):
<cffunction name="_run" output="yes" returntype="void">
<cfset local.success=true>
<cfset local.msg="">
<cftry>
<!---get request data--->
<cfset local.requestData=GetHttpRequestData()>
<!---validate post--->
<cfif NOT StructKeyExists(local.requestData,"headers")>
<!---headers is missing--->
<cfset _returnJson(msg="Headers is missing")>
</cfif>
<!---and a bunch of other validations...--->
<!---get body--->
<cfset local.isBinaryBody=IsBinary(local.requestData.content)>
<cfset local.bodyAsString=local.isBinaryBody ? ToString(local.requestData.content) : local.requestData.content>
<cfset local.body=DeserializeJson(local.bodyAsString)>
<cftransaction action="begin">
<cftry>
<!---prepare for database inserts by cleansing tables etc--->
<!---loop data array, can be tens of thousands of rows--->
<cfloop array="#local.body.items#" index="local.item">
<!---do some inserts/updates into MySQL Database--->
</cfloop>
<cfcatch type="any">
<!---error--->
<cfset local.msg=_parseCatchError(catch=cfcatch)>
<cfset local.success=false>
</cfcatch>
</cftry>
<!---rollback or commit transaction depending on success flag--->
<cftransaction action="#local.success ? 'commit' : 'rollback'#" />
</cftransaction>
<cfcatch type="any">
<!---error--->
<cfset local.msg=_parseCatchError(catch=cfcatch)>
<cfset local.success=false>
</cfcatch>
</cftry>
<!---return JSON--->
<cfset _returnJson(msg=local.msg,success=local.success)>
</cffunction>
<cffunction name="_returnJson" output="yes" returntype="void">
<cfargument name="msg" type="string" required="yes">
<cfargument name="success" type="boolean" default="false">
<cfscript>getPageContext().getOut().clearBuffer();</cfscript>
<cfcontent type="application/json; charset=utf-8" reset="yes">
<cfoutput>#SerializeJson(arguments)#</cfoutput>
<cfabort>
</cffunction>
<cffunction name="_parseCatchError" output="no" returntype="string">
<cfargument name="catch" type="any" required="yes">
<cfset local.error="#Trim(arguments.catch.message)# - #Trim(arguments.catch.detail)#">
<cfif StructKeyExists(arguments.catch,"tagContext")>
<!---grab line info--->
<cfset local.tagContext=arguments.catch.tagContext[1]>
<cfset local.error=local.error&" - #Trim(local.tagContext.template)# line #Trim(local.tagContext.line)#">
</cfif>
<cfreturn local.error>
</cffunction>
听起来您只需要调用方提供的回调。
- 接受请求,包括一个外部 url 供您的代码在完成后点击
- 开始跟帖,请勿加入
- return 通知调用方请求正在处理,结束主要请求
- 线程保持 运行 并在结束时触发回调 url,提供有关成功或失败的任何其他信息
此处还提供了一些其他选项。首先,您可以只接受请求所需的数据并将其记录在队列中,然后稍后再点击它们。此外,如您所述,您可以为调用者提供状态端点以检查进度。
请记住,coldfusion 中的线程受服务器版本的限制——我认为标准版最多支持 10 个线程——其他的则排在队列中。向线程提供 "callback" 将完成这项工作。
sql 转储完成后,还可以利用计划任务。
我构建了一个 API 外部服务器可以在其中 post JSON 数据到我们的 ColdFusion 服务器。 ColdFusion 服务器然后处理这些数据(遍历数组并将数据插入数据库等),然后向外部服务器响应一切是否正常。
但我宁愿运行进程在一个CFTHREAD中,因为将来负载会变得更重。我怎样才能执行这种异步操作,并且仍然响应发出请求的服务器?
我研究过事件网关,但这似乎不是可行的方法。 我也考虑过让外部服务器进行循环调用,比如 "are you done yet"、"are you done yet",但这并不是最终的解决方案。
我可以让管理外部服务器的人按照我们想要的方式调用我们的 ColdFusion 服务器,所以至少我不受此约束。
这是我的 index.cfm 接收呼叫的简化版本:
<cftry>
<cfinclude template="udf.cfm" />
<cfset _run()>
<cfcatch type="any">
<cfcontent type="application/json; charset=utf-8" reset="yes">
<cfoutput>{"success":0,"message":"#cfcatch.Message# - #cfcatch.Detail#"}</cfoutput>
</cfcatch>
</cftry>
这是我的 udf.cfm,它处理调用(又是它的简化版本):
<cffunction name="_run" output="yes" returntype="void">
<cfset local.success=true>
<cfset local.msg="">
<cftry>
<!---get request data--->
<cfset local.requestData=GetHttpRequestData()>
<!---validate post--->
<cfif NOT StructKeyExists(local.requestData,"headers")>
<!---headers is missing--->
<cfset _returnJson(msg="Headers is missing")>
</cfif>
<!---and a bunch of other validations...--->
<!---get body--->
<cfset local.isBinaryBody=IsBinary(local.requestData.content)>
<cfset local.bodyAsString=local.isBinaryBody ? ToString(local.requestData.content) : local.requestData.content>
<cfset local.body=DeserializeJson(local.bodyAsString)>
<cftransaction action="begin">
<cftry>
<!---prepare for database inserts by cleansing tables etc--->
<!---loop data array, can be tens of thousands of rows--->
<cfloop array="#local.body.items#" index="local.item">
<!---do some inserts/updates into MySQL Database--->
</cfloop>
<cfcatch type="any">
<!---error--->
<cfset local.msg=_parseCatchError(catch=cfcatch)>
<cfset local.success=false>
</cfcatch>
</cftry>
<!---rollback or commit transaction depending on success flag--->
<cftransaction action="#local.success ? 'commit' : 'rollback'#" />
</cftransaction>
<cfcatch type="any">
<!---error--->
<cfset local.msg=_parseCatchError(catch=cfcatch)>
<cfset local.success=false>
</cfcatch>
</cftry>
<!---return JSON--->
<cfset _returnJson(msg=local.msg,success=local.success)>
</cffunction>
<cffunction name="_returnJson" output="yes" returntype="void">
<cfargument name="msg" type="string" required="yes">
<cfargument name="success" type="boolean" default="false">
<cfscript>getPageContext().getOut().clearBuffer();</cfscript>
<cfcontent type="application/json; charset=utf-8" reset="yes">
<cfoutput>#SerializeJson(arguments)#</cfoutput>
<cfabort>
</cffunction>
<cffunction name="_parseCatchError" output="no" returntype="string">
<cfargument name="catch" type="any" required="yes">
<cfset local.error="#Trim(arguments.catch.message)# - #Trim(arguments.catch.detail)#">
<cfif StructKeyExists(arguments.catch,"tagContext")>
<!---grab line info--->
<cfset local.tagContext=arguments.catch.tagContext[1]>
<cfset local.error=local.error&" - #Trim(local.tagContext.template)# line #Trim(local.tagContext.line)#">
</cfif>
<cfreturn local.error>
</cffunction>
听起来您只需要调用方提供的回调。
- 接受请求,包括一个外部 url 供您的代码在完成后点击
- 开始跟帖,请勿加入
- return 通知调用方请求正在处理,结束主要请求
- 线程保持 运行 并在结束时触发回调 url,提供有关成功或失败的任何其他信息
此处还提供了一些其他选项。首先,您可以只接受请求所需的数据并将其记录在队列中,然后稍后再点击它们。此外,如您所述,您可以为调用者提供状态端点以检查进度。
请记住,coldfusion 中的线程受服务器版本的限制——我认为标准版最多支持 10 个线程——其他的则排在队列中。向线程提供 "callback" 将完成这项工作。 sql 转储完成后,还可以利用计划任务。