View Single Post
  #42 (permalink)  
Old 02-21-2008, 01:40 AM
prasannavigneshr prasannavigneshr is offline
D-Web Incredible
 
Join Date: Feb 2007
Posts: 1,321
prasannavigneshr is on a distinguished road
Send a message via MSN to prasannavigneshr
Thumbs up ColdFusion Tips & Tricks - Passing variables between clustered servers

Passing variables between clustered servers


The problem with running an application in an clustered environment is that when you update an Application scope variable on one box, the change does not propagate to the other boxes. The SendApplicationVar( ) UDF below handles it for you.

First, we need to set up a few Request variables. Ideally, these would exist in your environment.cfm template.

First, we need to configure the URL's for the different clustered servers - use the actual name of the server (or the IP address), not the shared name. For single server environments (such as dev/test/uat), set it to your server's name/IP and port. If the port is 80, you may leave it out. If you run multiple applications under the same port (such as 8500), enter the path of the application after the ip/port number.

Code:
<!--- Single server configuration --->
<CFSET Request.ClusterList="127.0.0.1:9091">
<!--- Single server, shared port configuration --->
<CFSET Request.ClusterList="127.0.0.1:8500/myappdir">
<!--- Multiple server configuration --->
<CFSET Request.ClusterList="iwof1.fubar.com,iwof2.fubar.com,iwof3.fubar.com">
For security, there needs to be two Request variables that contains a User ID and Password that is the same on all boxes. It doesn't have to be a real User ID or even values that make any sense, as long as they match. If they do not match, no update will take place.
Code:
<CFSET Request.AdminRoot="iwantmy">
<CFSET Request.AdminPasswd="moogie">
Now that we have the configuration out of the way, we can send the variable(s)! Note that you don't have to set the local Application variable, this process will set it as well.

Once you CFINCLUDE the UDF (or have it coded in-line), the below code will propagate a variable and a structure to all of the servers.

Code:
<cfscript>
Out=StructNew();
Out.TestVar=StructNew();
Out.TestVar.Foo="Strong";
Out.Bar="FUBAR";

SendApplicationVar(Out);
</cfscript>
The way it works is that you create a structure that contains all of the variables, structure, arrays, query objects - whatever - and pass that structure to the SendApplicationVar( ) function. The function will convert the structure into a WDDX packet and pass it to each of the clustered servers. The CFM template that receives the WDDX packet deserializes it and checks the authentication information. If it passes, it sets the Application variables passed to it. Note that if your application forces users to log in, the URL specified in SendApplicationVar( ) must exist in an "isolated" directory - a directory that allows code to run without a user being logged in. This directory should have it's on Application.cfm template that contains a matching <CFAPPLICATION> tag and a CFINCLUDE to your environment.cfm template. Also note that since the authentication is stored in a structure called "Auth", you can't pass any variables using that name (unless you change the name).

SendApplicationVar( ) will return a 1 if all servers were updated successfully, 0 otherwise.


Example HTML/CFML code:

Code:
<cffunction name="SendApplicationVar" access="private" returntype="string">

<cfargument name="InStruct" type="struct" required="yes">

<cfscript>

var Results="";
var OutStruct=StructNew();
var ErrorCode=1;
// Add authentication
OutStruct.Auth=StructNew();
OutStruct.Auth.UserID=Request.AdminRoot;
OutStruct.Auth.Password=Request.AdminPasswd;
OutStruct.Application=Duplicate(Arguments.InStruct);

</cfscript>

<CFPARAM name="Request.ClusterList" default="">
<CFIF Len(Trim(Request.ClusterList)) GT 0>

    <cfwddx action="CFML2WDDX" input="#OutStruct#" output="OutWDDX" usetimezoneinfo="Yes">

    <CFLOOP index="CurrBox" list="#Request.ClusterList#">
        <!--- Send the WDDX packet to the remote servers, change path to suit -->
        <cfhttp url="http://#CurrBox#/common/isolated/receive_var.cfm" method="POST">
            <cfhttpparam type="FORMFIELD" name="InVar" value="#OutWDDX#">
        </cfhttp>
        <CFIF Left(CFHTTP.FileContent,2) NEQ "Ok">
            <CFOUTPUT>#CurrBox#: #CFHTTP.FileContent#<br></CFOUTPUT>
            <CFSET ErrorCode=0>
        </CFIF>
    </CFLOOP>

    <CFRETURN ErrorCode>

</CFIF>

</cffunction>
***************
receive_var.cfm
***************
Code:
<CFPARAM name="FORM.InVar" default="">

<CFIF Left(FORM.InVar,20) NEQ "<wddxPacket version=">
    <CFOUTPUT>Invalid WDDX Packet</CFOUTPUT>
    <CFABORT>
</CFIF>

<cfwddx action="WDDX2CFML" input="#FORM.InVar#" output="InStruct">

<CFIF IsStruct(InStruct) EQ "NO">
    <CFOUTPUT>Invalid Structure</CFOUTPUT>
    <CFABORT>
</CFIF>

<CFIF IsDefined("InStruct.Auth.UserID") EQ "NO" OR IsDefined("InStruct.Auth.Password") EQ "NO">
    <CFOUTPUT>Invalid Authentication</CFOUTPUT>
    <CFABORT>
</CFIF>

<CFIF InStruct.Auth.UserID NEQ Request.AdminRoot OR InStruct.Auth.Password NEQ Request.adminpasswd>
    <CFOUTPUT>Invalid Authentication</CFOUTPUT>
    <CFABORT>
</CFIF>

<CFIF IsDefined("InStruct.Application")>
    <CFSET KeyList=StructKeyList(InStruct.Application)>
    <cflock timeout="30" throwontimeout="Yes" type="EXCLUSIVE" scope="APPLICATION">
        <CFLOOP index="CurrKey" list="#KeyList#">
            <CFSET "Application.#CurrKey#"=Duplicate(Evaluate("InStruct.Application.#CurrKey#"))>
        </CFLOOP>
    </cflock>
</CFIF>

<CFOUTPUT>Ok</CFOUTPUT>
__________________
Prasanna Vignesh
MCPD | Web Developer
Reply With Quote