|
|
"Dig within. Within is the wellspring of Good; and it is always ready to bubble up, if you just dig."
|
|
--
Marcus Aurelius
|
|
In the days of static content, web sites were ideally scalable -- or at least,
it was as close to ideal as technology allows. If a site outgrew its capacity,
another server could be added to the infrastructure. This architecture scales
outward. That's because each instance is self contained
-- there's no cross talk between servers and as far as the user or the
architecture is concerned, it doesn't matter if there are 1 or 1,000 servers
behind the scenes serving content. A typical diagram may look something like
this:
In this type of architecture, the largest problem is scaling a load balancing
mechanism; generally, though, that's more of an issue of redundancy.
While of course our solutions must be somewhat scalable, we need them to be
redundant, too. A single server may very well handle the load, but it's a scary
world if it's the only thing between being on or offline.
While scaling outward is as close to ideal as we can hope for, it's not easily
obtained with web applications. Since the web is inherently stateless, we rely
on building an infrastructure to support data, dynamic content, and of course
user state. In most cases, these solutions scale upward.
That's because a server needs context to respond to a request, and a
group of servers need to be able to share data.
That's where scalability takes a hit. In a data driven application, an
architecture may look something like:
Data needs to be equally available to all servers, but a single database server
may collapse under the pressure (not to mention the fact that it offers no
redundancy). It's easy to see the problem in the above diagram. That's where
clustering helps -- but even in an active-active configuration, the biggest
bang for the buck for performance improvements come from throwing more hardware
at the cluster -- more CPU, more RAM, faster disks; in other words, scaling
upwards.
Replication may help scale out a solution, but for geographically balanced
environments, replication can only take you so far.
If you're trying to persist user information from request to request, it's
clear that an extensible database solution is 1) not easy, 2) not cheap, and 3)
requires a lot of maintenance. Prior to ASP.NET, developers had to roll their
own solution for managing state beyond a single server instance. .NET offers
two new methods for storing state: State Server and SQL. Both offer advantages
and disadvantages -- and while both are great options, they still present
scalability issues. In fact, for SQL Servers already under load, throwing user
state on top of it is just too much.
The StructureTooBig StateManager was designed to solve the user state
scalability and redundancy issues in a secure way. It does this by relying on
each client to maintain a copy of their own state in an encrypted form, easing
the pressure from the backend to maintain state. Even for single web servers,
this solution has many advantages over typical (in process) session management.
I thought you'd
never ask!
The module plugs itself (via the web.config) into the HTTP pipeline. If the
user already has a session, the state itself is serialized and the resulting
serialized text is then encrypted and stored in an in-memory session cookie on
the user's machine. When the next request is made, the module is invoked by the
Http Application. The State object within the module attempts to decrypt the
cookie, and then deserialize the state (see diagram to the right). Assuming
this is successful, the state is rehydrated as it was on the last request.
When the page has completed processing, the module serializes the StateManager
and then encrypts the contents back into the session cookie. If any of the
fields or properties in the StateManager are marked with the [Persisted]
attribute, they'll be serialized and encrypted into a persisted cookie on the
users machine. Next time the user visits the site, they'll automatically
rehydrate into the StateManager object.
You'd think, but it holds up pretty darn well. Of course, InProc session state
will win hands down in any contest in terms of speed, but it's also the most
limited. On a single IIS box with no database usage except on the first hit,
InProc could churn about 240 requests per second on my test application. That's
really impressive on my hardware. But it's also not very "real-world." With an
IIS box connected to a dedicated State Server (gigabit LAN), we still got an
impressive 195 requests per second. An IIS box connected to a dedicated SQL
State Server processed around 185 requests per second. The StructureTooBig
StateManager, with full AES encryption, delivers around 190 requests per
second. Without encryption, the number jumps to 225. With no persisted values
(values serialized to a persisted cookie) the number hovers around 205 (the
session data is still encrypted).
With multiple servers, the numbers change. InProc yields the same output, but
unless you're using sticky server assignments, it's not real. The dedicated
State Server performance on each machine starts to dive to about 80% on each
box. SQL performance scales back similarly. This decrease continues as more
load is introduced. The StructureTooBig StateManager numbers remain the same because
the design uses each user as a distributed database.
This is what I mean when we talk about the advantages of scaling out
vs. scaling up.
I developed this module specifically because I'm on a single server! First, if
you know the application will always live on a single server, and
you own and manage the server, stick with InProc session state.
If you've got a website that's hosted with a bazillion other websites, this may
be just what you need. While I'm happy with my hosting for what I pay, I have
no control over what everyone else is doing. This triggers a large number of
Application Pool recycles -- meaning everyone's session state is lost. Just
look at my Server Stats page that
shows the number of recycles -- I've seen it as high as 90 recycles in one day!
That's nearly 1 every 15 minutes! If I had a shopping cart or high volume site,
this would be infuriating and would no doubt affect sales.
This may also be a good solution if you're currently using InProc session
state, but want to design your application with scalability in mind for future
growth.
Now, with this solution, it's possible to have sessions live indefinitely if
you wanted to -- 100% independent of Application Recycles. The Session Timeout
is now a matter of preference, rather than a balancing act between resources on
the server and user experience. The session is only lost if the user closes
their browser, or if the encryption keys are changed (something you control and
generally speaking, not needed).
|
|