Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Cache-Proxy in Java: Ein Tutorial zu verteilten Systemen mit RMI und LRU-Caching

Lerne, wie du einen File-Caching-Proxy in Java 8 mit RMI und LRU-Verdrängung implementierst – ideal für Studierende, die verteilte Systeme verstehen wollen.

File-Caching-Proxy Java RMI LRU-Caching verteilte Systeme Proxy-Implementierung Open-Close-Session-Semantik Nebenläufigkeit Java Cache-Verwaltung 15-440 Projekt 2 CMU Caching Proxy Java Threading RPC-Protokoll Dateioperationen Proxy Cache-Verdrängungsstrategie Java 8 Linux Projekt Caching Tutorial Deutsch

Einführung in das Projekt: File-Caching-Proxy für verteilte Systeme

In diesem Tutorial lernst du die Grundlagen zum Entwurf eines File-Caching-Proxys, wie er in der Projektaufgabe "15-440 Project 2" gefordert wird. Das System besteht aus einem Client, einem Proxy und einem Server. Der Client führt Dateioperationen wie open, read, write, lseek, close und unlink aus. Der Proxy fängt diese Aufrufe ab, cached ganze Dateien aus dem Server und sorgt für konsistente Sichten bei gleichzeitigen Zugriffen. Du wirst Java RMI, Threading und LRU-Caching einsetzen. Dieses Tutorial erklärt die Konzepte, ohne die komplette Lösung zu verraten.

Warum Caching? Aktuelle Trends und Anwendungen

Caching ist überall: Von Content Delivery Networks (CDNs), die Videos von TikTok oder YouTube ausliefern, bis zu KI-Modellen, die häufig angefragte Ergebnisse zwischenspeichern. Auch in Finanz-Apps wie Trade Republic werden Aktienkurse gecached, um Ladezeiten zu verkürzen. In diesem Projekt lernst du, wie Caching auf Dateiebene funktioniert – eine wichtige Fähigkeit für Backend-Entwickler.

Architektur des Systems

Das System besteht aus drei Komponenten:

  • Client: Nutzt die bereitgestellte Interpositionsbibliothek, die C-Funktionen wie open() in RPCs umwandelt.
  • Proxy: Deine Implementierung. Er empfängt die RPCs vom Client, verwaltet einen Dateicache und kommuniziert mit dem Server über Java RMI.
  • Server: Speichert die Dateien und bietet Zugriff auf Dateiebene. Er muss mehrere gleichzeitige Proxy-Verbindungen unterstützen.

Design des Caching-Protokolls

Dein Proxy muss ein Protokoll definieren, das Open-Close-Session-Semantik gewährleistet. Das bedeutet: Solange ein Client eine Datei geöffnet hat, sieht er eine stabile Version – unabhängig von Änderungen anderer Clients. Typischerweise wird eine Datei beim Öffnen komplett vom Server geholt, lokal gecached und beim Schließen zurückgeschrieben. Du musst entscheiden, ob du Write-Through oder Write-Back einsetzt. Für die Konsistenz ist Write-Back üblich, erfordert aber eine Invaliderungsstrategie.

LRU-Cache-Verwaltung für variable Dateigrößen

Dein Cache hat eine feste Größe in Bytes, aber die Dateigrößen variieren. Du musst eine LRU-Verdrängungsstrategie (Least Recently Used) implementieren. Das bedeutet: Wenn der Cache voll ist, wird die am längsten nicht verwendete Datei entfernt. Achte darauf, dass große Dateien den Cache schnell füllen können – du könntest eine maximale Dateigröße für den Cache festlegen oder Dateien aufteilen.

Implementierungstipps für LRU

Verwende eine LinkedHashMap mit Zugriffsreihenfolge oder eine PriorityQueue mit Zeitstempeln. Denke daran, dass der Cache threadsicher sein muss – verwende synchronized Blöcke oder ReadWriteLock.

import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxSize;

    public LRUCache(int maxSize) {
        super(16, 0.75f, true); // access order
        this.maxSize = maxSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxSize;
    }
}

Implementierung der Dateioperationen

Deine Proxy-Klasse muss die Methoden open, close, read, write, lseek und unlink implementieren. Die bereitgestellte RPCreceiver-Klasse ruft diese auf. Du musst für jede geöffnete Datei einen Dateideskriptor verwalten, der die aktuelle Position und einen Puffer enthält. Beachte, dass read und write auf dem Cache arbeiten, nicht direkt auf dem Server.

Beispiel: open-Methode

public int open(String filename, int flags) {
    // Cache prüfen, ggf. vom Server holen
    // Dateideskriptor erzeugen und zurückgeben
    return fd;
}

Nebenläufigkeit und Konsistenz

Dein System muss mehrere Clients gleichzeitig bedienen können. Verwende Java Threads für jeden Client. Für die Dateikonsistenz solltest du Sperren auf Dateiebene einsetzen: Read-Write-Locks erlauben mehrere Leser, aber nur einen Schreiber. So wird sichergestellt, dass ein Client während einer offenen Session eine stabile Version sieht.

Kommunikation zwischen Proxy und Server mit Java RMI

Die RPCs zwischen Proxy und Server werden mit Java RMI realisiert. Definiere ein Interface für den Server, das Methoden wie byte[] fetchFile(String filename) und void storeFile(String filename, byte[] data) enthält. Der Server registriert sich in der RMI-Registry, der Proxy holt sich das Remote-Objekt.

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface FileServer extends Remote {
    byte[] fetchFile(String filename) throws RemoteException;
    void storeFile(String filename, byte[] data) throws RemoteException;
}

Checkpoint 1: Client-seitige Funktionalität ohne Server

Im ersten Checkpoint soll der Proxy ohne Server funktionieren. Er bedient Dateien aus seinem Arbeitsverzeichnis. Das ist eine gute Gelegenheit, die Dateioperationen und die Cache-Logik zu testen, bevor die RMI-Kommunikation hinzukommt. Stelle sicher, dass alle Fehlercodes (z.B. Datei nicht gefunden) korrekt zurückgegeben werden.

Häufige Fehler und Lösungen

  • Vergessene Synchronisation: Wenn du synchronized nicht richtig einsetzt, kommt es zu Race Conditions. Verwende synchronized Blöcke oder ReentrantReadWriteLock.
  • Cache-Invalidierung: Wenn ein Client eine Datei schreibt, müssen andere Proxy-Instanzen benachrichtigt werden. Hierfür könntest du einen zentralen Server verwenden, der Versionen vergibt.
  • RMI-Verbindungsprobleme: Stelle sicher, dass der Server die RMI-Registry startet und der Proxy die richtige IP und Port verwendet.

Zusammenfassung und Ausblick

Dieses Tutorial hat dir die Kernkonzepte des File-Caching-Proxys vorgestellt. Du hast gelernt, wie du LRU-Caching, Java RMI und Concurrency in einem verteilten System einsetzt. Diese Kenntnisse sind auch in modernen Anwendungen wie KI-Chatbots (Caching von Modellgewichten) oder Cloud-Speicherdiensten (Dropbox, Google Drive) relevant. Viel Erfolg bei deiner Implementierung!

Denke daran: Das Projekt erfordert Java 8 auf einem 64-Bit-Linux-System. Teste deine Lösung auf den Andrew Unix Servern.