In this article I am presenting one solution for duplicating content across a
Web farm.
If you're familiar with highly available systems that run on a Web farm; you
have multiple machines serving content for your Website. The most common configuration
is where you have your development machine, and once you're done with development
you place the content on a middle tier server called a staging server. Once content
is on the staging server, it is then replicated across the members of the production
Web farm. This configuration provides a great amount of performance and redundancy
for the Web site. The major drawback is if you want people to be able to upload
data to the Web server, usually you have to use some kind of database solution to
store the files in a centralized database rather than on the Web server itself.
This is due to the fact you are never sure which Web server the file will be uploaded
to, and when replication occurs again, it may delete the file since it does not
exist on your staging server.
In this article we are going to present a .Net procedure that accepts the path
information of a file that has been placed on a Web server, and then it copies the
file to other servers via a share name. The routine will check to see which server
in the share name array it is and replicate the content to other machines in the
array.
In this example we will need to specify a delimited string which will be our
paths to the shares which are members of the farm. In addition to this we will specify
a privileged account that will be used to access the share. Modify the
appSettings section of the Web.config
file to contain the following:
<add key="ADsUserName"
value="domain\accountname" />
<!--To be replaced with your network setting-->
<add key="ADsPassword" value="password" />
<!--To be replaced with your network setting-->
<add key="FarmMembers" value="\\server1\share$;\\server2\share$" />
<!--Enter a semicolon delimited string of share paths to directory of
the application root of your web, for example if your root is located
on c:\InetPub\wwwroot\ you could use
\\server\c$\Inetpub\wwwroot\
as your share as long as the account has admin privs to use dollar shares --->
The next thing we need to do is create a WebFarm.vb class which we will use to call
from our aspx pages. Create the class and then import the System.IO class for
manipulating files, System.Security.Principal for account impersonation, and
System.Configuration for working with our web.config file.
Imports System.IO
Imports System.Security.Principal
Imports System.Configuration
Next make a declaration to the advapi32..dll API, this will allow us to execute
the code under a privileged account that has permissions to all the server shares
that were specified.
Public Class Logon
Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal lpszUsername As String, _
ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As Integer, _
ByVal dwLogonProvider As Integer, ByRef phToken As Integer) As Boolean
End Class
Next declare some constants to be passed to the Logon class, whether to logon
as an interactive user, or using clear text. In our example here we will be logging
on as an interactive user to access the share on the network
Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Const LOGON32_LOGON_NETWORK_CLEARTEXT As Integer = 3
'This parameter causes LogonUser to create a primary token.
Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Private DomainName As String
Private UserAccount As String
Private Password As String
Private CurrentMachine As String = System.Environment.MachineName
Private aryShares As String()
Here we call a routine that is going to populate the variables we declared above.
The variables will be populated with the username and password we're going to use
to access the share, and create an array of the shares that we want files replicated
to.
'GetFarmValues
'Description: Used to parse out information defined in the
' Web.config file for account information and shares.
Private Sub GetFarmValues()
'points to the root directory of the application.
Dim strServers As String = ConfigurationSettings.AppSettings("FarmMembers")
aryShares = Split(strServers, ";")
Password = .Configuration.ConfigurationSettings.AppSettings("ADsPassword")
UserAccount = Mid(ConfigurationSettings.AppSettings("ADsUserName"), _
InStr(ConfigurationSettings.AppSettings("ADsUserName"), "\") + 1)
DomainName = Left(ConfigurationSettings.AppSettings("ADsUserName"), _
InStr(ConfigurationSettings.AppSettings("ADsUserName"), "\") - 1)
End Sub
The following procedure accepts InPath as the local path that the file is being
copied to. InFile is the name of the file that we want to copy. And InAppRoot is
the application root directory of the path. We use this to determine the path for
the share to its web directories.
Public Sub CopyFileToShare(ByVal InPath As String, ByVal InFile As String, _
ByVal InAppRoot As String)
Dim sDestination As String
Dim hToken As Integer
Dim iPtrToken As IntPtr
GetFarmValues()
Dim LocalPath As String = InPath & InFile
Dim i As Integer
Dim loggedOn As Boolean = Logon.LogonUser(UserAccount, DomainName, Password, _
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken)
If loggedOn Then
iPtrToken = New IntPtr(hToken)
Dim ImpersonatedIdentity As New WindowsIdentity(iPtrToken)
Dim MyImpersonation As WindowsImpersonationContext = ImpersonatedIdentity.Impersonate()
'do routine.
Try
For i = 0 To UBound(aryShares)
'check out the share in the array against the current machine.
If InStr(LCase(aryShares(i)), LCase(CurrentMachine)) = 0 Then
'this is a different machine than the local machine.
'we need to figure out the root of the servers.
sDestination = Replace(InPath, InAppRoot, aryShares(i)) & InFile
'if it's there delete it first.
File.Delete(sDestination)
File.Copy(LocalPath, sDestination)
End If
Next
Catch ex As Exception
'file copy failed.
End Try
MyImpersonation.Undo()
End IfEnd Sub
Then from our aspx file for example, if we had an upload routine that accepted
a file from a user via the Web, once the file was uploaded to the server, we would
immediately call our newly created class and copy that file to other servers in
our share array:
Dim objWebFarm As New WebFarm
objWebFarm.CopyFileToShare(Request.MapPath("c:\inetpub\wwwroot\myapp\", _
strFileName, Server.MapPath(Request.ApplicationPath))
objWebFarm = Nothing
By:
Patrick Santry, MCT, MCSE, MCSA, MCP+SB, contributor of "Windows 2003 Server
- The Complete Reference", Co-Author of
"Administering IIS 5.0" both books published by McGraw-Hill. In
addition, Patrick has written several articles and co-authored several books on
Microsoft certification. You can view his personal website at
http://www.santry.com/.