Design for mvserver, a modular, generic application-server framework.

A picture is worth (roughly) one thousand words

Important things not denoted by this picture

What does mvserver do?

What pieces of data will any server implemented using the mvserver framework need?

English description

Thread pooling

Some application servers launch one thread for each incoming connection. This approach causes each new request to cost at least the cost for spawning, initializing and destroying a new thread, a considerable cost. Also, this practice could allow a denial-of-service attack to succeed.

By pooling threads, we save the cost of thread creation and destruction at each request point. Also, it would be possible to analyze and reject work in the queue if the queue becomes too long (shed load). No such analysis is possible (or such analysis is more difficult) when automatically spawning a thread for each incoming connection. This time is purchased at the cost of having more memory allocated in the form of worker thread stacks. We must also pay the cost of synchronization: exclusive access to the shared queue and maintaining the semaphore to notify worker threads when there is more work.

Abstract interface

In the new model, each entity is a shared library with an advertised entry point. There are two types of entities, RAD and XML entities. RAD entities are lighter-weight than XML entities but do not offer typed arguments (all arguments are considered strings).

Each RAD entity will have the same signature,

void f(work_t *work)
(see 'The abstract notion of "work"' for work_t) which will make entities easier to use and write and allow factoring of common code. This entry point will be associated with an entity name (what we think of as an entity) by calling the procedure named RegisterRADEntity with the entity name. The name of the entry point is inferred. RegisterRADEntity will determine the proper function address through the use of dladdr.

Each XML entity will have the same signature,

void f(XMLRPC_REQUEST pRequest, work_t *work)
(see 'The abstract notion of "work"' for work_t, and xmlrpc docs for a description of XMLRPC_REQUEST) which will make entities easier to use and write and allow factoring of common code. This entry point will be associated with an entity name (what we think of as an entity) by calling the procedure named RegisterXMLEntity with the entity name. The name of the entry point is inferred. RegisterXMLEntity will determine the proper function address through the use of xmlrpc.

The abstract notion of "work"

Work, in the mvserver, is an ADT, work_t. In order for the mvserver to do work, it gives one of its worker threads a pointer to one of these structures, and has it invoke the entity registered (through RegisterAllEntities) for the entity named in the RAD request with a pointer to a work_t structure as an argument.

Treat work_t as an opaque type, using only the functions in work.h:
/* taken from work.h,v 1.6 2000/07/11 01:15:29 mooreb Exp */
work_t *work_new_work(void);
int work_get_unique_id(work_t *work);
const char *work_get_name(work_t *work);
HashTable work_get_entity_arguments(work_t *work);
int work_get_num_entity_arguments(work_t *work);
const char *work_get_rad_body(work_t *work);
int work_get_rad_body_length(work_t *work);
int work_get_socket(work_t *work);
int work_get_is_xml(work_t *work);
int work_get_wants_response(work_t *work);
void work_set_unique_id(work_t *work, int unique_id);
void work_set_name(work_t *work, const char *external_entity_name);
void work_set_entity_arguments(work_t *work, HashTable ht);
void work_set_num_entity_arguments(work_t *work, int num_entity_arguments);
void work_set_rad_body(work_t *work, const char *rad_body);
void work_set_rad_body_length(work_t *work, int rad_body_length);
void work_set_socket(work_t *work, int socket);
void work_set_is_xml(work_t *work, int is_xml);
void work_set_wants_response(work_t *work, int wants_response);
void work_print_work(work_t *work);
void work_free_work(work_t *work);
void work_set_output_string_func(work_t *work, output_string_func f);
void work_set_output_header_func(work_t *work, output_header_func f);
void work_send_output(work_t *work, const char *s);
void work_send_header(work_t *work, const char *name, const char *value);
    

Answers to your burning questions


Brian Moore
Last modified: Wed Mar 21 17:20:23 PST 2001