【发布时间】:2014-08-19 07:42:30
【问题描述】:
我有一个交易虚拟物品的应用程序,并且有一个页面可以获取我的所有帐户,并为每个帐户创建一个线程,该线程首先登录帐户,然后在会话处于活动状态时搜索和购买物品。在此我应该指出,这是我第一次使用 cfthread。
我遇到了问题。每 30 分钟(如果不是更少)我的 ColdFusion 服务器就会停止,我必须重新启动服务。重新启动服务后,我检查日志,发现“超出 GC Overhead Limit Exceeded”的错误。
我在网上进行了广泛的研究,但 cfthread 对我来说是新的,JVM 及其运行方式也是如此。我在 CF10 企业版上运行并加载了服务器监视器,我可以看到 JVM 内存使用量不断增长,直到达到限制(刚才我将它设置为 2gb,就像我设置更高时一样内存似乎填得更快)。即使我在监视器中选择了 Run GC 选项,它也不会显着减少内存使用量。
这很可能与我的代码有关吗?目前,我创建了不到 50 个线程,但随着我向应用程序添加更多帐户,需要的线程也就越多。
这是页面中的代码...
<script>
/* RELOAD PAGE EVERY 65 MINUTES */
setTimeout(function(){
window.location.reload(1);
}, 3900000);
</script>
<!--- GET ACTIVE ACCOUNTS --->
<cfquery name="getLogins" datasource="myDB">
SELECT * FROM Logins WHERE active = 1
</cfquery>
<!--- LOOP THROUGH ACCOUNT --->
<cfloop query="getLogins">
<!--- HAVE A SLEEP SO IP DOESN'T GET FLAGGED FOR SENDING TOO MANY REQUESTS AT ONCE --->
<cfset Sleep(30000) />
<!--- CREATE THREAD FOR ACCOUNT --->
<cfthread
name="#getLogins.accountName#"
action="run"
accountName="#Trim(getLogins.accountName)#"
email="#Trim(getLogins.email)#"
password="#Trim(getLogins.password)#"
resourceId="#Trim(getLogins.resourceID)#">
<!--- DEFAULT SESSION VARIABLES --->
<cfset SESSION["#attributes.accountName#LoggedIn"] = 0 />
<cfset SESSION["#attributes.accountName#LoginAttempts"] = 0 />
<!--- WHILE ACCOUNT NOT LOGGED IN AND LESS THAN 8 LOGIN ATTEMPTS MADE --->
<cfscript>
while (SESSION['#attributes.accountName#LoggedIn'] EQ 0 AND SESSION['#attributes.accountName#LoginAttempts'] LT 8) {
// ATTEMPT LOGIN
THREAD.logInAccount = Application.cfcs.Login.logInAccount(attributes.email,attributes.password);
// IF LOGIN ATTEMPT UNSUCCESSFUL
if (THREAD.logInAccount EQ 0) {
// INCREASE ATTEMPT COUNT
SESSION['#attributes.accountName#LoginAttempts'] = SESSION['#attributes.accountName#LoginAttempts'] + 1;
}
// ELSE IF RETURNED VALUE IS 481 THEN ACCOUNT IS LOCKED
else if (THREAD.logInAccount EQ 481) {
// SET LOGIN ATTEMPT COUNT TO STOP LOOP
SESSION['#attributes.accountName#LoginAttempts'] = 8;
// UPDATE ACCOUNT TO MARK AS LOCKED
THREAD.updLogin = Application.cfcs.Login.updLogin(attributes.email);
}
}
</cfscript>
<!--- IF ACCOUNT LOGGED IN --->
<cfif SESSION['#attributes.accountName#LoggedIn'] EQ 1>
<!--- SET ID FOR SEARCHING --->
<cfset THREAD.definitionID = attributes.resourceID - 1610612736 />
<!--- WHILE ACCOUNT LOGGED IN --->
<cfloop condition="SESSION['#attributes.accountName#LoggedIn'] EQUALS 1">
<!--- GET LATEST LOWEST BUY NOW PRICE --->
<cfquery name="THREAD.getMinBIN" datasource="FUT" cachedWithin="#CreateTimeSpan(0,0,1,0)#">
SELECT TOP 1 * FROM v_FUT14BINPrices WHERE resourceID = #attributes.resourceId# ORDER BY lastUpdated DESC
</cfquery>
<!--- INCLUDE FILE THAT CALCULATES BUYING AND SELLING PRICES --->
<cfinclude template="sellingPrices.cfm" />
<!--- IF BIDDING PRICE HAS BEEN SET --->
<cfif StructKeyExists(THREAD,"biddingPrice")>
<!--- MAKE SEARCH REQUEST, TIMING THE REQUEST --->
<cfset THREAD.requestStart = GetTickCount() />
<cfset THREAD.search = Application.cfcs.Search.dosearchOld(attributes.resourceId,THREAD.biddingPrice,0) />
<cfset THREAD.requestDuration = GetTickCount() - THREAD.requestStart />
<!--- IF SEARCH CONTAINS FILE CONTENT --->
<cfif StructKeyExists(THREAD.search,"FileContent")>
<!--- DECLARE NUMBER OF RESULTS VARIABLE --->
<cfset THREAD.numResults = 0 />
<!--- IF JSON RETURNED --->
<cfif IsJSON(THREAD.search.FileContent)>
<!--- DESERIALIZE JSON --->
<cfset THREAD.searchResults = DeserializeJSON(THREAD.search.FileContent) />
<!--- IF PLAYER SEARCH RETURNS AUCTIONINFO STRUCT --->
<cfif StructKeyExists(THREAD.searchResults,"auctionInfo")>
<!--- SET NUMBER OF CARDS RETURNED FROM SEARCH --->
<cfset THREAD.numResults = ArrayLen(THREAD.searchResults.auctionInfo) />
<cfset THREAD.statusCode = "Successful" />
<cfif THREAD.numResults EQ 0>
<cfset THREAD.statusCode = "Successful - No Results" />
</cfif>
<!--- ELSE IF ERROR CODE RETURNED --->
<cfelseif StructKeyExists(THREAD.searchResults,"code")>
<cfset THREAD.statusCode = THREAD.searchResults.code />
<!--- IF CODE 401 THEN SESSION HAS EXPIRED --->
<cfif THREAD.statusCode EQ 401>
<!--- SET SESSION AS LOGGED OUT AND ATTEMPT SESSION REFRESH --->
<cfset SESSION['#attributes.accountName#LoggedIn'] = 0 />
<cfset THREAD.logInAccount = Application.cfcs.Login.logInAccount(attributes.email,attributes.password) />
</cfif>
<!--- ELSE SOMETHING ELSE HAS HAPPENED --->
<cfelse>
<cfset THREAD.statusCode = "Something Else - " & THREAD.searchResults.code />
</cfif>
<!--- IF RESULTS RETURNED --->
<cfif THREAD.numResults GT 0>
<!--- LOOP ROUND RESULTS AND CHECK IF MATCH BUYING CRITERIA --->
<cfloop index="i" from="1" to="#THREAD.numResults#">
<!--- ***SAFETY CHECK*** - ENSURE ID OF CURRENT CARD IS SAME AS ONE SEARCHING FOR --->
<cfif THREAD.searchResults.auctionInfo[i].itemData.resourceID EQ attributes.resourceId AND THREAD.getMinBIN.resourceID EQ attributes.resourceId>
<!--- ENSURE BIN PRICE SET AND IS LESS THAN SET BUYING PRICE --->
<cfif THREAD.searchResults.auctionInfo[i].buyNowPrice GT 0 AND THREAD.searchResults.auctionInfo[i].buyNowPrice LTE THREAD.biddingPrice>
<!--- SET AUCTION END TIME --->
<cfset THREAD.timeLeft = THREAD.searchResults.auctionInfo[i].expires />
<cfset THREAD.auctionEnds = DateAdd("s",THREAD.timeLeft,Now()) />
<!--- BUY CARD --->
<cfset THREAD.buyCard = Application.cfcs.Bid.doBIN(THREAD.searchResults.auctionInfo[i].tradeID,THREAD.searchResul ts.auctionInfo[i].buyNowPrice,THREAD.searchResults.auctionInfo[i].startingBid,THREAD.searc hResults.auctionInfo[i].itemData.ID,THREAD.searchResults.auctionInfo[i].itemData.resourceI D,THREAD.startPrice,THREAD.binPrice,THREAD.lowestBIN,THREAD.searchResults.auctionInfo[i].i temData.discardValue,THREAD.auctionEnds,THREAD.requestStart,THREAD.requestDuration) />
</cfif>
</cfif>
</cfloop>
</cfif>
<cfelse>
<cfset THREAD.statusCode = THREAD.search.FileContent />
</cfif>
<cfset THREAD.sleepDuration = 1000 - THREAD.requestDuration />
<cfif THREAD.sleepDuration GT 0><cfset Sleep(THREAD.sleepDuration) /></cfif>
</cfif>
<!--- INSERT SEARCH RECORD --->
<cfset THREAD.insSearchRecord = Application.cfcs.Search.insSearchRecord(THREAD.definitionID,THREAD.statusCode,THREAD.requ estDuration,THREAD.numResults,THREAD.biddingPrice) />
</cfif>
</cfloop>
</cfif>
</cfthread>
</cfloop>
我原以为内存会保持相同的使用率,因为每个循环都执行相同的一组操作,所以一旦循环回到开始,我认为前一个循环会从内存中删除(释放空间),然后执行相同的操作,因此相同的内存总量将被用完,但似乎每个循环都保存在内存中,这就是它不断增长的原因。
有人可以帮助我并就如何解决此问题提供一些指导吗?如果您需要更多信息,请告诉我
提前致谢
【问题讨论】:
-
我认为您需要完全重新考虑您的架构,因为 CF 只会在请求/线程结束时从内存中清除变量。基本上你在做什么是错的。不太确定您要实现什么,但也许运行每个 X 并创建新请求/线程的计划任务会更好,因此请求/线程实际上可以结束。
-
如上所述,我想登录每个帐户,并且在每个帐户的会话处于活动状态时,我想在不超过 1 RPS 阈值的情况下发送尽可能多的搜索和购买请求。在解决这个多线程解决方案之前,我不确定我可以拥有什么其他结构,因为我已经和其他人搞砸了。不过,我愿意接受建议和指导。
-
每分钟运行一次并且每个登录用户执行 40 - 50 个请求的计划任务怎么样。您可以在 Application 范围内存储登录用户的结构。
-
除非您有 50 个 CPU 内核,否则您实际上会损害性能。此外,循环不会“从内存中删除”。显然,每次迭代都将永远不会删除的变量留在堆中。您还可能使用大量 CPU,因为使用“while”循环等待事件的效率非常低。您实际上是在构建一个投标网站,还是只是想使用 CFML 向另一个网站发送请求?
-
@BradWood 我正在使用 CFML 向托管拍卖行的另一个站点发送请求。有没有办法找出哪些变量留在堆中,以便在每次迭代结束时将它们删除?
标签: multithreading memory coldfusion garbage-collection jvm