Site Server - Using the Membership Directory and Active User Object (AUO) for Session State Data
This article discusses how you can use the Microsoft® Site Server version 3.0 Membership Directory and Active User Object (AUO) to support session state data in Active Server Pages (ASP)-based Web applications.
This approach has the benefit that it will work across Web farms. In addition, session state data can be stored in the RAM-based dynamic portion of the Membership Directory, yielding higher performance in comparison to storing the session state data in a static database. Furthermore, you can configure AUO to provide access to session state data, resulting in a single, simple interface to all user data.
Note This article assumes that you are familiar with the Membership Directory and AUO, as well as Microsoft® Internet Information Services (IIS) and ASP.
Session state data is information about a user that you want to track during the user's visit to your Web site. Some examples of session state data are:
Whether or not a user has visited a promotional page. If they have not, that can trigger ASP code to show them a highly visible link to that page.
The number of times the user has requested a stock quote during the session. If the number exceeds a certain limit, that can trigger code to set an attribute for that user which indicates they may be interested in the stock market.
A list of stock quotes that the user has requested during the session. At the top of the page, you can have ASP code that provides shortcut links to the latest quotes for those stocks.
The page that the user is currently viewing. You can use this information to generate a list of all users currently online, as well as a list of all users currently viewing a given page.
Session state is inherently temporary; if the user does not request another page within a certain time (for example, 20 minutes), the session expires and all data associated with it is lost. The next time the user requests a page from the site, a new session begins.
IIS and ASP provide script authors with an easy way of managing session state data, using the built-in Session object (see the Microsoft® Windows NT® version 4.0 Option Pack documentation). When a user first requests an ASP page within an application, IIS automatically creates a new Session object for that user in the Web server. You can use this object to store and retrieve arbitrary user properties. You don't need to create the Session object, assign a session cookie to the user, or retrieve the Session object for the current user; this is handled behind the scenes by IIS. To store a variable in the Session object, you simply assign it to a named entry. For example:
Session("visitedPromoPage") = True
To retrieve a session variable, you simply access the entry where it is stored. For example:
<% if Session("visitedPromoPage") = False then %> <a href="PromotionalPage"> CLICK HERE FOR COOL STUFF </a> <% end if %>
The IIS Session objects work great but they have limitations; in particular, they are difficult to use with Web farms. The Session object is stored in RAM on the server computer where it was originally created. If on subsequent requests to the same site the user receives a Web page from a different Web server computer, a new Session object will be created there, with no relation to the original Session object. This is clearly not the desired behavior. There are various workarounds to this problem, as well as other solutions. Full discussion of these approaches and their tradeoffs is outside the scope of this paper; see the paper Managing Session State in a Web Farm (consult the Microsoft® BackOffice® Web site at http://backoffice.microsoft.com/ ).1
This document will discuss one approach not mentioned in that paper.
Storing and Accessing Session State Data
If you encounter the problems mentioned in the previous section while tracking your users session state data, consider this approach: store session state data in the Microsoft® Site Server Membership Directory and to access it with your ASP pages using an Active User Object (AUO). This method is discussed in the remainder of this document.
Using the Membership Directory to Store Session State Data
The Membership Directory can store user and application data for intranet and Internet Web sites that have been built with Microsoft® Internet Information Services (IIS) and Active Server Pages (ASP). Data stored in the Membership Directory can be either static or dynamic. For example, user accounts and personalization profiles are stored as static data. On the other hand, the user's current IP address can be stored as dynamic data because it will change if the user logs on at a different computer at different times. Access to data in the Membership Directory is supported through the Active Directory Service Interface (ADSI). In addition, you can access the Membership Directory over a TCP/IP connection using the standard Lightweight Directory Access Protocol version 3.0.
The Dynamic Directory area of the Membership Directory is a natural place to house session state data. You can configure AUO to automatically create a dynamic object in the Membership Directory for each user visiting the site. Attributes on this object are the user's session attributes. Dynamic objects in the Membership Directory have a time-to-live (TTL), which is specified when the object is created. Users can refresh a dynamic object, resetting its TTL; once the TTL has expired, the object is automatically deleted. This corresponds precisely to the notion of a session. The TTL is the session length; when the TTL, and therefore the session object itself, expires the session is over. Whenever the user visits a page on the site, the dynamic object is refreshed and the session is renewed. When the user does not access a page on the site for a period equal to or longer than the TTL, the session object expires.
The main benefit of this approach is its ability to work in a Web farm environment where users may be bounced from Web server to Web server as they traverse the site. Since the session state data is stored on the common Membership Directory there is only one object for each user session on the site. Another benefit is fault tolerance. Using the IIS built-in Session object, if the Web server containing a user's session data crashes, that session data is lost. Using the Membership Directory to store session data, if a Web server goes down, the user's session state data is preserved. When the load-balancing mechanism redirects the user to a working Web server, pages on that server will be able to retrieve the user's session data from the Membership Directory. Additional fault tolerance can be added by using the dynamic data replication feature of the Membership Directory. The same Membership Directory can have multiple Lightweight Directory Access Protocol (LDAP) services (on separate computers). You can configure these LDAP services to replicate dynamic data amongst each other. If one of the LDAP services crashes the others take over responsibility for managing its dynamic data.
One of the key features of Site Server is Active User Object (AUO). AUO provides you with seamless access to user attributes stored in a variety of stores. AUO is based on a provider model; any store accessible through ADSI can be an AUO data provider. To access session state data using AUO, you need to configure a provider to access the Dynamic Directory portion of the Membership Directory.
First, make sure you have installed a Membership Server with AUO on your Web server computer and mapped the appropriate Web site to it. The Membership Directory can use either Membership Authentication mode or Microsoft® Windows NT® Authentication mode.
Second, you need to choose or create a class of objects for the users' session state data. This can be any class that the Membership Directory supports. You can use an existing class, such as the Member class, or you can define a new class with the attributes that you want as session properties.
One of the Site Server samples, which you can download at http://www.microsoft.com/siteserver/default.asp , is an example of how to use AUO with session state data. The sample is an ASP page designed to work with session state data. If you want to try this ASP page, you must configure a new container, new class, and attributes as shown in the following procedure. Use the Membership Directory Manager (MDM) from the Microsoft® Management Console (MMC) to add containers, classes, and attributes as needed.
To configure your Site Server installation to use the session state data sample:
Under the Membership Directory root node, create a container named ou=SessionStateData
Create an attribute in the schema with the following attributes:
Create a class named SessionState with the following attributes:
Must-have: cn and objectClass
May-have: GUID and HitCount
Parent class: organizationalUnit
Use Microsoft Site Server Service Admin (MMC) to create a secondary AUO provider with the alias SessionState and give it the following properties:
ADS path: computername:LDAPport/o=directoryname/ou=SessionStateData
Schema path: computername:LDAPport/o=directoryname/ou=Admin/cn=Schema/cn=SessionState
Accessing Session State Data Using AUO
On any ASP page where you want to get or set session state data, you need to create an AUO instance for the current user.
Set user = Server.CreateObject("Membership.UserObjects")
This ASP code sets the user variable to the AUO of the user currently accessing the page. We will use this variable in the example later in this topic.
For the static AUO providers, you are now ready to access user properties. For the dynamic provider you have configured for session state, but you need to do a little more work. Because the session object is dynamic, it may not currently exist. Therefore, you need to add code that checks if the user's session object exists, and if the object does not exist, creates it. The code also needs to set the object's TTL to the desired session length. The following example shows how to do this. It assumes the AUO session state provider is named SessionState, and the class for session objects is SessionState.
If Not IsArray(user("SessionState").objectClass) Then ' User doesn't yet exist, so make sure it's dynamic user("SessionState").objectClass = Array("SessionState", "dynamicObject") end if ' set the time-to-live to the desired session length. This is specified in seconds. User("SessionState").entryTTL = 600 ' set to 10 minutes User("SessionState").SetInfo
You may want to refresh the user's session object whenever they visit any page in the site, whether or not that page actually uses session state data. To keep from having redundant code everywhere, you can put the previous code into an .inc file, and include this file on every ASP page.
Now you can access user session properties very much like you would with the built-in Session object. Two pairs of examples follow that compare how you could use the Session object with how you could use AUO and the Membership Directory.
This example sets a user session property with the built-in Session object:
Session("quoteTotal") = 1 Session("currentPage") = Request("SCRIPT_NAME")
This example sets a user session property with AUO and the Membership Directory:
User("ILSsession").quoteTotal= 1 User("ILSsession").currentPage = Request("SCRIPT_NAME")
This example retrieves a user session property with the built-in Session object:
userQuoteTotal = Session("quoteTotal")
This example retrieves a user session property with AUO and the Membership Directory:
userQuoteTotal = User("ILSsession").quoteTotal
Finally, at the end of every page on which you have set any session properties using AUO with the Membership Directory, be sure to call the SetInfo() method on your dynamic provider:
Note If you call SetInfo() at the end of your ASP page, it is technically not necessary to do so in the previously mentioned include file. However, to be safe it is probably best to leave the SetInfo() call in the include file, and call it again at the end of any ASP page where you use session state data through AUO. This way, even if you forget to call SetInfo() on a page that is not using session state data, the user's session object will still be refreshed by the code in the include file.
Differences Between IIS Sessions and Membership Directory Session Objects
As you can see from the previous examples, using AUO for session state data is similar to using the built-in Session object. However, there are three significant differences.
Defining New Session Properties
With the built-in Session object, you can define new properties simply by specifying a new entry name. For example, suppose you want to let each user enter a friendly name to be used in their session. You might do something like this:
Session("userFriendlyName") = Request.Form("FriendlyName")
Defining properties using AUO with the Membership Directory is slightly more work. Before you can use userFriendlyName as a session property, you need to define an attribute, called userFriendlyName, in the Membership Directory schema, then add this attribute to the class being used for session state data. To do this, use the Membership Directory Manager (MDM). Once this is done, you can use the following code:
User("ILSsession").userFriendlyName = Request.Form("FriendlyName")
Searching for Session State Data
One of the biggest differences between using the Microsoft® Internet Information Services Session object and storing session state data in the Membership Directory is that you can search for objects in the Membership Directory. For example, you could generate a list of all the users currently viewing a given Web page. Assuming that the name of each user's current page is stored in the currentPage property as shown previously, the following code will print a list of all users viewing the current page. The following code utilizes the Active Data Object (ADO) to search the Membership Directory.
set conn = CreateObject("ADODB.Connection") conn.Provider = "ADsDSOObject" conn.Open "ADs Provider" queryString = "<LDAP://myserver:389/o=microsoft/ou=SessionStateData>;(&(objectClass=SessionState)(currentPath=" & Request("SCRIPT_NAME") &"));ADsPath,cn;SubTree" Set rs = conn.Execute( queryString, numRecords, 1 ) Do While Not rs.EOF Response.Write rs("cn") & "<br>" rs.MoveNext Loop
Types of User Session Properties
Using the built-in Session object, you can store any type of variable in a session entry, including string, integer, and object types. For example, suppose your ASP page creates a COM object that stores a list of the stocks the user has requested quotes for during the current session. Using the Session object, you can store this COM object for subsequent pages the user accesses.
Set userQuotes= CreateObject("QuoteRequests") ' create COM object userQuotes.addQuote("msft") ' call methods on object userQuotes.addQuote("ibm") Session("Quotes") = userQuotes ' store the object
If you are using the Membership Directory for session state data, this is not quite as easy. The types of values you can store for user attributes depends on the syntax of these attributes, as defined in the Membership Directory schema. The Membership Directory supports five attribute syntaxes: UnicodeString, Integer, GeneralizedTime, Binary, and DistinguishedName. (See the Operations Guide in the Personalization & Membership (P&M) section of the Microsoft Site Server documentation for explanation of these syntaxes.) Typically, your session attributes will have UnicodeString or Integer syntax.
Note The following discussion assumes you are familiar with writing and extending COM objects. If you are not, ignore this section.
In order to store a COM object as a session attribute in the Membership Directory, it needs to be able to persist itself into a syntax suitable for storing as an attribute value in the Membership Directory, such as String or Binary.
Taking the previous example, if you wanted to persist the QuoteRequests COM object in the Membership Directory, you could add a Persist() and Unpersist() method. The Persist() method would return a string that represents the current state of the object. The Unpersist() method would take as an argument a string generated by calling Persist() and restore the object back to the state represented by the string. This is a standard technique used to persist objects to external stores. Your COM classes may already have methods that perform such functions.
Once you have the Persist() and Unpersist() methods, you can easily store a QuoteRequests object as a session attribute in the Membership Directory. Here is code that duplicates the previous example.
Note For this example to work, you will have already defined an attribute named Quotes with UnicodeString syntax in the Membership Directory schema, and added it to the SessionState class.
Set userQuotes = CreateObject("QuoteRequests") ' create COM object userQuotes.addQuote("msft") ' call methods on object userQuotes.addQuote("ibm") 'additional operations on object User("ILSsession").quotes = userQuotes.Persist()
The following code illustrates how a different ASP page can get the user's current QuoteRequests object.
Set userQuotes= CreateObject("QuoteRequests") ' create COM object userQuotes.Unpersist( User("ILSsession").quotes ) ' now you can perform operations on the UserPurchases object userQuotes.addQuote("nscp") userQuotes.listQuotes()
Load and Scaling Issues
When designing your site, you need to take into account issues of load and scaling. Your configuration needs to be such that the anticipated peak load on any one component will not overtax it. When using the Membership Directory to store session state data, the load levels of the Lightweight Directory Access Protocol (LDAP) services will depend on a number of different factors, including:
The number of Web servers with Active User Object (AUO) instances that point to the LDAP service.
The average hits/second for the pages on these Web servers that use the AUO dynamic provider for session state data.
The number of pages on these Web servers that use AUO for session state data.
The average number of reads and writes to the AUO dynamic provider for these pages.
It is recommended that you use a combination of empirical testing and analysis of existing and anticipated loads. For more guidance on configuring your site, consult the Microsoft BackOffice Web site ( http://backoffice.microsoft.com ) for additional white papers and other information.
The basic approach for addressing session state load is to have multiple LDAP services pointing to the same Membership Directory. The AUO instances associated with your Web servers should be balanced across these LDAP services. You will most likely want to have multiple LDAP services for other reasons as well. For example, to balance the load for authentication and personalization based on static attributes, fault tolerance, and so on.
Personalization & Membership (P&M) supports replication of dynamic data among LDAP services that point to the same Membership Directory. Although a user may get bounced across Web servers through round-robining, all of the ASP pages on the Web servers will be able to access the user's session state data using AUO, so long as the AUO instances point to LDAP services that access the same Membership Directory database. The following diagram illustrates this:
Besides load, you should also consider the anticipated peak number of concurrent sessions. This number translates into the maximum number of dynamic objects that the LDAP service must store at any single time. Based upon this number, you need to appropriately configure the maximum number of dynamic objects that the LDAP service allows. You can set this property using Microsoft® Site Server Service Administration (MMC), or using the P&M Service Administration command-line interface.
1 Note that one of the approaches described in Managing Session State in a Web Farm is to use the Microsoft Personalization System (MPS). This product has been succeeded by the Personalization system in Site Server 3.0. If you wish to use a static store approach to storing session state, it is strongly recommended that you use Site Server 3.0.