I've recently added a new dispatcher to the PLT Web Server: dispatchers/limit.
It allows you to limit the number of concurrent requests handled by a particular sub-dispatcher. For example, you could limit the number of active servlet requests, but have limitless file-system requests. The documentation gives a good example of using it.
One of the interesting features is the ability to control what happens to requests that occur after the limit has been exceeded. You can choose to (a) make them wait until there is space, (b) cause them to instantly fail, or (c) kill old request handlers and prioritize the new ones.
The code that implements this dispatcher is a good example of using the synchronization primitives of PLT Scheme (inherited from Concurrent ML.) Take a look at the code. You'll notice a few things:
- The manager of the resources is abstracted into a separate thread, created at the time the dispatcher is constructed.
- The users of limited dispatcher communicate with the manager through private synchronized channels that are invisible to the rest of the Web Server.
- When a limit request is made, it is sent to the manager with a new first-class reply channel, so that the thread can receive a go-ahead to continue. Notice that this is not the case when the limited request completes: it doesn't need to wait for confirmation.
- Because the channels of communication to the limit manager are completely controlled in this short function, the users can be relied upon to implement the load-shedding behavior. (See how if the reply channel communicates
false, then the thread errors, freeing up resources.) - The manager is implemented as tail-recursive loop, storing the changing state (the current limit and the active requests) in lexical variables.
- The
syncfunction is repeatedly called to decide what activities the manager does at each step.syncwill choose one of the available events each time it is called. - Based on whether the limit has reached it maximums, the manager chooses between allowing new requests in, or implementing the over-limit behavior. It can cause the new threads to block simply by ignoring the incoming request channel (implemented by returning the
never-evt, rather than a handler based on it. - In either case, the manager will wait for requests to finalize and free-up their resources.
- Because request handlers might die unexpectedly if their connection to the Web client is broken, the manager needs to monitor active threads and free their resources if they die. Therefore, it constructs an event for each partner that handles that partners death. This is why we use
applyto callsync, rather than calling it directly.
This dispatcher would be much more painful to implement if we did not have the ability to use first-class channels, closures, variable-arity functions, or CML-style synchronization operators.
2 comments:
Hello Jay - very cool!
I have been playing with the PLT Web Server stack off and on for a long while, but I have not had the opportunity to use it on a real project. Can PLT Web Server apps run for long periods without restarting processes, etc.? I use Rails a lot but I don't have the good feeling for the "longevity" of Rails apps, compared to Java web apps that run for years unattended.
Do you have ay experience with very long running PLT Web Server apps?
Thanks,
Mark
Hi Mark,
The biggest problem with running the PLT Web Server for long periods of time is that the continuations behind each link live on the server. The default behavior a few years ago was to keep these continuations around until a timeout happened. It was basically impossible to choose a good timeout.
Now, the default is to use an LRU manager. This manager is great under load. In the applications I've had experience with, the only restarts were for machine upgrades and problems outside our control.
That said, the LRU manager needs a lot of memory to run well. If you don't want to do that, then you can write in the stateless language. You get basically the same API, but all the state is stored on the clients. In one of our applications, we went from a 528MB server process to a 20MB server process.
If you have anymore questions, just shoot me an email.
Jay
Post a Comment