Architectures for Distributed Internet Services

Jonas Otto

2023

Architectures for Distributed Internet Services

This is my summary for the lecture “Architectures for Distributed Internet Services”, as held by Franz Hauck and Benjamin Erb in 2022 at Ulm University.

The HTML version of this summary can be found at https://ottojo.github.io/ADIS_Summary/, the sources are on GitHub at https://github.com/ottojo/ADIS_Summary.

Static Web Content

Web Servers

Static Content

Architectures

multi-thread / multi-process

Solutions for the C10k Problem

Reactor Pattern

Dynamic Web Content

Client-side dynamicity

Server-side dynamicity

Creating dynamic web content

Server Side Includes

External Programs: CGI

Drawbacks:

Alternative: FastCGI

SCGI (Simple Common Gateway Interface): Alternative to FastCGI

Problems with CGI

Server-side Scripting

Scenario: Static HTML page should be enriched by dynamic content.

Here, inside-out scripting is considered. This means scripts are placed inside HTML document. The script output is inserted at their location in the HTML document.

Execution of the scripts:

Inside-out scripting allows HTML-centric development approach.

Self-processing pages are a pattern for processing form data on the same page it is displayed on. This is done by distinguishing between GET/POST request during script execution.

Example: Perl

Example: Java

Example: PHP

Data Management: Sever-Side State

Data is stored both on server and client, and data is exchanged in requests and responses. Examples for client data include:

Server data includes:

The server holds static resources and dynamic application state. Application state may include:

Aspects of server-side state include:

Resulting from those aspects are design choices regarding server-side state:

Storage Options

Options are:

main memory

local storage

remote storage

Session Management

Sessions provide state beyond individual HTTP requests. Example uses are multi-step forms, login status or shopping carts.

This requires the server to identify subsequent requests belonging to the same user/session. A session is the list of consecutive actions of an individual user. This is usually implemented by labeling the requests, that label is called Session ID or Session Token. The ID is generated on the server side, and has to be unique and un-guessable (to prevent session hijacking). It is transmitted to the client in the first response, which then includes it in subsequent requests. This allows the server to relate it to the session.

Sessions may end, either explicitly (discarding the session ID on server or client) or by timeout.

URL Session Identifiers

Also called URL rewriting, this is a technique for handling session IDs. It works by embedding the ID in the URL, which requires the server to rewrite all hyperlinks in the response to include the ID.

Advantages:

Disadvantages:

Cookies

Cookies are small chunks of data (KV pairs) related to one domain, which the client includes for every request to the same domain. Cookies provide a more secure way to attach IDs to session.

The server provides the cookies using Set-Cookie headers, which include the session ID.

The client receives the cookie, saves it and sends it back to the server in subsequent requests using the Cookie header.

Session State

Once the server has the possibility to relate requests to sessions, session state can be stored. This session state can be arbitrary, and would usually stored in a database indexed by the session ID.

In principle, the entire state could be stored in a cookie, which eliminates the need for server-side storage but has several drawbacks such as limited size and the possibility for the client to modify the state in unexpected ways.

Security

There are multiple security considerations regarding sessions, in particular Leaking the ID, which can lead to session hijacking and thus impersonating the user

Client-side Developments

The desire for more dynamic user interfaces in the web go beyond static HTML pages delivered from the server, and interactions consisting mainly of navigating to different pages. This requires computation on the client-side, which is possible using javascript. Today, the client-side of a web page consist of multiple elements of

The JS execution model of a browser consists of an event loop processing callbacks. Inside the callbacks, the JS code has access to multiple APIs, such as for reading and modifying the DOM, setting timers or making (AJAX) requests.

Object-Relational Mapping

Traditionally, relational DBs are accessed using SQL statements. Drawbacks to this are that SQL is both a different programming model to the application language and a different data model (relational vs e.g. variables, object oriented). This requires mapping between those two domains, and to synchronously maintain both models, the application code and database.

An idea to solve this is to specify the model only in the application programming language. Data entities could be represented by language objects, relationships by references between objects. The mapping to the DB data model is automatic. Database queries/updates are hidden behind read/write of an object or its properties. Database inserts are hidden behind object creation. Executing queries instantiates language objects (queries themselves still exist).

The database still requires a data model, and schema. This could however automatically be generated from the data model definition in the application language, and may even automatically synchronize changes in the data model.

Structured Developments

All web developments covered up to this lecture required lots of boilerplate code, which is error prone and causes a high development overhead. This created a demand for a more structured development environment, which includes:

Those demands ultimately resulted in entire new frameworks for web development.

MVC

Model-View-Controller is an architectural pattern applied to web-based services. It separates the application into three independent components:

The business logic may be located in either the controller or model. If the logic is combined with the data in the model, operations would still be invoked by the controller.

MVC can be applied to web applications in multiple ways:

Server Side MVC

Technologies such as Java Servlets assist in extracting the model, but controller and view would still be tightly coupled. Java Server Faces provides a generic, configurable controller component and allow separating the view (“Facelet”). The view can be implemented declaratively as an HTML page containing references to the model.

Django takes a different approach: Django separates the tasks in the components view, model and template. The view handles the request and contains the business logic (like the controller in other examples). The template corresponds to the view in the java example: It is an HTML template which gets instantiated with model data by the view.

Summary

Some more structural patterns

Inversion of Control (IoC)

This principle states that the application code is reactive, not active: it gets called by the framework when relevant events occur, instead of being the main entrypoint into the application.

Don’t Repeat Yourself

Self explanatory

Convention Over Configuration

Code, configuration and other aspects such as directory structure should be derived from convention whenever possible.

Structured Project Directories

RIA and SPA

Thin vs. Thick Clients

In thin-client applications, the browser only handles inputs and renders responses from the server. Interactions between client and server are HTTP requests.

Thick-client applications move some business logic to the client side. This typically allows for improvements in user experience. Some actions are executed locally without server interaction. Other interactions with the server might be application specific, once the client application is loaded.

Rich Internet Applications (RIA)

Basic idea: Resemble user experience of local desktop application. This requires running some logic on the client side. An extreme example would be a client side game engine running in the browser. RIAs allow platform independence, which might be an advantage compared to native applications.

Examples of RIAs:

Frameworks are available for both client- and server-side programming.

Today, RIA are “rich web applications”, using native web technologies such as HTML(5), CSS and JS.

Single Page Applications (SPA)

SPAs are thick clients, using current web technologies (HTML5, CSS3) and modern web APIs using JS. The page is loaded only once, and the application dynamically modifies the current page based on user input, instead of loading new pages. Interaction with the server is done in the background.

Technologies used:

Challenges include:

Node.js and Unification of Languages

Emerging Challenges

A traditional web application usually features

while a modern application wants to use techniques such as long polling, wherein the server delays a response until some event has occurred (such as a new chat message being sent). This requires a new programming model with

Event-driven Web Application Runtime

This model extends the reactor pattern by allowing the event loop to execute arbitrary application code such as making database queries and generating HTML output.

Node.js

Unification of Languages

Using the same programming language for client and server has many advantages, such as easier debugging and less duplication of server and client code.

A full JS stack has the advantage of not requiring transpilation or code generation for the client side. Also, developers might already be familiar with JS because of its prominence for frontend code.

Popular JS stack “MEAN”: MongoDB, Express, AngularJS, Node.js.

Component-based Frameworks

This focuses on web component for the client side.

Web Components Specification

Building blocks:

Component-based Application Frameworks

Those don’t use the standard components directly, but implement similar concepts.

Standard Components vs Frameworks:

Progressive Web Applications

Principles of PWAs:

Implementing PWAs:

Popular Implementations

This section has been skipped due to an unbearable amount of Java EE content.

Introduction to Scalability

Part 1

The main requirements for a large scale web application are

A scalable system is able to cope with varying load. This might require utilizing additional resources, or graceful degradation in case of temporary overload. Scalability is not the same as performance, as a system which has good performance for a single user it might still not scale well for large numbers of users.

Client Side Performance Metrics

Server Side Performance Metrics

Response Times

Both average and peak response time can be measured.. However, those statistics are of limited relevance, since the distribution is usually not normal or uniform. A better way of assessing response times are percentiles (99% of all responses arrive within \(p_{99}\)). Percentile plots (hockey stick plots) allow comparing response times of multiple systems.

Throughput vs Response Time

Under load, increased throughput negatively affects response times. This might be due to requests being queued for example.

Part 2

Load Characteristics

Load: externally assigned work for the system

Load is characterized by load parameters:

Roary example load parameters:

secondary load parameters in this example might be the number of followers and subscriptions per user, or the distribution of those among the user-base.

Scalability Strategies

Scale Cube

Axes:

Example for scaling up users+products database:

All three approaches can be combined, even for this simple database example.

General Strategies

Interaction/Communication

HTTP2

Client-Backend Communication

Required for:

REST

“Representational state transfer” is a style and set of guidelines, which supports large-scale web applications and works well with HTTP:

GraphQL

The goal of GraphQL is to avoid disadvantages of REST, and in particular make it possible to retrieve only the necessary data, and all of it in a single operation. An example from Roary would be requesting some information about a user and their last three posts. This would require multiple requests when following REST (user, post list, individual post-details).

In GraphQL, the query specifies the exact fields required, such as:

query {
    User(id: 34) {
        name
        roars(last: 3) {
            message
        }
    }
}

This requests only the name of the users, and the text of the last three roars. Responses are provided in JSON format.

Using GraphQL requires a schema definition for the available resources, which serves as a template for possible queries. Queries can also be created to modify data, or subscribe to update-events. The server has to implement some functions to resolve queries, libraries are available to connect GraphQL queries to data sources such as databases.

Client-side frameworks are available which link views with GraphQL queries.

While GraphQL eliminates REST problems such as transferring unnecessary data in multiple requests, and allows for fast frontend iteration without backend modifications, drawbacks exist: Caching is more complex, since data is often incomplete, and queries can be very complex. This leads to large processing demands on the server, which also vary widely by request.

Websockets

HTTP is not suited for interactive two-way communication (prime example: web chat application). An option to solve this would be AJAX polling, or AJAX long polling, but this requires complex server-side coordination and bears the hidden cost of HTTP (headers, TLS handshakes, etc).

The modern approach however is WebSocket: WebSocket describes both an API and protocol:

The client opens a WebSocket connection to the server, and can then send messages and react to incoming messages. The server listens to incoming requests to open connections, and then interacts with the connection.

The WebSocket protocol is compatible with HTTP, and uses a GET request to initiate a connection. After the response, the protocol is switched to WebSocket. Subsequent messages are not HTTP requests, but use the WebSocket wire protocol. WebSocket packets have at least two bytes overhead and allow binary payloads.

Application protocols are usually used on top of WebSocket, which can already be specified when initiating the connection.

Backend-Backend Communication

This type of communication occurs between servers which are part of the backend. An example might be communication between an application server and database server.

Previously mentioned protocols like REST and GraphQL might be used between servers, but there are additional options:

RPC: Remote Procedure Call

Popular implementations:

Web Services Communication

SOAP:

WSDL “Web Service Description Language”:

Advanced Concepts

Messaging

Queuing

Pub-Sub

Implementation Notes

Examples:

SEDA: Staged Event-Driven Architecture

Advantages:

Disadvantages:

Back Pressure

State

State in Scalable Web Architectures

In this section, we consider server-side state. We have to differentiate the context of the state: request, session, long-term. Additionally, we have to differentiate between ephemeral/volatile, derived and persistent state.

Caching

Main idea: prevent duplicate work.

Challenges:

Application of caching throughout the architecture:

Rule of thumb: cache high up in the call stack (close to the client).

Data Models

Relational Model

Key/Value

Document oriented

Graph Model

Column-oriented Model

Functional Decomposition

Idea: Split application model into logical parts which can be separated.

Replication

Idea: Multiple copies of the same data.

Primary challenge: mutable data. This requires propagation of updates to achieve consistency between replicas.

Leader-Follower Replication

Replication Mechanism:

Properties/guarantees relating to consistency anomalies:

Multi-Leader Replication

Main problem: write conflicts. Strategies for resolution:

Leaderless Replication

Sharding/Partitioning

Partitioning schemes:

Rebalancing shards: scale-out by adding instances, ideally with low migration effort

Sharding is often combined with replication.

Data Consistency

Consistency models define visibility semantics and system behavior.

CAP Theorem

In case of a partition, a system can either

PACELC Theorem

Harvest/Yield

Event Sourcing

Benefits:

Drawbacks:

Logic

Application Logic

Stateless and Stateful Application Logic

Examples:

Stateless logic is preferred whenever possible. It might be possible to decompose application logic into stateless and stateful parts.

Distributing Application Logic

Functional Partitioning

Horizontal Duplication (Replication)

Data Partitioning (Sharding)

Shared and Global State

Alternatives:

Background Tasks

Batch Processing

Stream Processing

Event Sourcing and CQRS

Deployment/Infrastructure

Backend Endpoints

Proxies

Reverse Proxies:

Popular software:

Load Balancing

Two design dimensions:

CDNs

Cloud Computing

Model for enabling on demand access to shared pool of computing resources, which can be rapidly provisioned with minimal effort and interaction.

Characteristics of cloud services:

Deployment Models:

Service Models

Infrastructure as a Service IaaS

Platform as a Service PaaS

Storage:

Software as a Service SaaS

Virtualization

Advanced Topics

Monitoring

Logging

Microservices

Service Composition:

Microservices:

Security and Privacy

Secured Communication

TLS

Browser Policies

Authentication

Problem: HTTP is stateless. Possible solutions:

Challenges:

HTTP digest

Shibboleth

OpenID/OAuth

JWT: JSON web Token

Privacy

Privacy by Design

Data

DevOps