In certain instances where you have endlessly running/sleeping ‘monitoring’ threads, there is effectively no way to kill them short of a CF server cycle or via the CF8 server monitor.
I had this issue during development where inside an app scoped component I was creating two ‘monitor’ threads, which were charged with monitoring two separate queues, sleeping every few seconds. Whenever the app scope was reinitialized, two more threads would be created, up to the point where all threads were utilized and the app bombed due to the lingering (and idle) threads left from the previous instantiation.
CFThreads are not application specific, they are bound to the server; furthermore, multiple threads with the same name can be created in different requests. In order to attempt to ‘bind’ a thread to a given application, I recommend prefixing all threads with an application-specific unique string prefix; this will allow you to use the prefix argument of the UDF below and effectively kill only those threads which were spawned from a particular application.
Given all this, I created the following UDF which allows you to kill any cfthread, either by name or by prefix string. Since the cfadmin password is required (the udf interacts with the cfadmin api), if you need to embed this routine into an app, you are suggested to alter the code to allow an encrypted password to be passed in which is subsequently decrypted as required before invoking the admin api login method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
Example usage: Kill a specific thread by cfthreadname: <cfset threadMurder(cfadminPassword='XXXXX',cfthreadname='queueMonitor')> Kill a specific thread by threadname (java): <cfset threadMurder(cfadminPassword='XXXXX',threadname='cfthread-25')> Kill a set of threads which share a common prefix: <cfset threadMurder(cfadminPassword='XXXXX',prefix='MyApp_')> And the udf: <cffunction name="threadMurder" access="public" description="forcefully destroys any cfthread which matches the naming criteria; returns the number of cfthreads which were killed" output="false" returntype="numeric"> <cfargument name="cfadminPassword" type="string" required="true"><!--- since we access the cfadmin api, we require the password for login ---> <cfargument name="cfthreadName" type="string" required="false" default=""><!--- if we're needing to kill a specific thread, this is the cfthread name= attribute ---> <cfargument name="threadName" type="string" required="false" default=""><!--- if for some reason we already have the *java* threadname for the cfthread, we prefer to use it instead ---> <cfargument name="prefix" type="string" required="false" default=""><!--- we can also target a set of threads by matching a prefix against each cfthread's name ---> <!--- Forcibly kills any cfthread @param cfadminPassword The cfadministrator password (required) @param cfthreadName If killing a single thread and you know its cfthread name= attribute, use this @param threadName If killing a single thread and you already know its java threadname, use this @param prefix If you want to kill a set of threads which have a common prefix string, use this @return numeric Returns the number of threads killed @author Kevin J. Miller (kmiller@websolete.com) @version 1.0 @date 12/9/2008 ---> <cfset var local = structnew()> <cftry> <!--- validate input ---> <cfif not len(arguments.cfthreadname) and not len(arguments.threadname) and not len(arguments.prefix)> <cfthrow type="InvalidArguments" message="You must specify either a cfthreadname, threadname (java name) or a prefix."> </cfif> <cfscript> local.adminObj = createobject("component","cfide.adminapi.administrator").login(arguments.cfadminPassword); local.monitorObj = createobject("component","cfide.adminapi.servermonitoring"); local.threads = local.monitorObj.getAllActiveCFThreads(); local.threadsKilled = 0; if(arraylen(local.threads)) { for(local.i = 1; local.i lte arraylen(local.threads); local.i++) { local.mycfthreadname = local.threads[local.i].cfthreadname; local.mythreadname = local.threads[local.i].threadname; // declared cfthreadname if(len(arguments.cfthreadname) and not comparenocase(local.mycfthreadname,arguments.cfthreadname)) { local.homicide = local.monitorObj.abortCFThread(local.mythreadname); if(local.homicide) { local.threadsKilled = 1; break; } } // declared threadname (java) else if(len(arguments.threadname) and not comparenocase(local.mythreadname,arguments.threadname)) { local.homicide = local.monitorObj.abortCFThread(local.mythreadname); if(local.homicide) { local.threadsKilled = 1; break; } } else if(len(arguments.prefix) and not comparenocase(left(local.mycfthreadname,len(arguments.prefix)),arguments.prefix)) { local.homicide = local.monitorObj.abortCFThread(local.mythreadname); if(local.homicide) { local.threadsKilled++; } } } } return local.threadsKilled; </cfscript> <cfcatch> <cfrethrow> </cfcatch> </cftry> </cffunction> |