Developing flexible and high-performance Web servers with frameworks and patterns

The goal of this paper is to illustrate how frameworks and patterns address complexities that arise in the design and implementation of high-performance distributed software systems. These complexities are both inherent (e.g., latency reduction and throughput preservation), and accidental (e.g., the continuous reinvention of key concepts and components). This paper explains how complexities occurring in the development of high-performance Web servers can be alleviated with the use of design patterns and object-oriented application frameworks. These techniques were applied to the development our high-performance adaptive Web server framework, JAWS. JAWS exemplifies how a framework can remain flexible without sacrificing performance. 1 Applying Patterns and Frameworks to Web Servers Developers of Web servers strive to build fast, scalable, and configurable systems. This paper describes some common pitfalls encountered by these developers and how to avoid these pitfalls. Common pitfalls include (1) coping with tedious and error-prone low-level programming details, (2) lack of portability, and (3) the complexity of navigating the wide range of server design alternatives. By carefully utilizing patterns and frameworks, these hazards can be avoided, by allowing developers to leverage reuse of design and code. 1.1 Common Pitfalls of Developing Web Server Software Web servers perform the following tasks: connection establishment, service initialization, event demultiplexing, event handler dispatching, interprocess communication, memory management and file caching, static and dynamic component configuration, concurrency, synchronization, and persistence. In most Web servers, these tasks are implemented in an ad hoc manner using low-level native OS application programming interfaces (APIs), such as Win32 or UNIX/POSIX, which are written in C. Unfortunately, native OS APIs are not an effective way to develop Web servers or other types of communication middleware and applications [1]. The following are common pitfalls associated with the use of native OS APIs: Excessive low-level details: Building Web servers with native OS APIs requires developers to have intimate knowledge of low-level OS details. Developers must carefully track which error codes are returned by each system call and handle these OS-specific problems in their servers. Such details divert attention from the broader, more strategic issues, such as protocol semantics and server structure. For example, UNIX developers who use the wait system call must distinguish between return errors due to no child processes being present and errors from signal interrupts. In the latter case, the wait must be reissued. Reinvention of incompatible programming abstractions: A common remedy for the excessive level of detail with OS APIs is to define higher-level programming abstractions. For instance, many Web servers create a file cache to avoid accessing the filesystem for each client request. However, these types of abstractions are often rediscovered and reinvented independently by each developer or project. This ad hoc devel-