036
architectural scalability reliability

Cluster-based Architecture

Reference Wikipedia ↗
Cluster-based Architecture — sequence diagram
Plate 036 sequence diagram

A Cluster-based Architecture involves grouping multiple interconnected computers (nodes) together to work as a single system. This approach enhances performance, availability, and scalability by distributing workloads across the cluster. The nodes typically share resources and are managed by software that coordinates their activities, presenting a unified interface to users or other systems.

This pattern is commonly used in scenarios demanding high throughput, low latency, and continuous availability. It’s essential for handling large volumes of data, serving numerous concurrent users, and ensuring resilience against hardware failures. Applications like web servers, databases, and big data processing systems frequently employ cluster-based architectures.

Usage

  • Web Applications: Distributing web server load across multiple instances to handle peak traffic and ensure responsiveness.
  • Database Systems: Creating database replicas and distributing queries to improve read performance and provide failover capabilities.
  • Big Data Processing: Parallelizing data processing tasks across a cluster of machines using frameworks like Hadoop or Spark.
  • Cloud Computing: The foundation of most cloud services, allowing for on-demand resource allocation and scalability.
  • Gaming Servers: Hosting game worlds and handling player interactions across multiple servers to support a large player base.

Examples

  • Kubernetes: A container orchestration platform that automates the deployment, scaling, and management of containerized applications across a cluster of nodes. It provides features like self-healing, load balancing, and automated rollouts/rollbacks.
  • Apache Cassandra: A highly scalable, distributed NoSQL database designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure. Data is replicated across multiple nodes in the cluster.
  • Amazon Web Services (AWS): Many AWS services, such as Elastic Compute Cloud (EC2) and Relational Database Service (RDS), are built on cluster-based architectures to provide scalability and reliability. Auto Scaling groups automatically adjust the number of EC2 instances in a cluster based on demand.
  • Google Kubernetes Engine (GKE): Google’s managed Kubernetes service, providing a fully-featured, production-ready environment for deploying and managing containerized applications on a cluster.

Specimens

15 implementations
Specimen 036.01 Dart View specimen ↗

The Cluster-based Architecture pattern distributes workload across a group of identical worker nodes (clusters) to improve performance, scalability, and fault tolerance. Each cluster performs the same tasks independently, allowing for parallel processing. A central dispatcher (or load balancer) routes requests to available clusters. In this Dart example, we simulate this by creating multiple WorkerCluster instances, each able to process Jobs, and a Dispatcher which distributes jobs amongst them. The use of Futures and async/await align well with Dart’s asynchronous nature for handling concurrent job processing.

// worker_cluster.dart
import 'dart:math';

class Job {
  final String id;
  final int processingTime;

  Job({required this.id, required this.processingTime});
}

class WorkerCluster {
  final String name;
  final int id;
  bool isBusy = false;

  WorkerCluster({required this.name, required this.id});

  Future<void> processJob(Job job) async {
    isBusy = true;
    print('Cluster $name ($id) processing job ${job.id} for ${job.processingTime}ms');
    await Future.delayed(Duration(milliseconds: job.processingTime));
    print('Cluster $name ($id) finished job ${job.id}');
    isBusy = false;
  }
}

// dispatcher.dart
class Dispatcher {
  final List<WorkerCluster> clusters;

  Dispatcher({required this.clusters});

  Future<void> dispatchJob(Job job) async {
    final availableCluster = clusters.firstWhere((cluster) => !cluster.isBusy);
    await availableCluster.processJob(job);
  }
}

// main.dart
import 'worker_cluster.dart';
import 'dispatcher.dart';

void main() async {
  final clusters = [
    WorkerCluster(name: 'Alpha', id: 1),
    WorkerCluster(name: 'Beta', id: 2),
    WorkerCluster(name: 'Gamma', id: 3),
  ];

  final dispatcher = Dispatcher(clusters: clusters);

  final jobs = [
    Job(id: 'A', processingTime: 500),
    Job(id: 'B', processingTime: 1000),
    Job(id: 'C', processingTime: 750),
    Job(id: 'D', processingTime: 250),
  ];

  await Future.wait(jobs.map((job) => dispatcher.dispatchJob(job)));

  print('All jobs completed.');
}