Pastry - Tapestry - Verteilte Systeme

Werbung
Pastry - Tapestry
Raimar Wagner
November 2006
1
Einleitung
Das Schlagwort Peer-to-Peer ist in den heutigen Medien zu einem Synonym für Filesharing geworden,
es wird jedoch meist nur die juristische und gesellschaftliche Relevanz des Themas aufgegriffen. Deren Scheitern oder Erfolg lässt sich meist in direkten Zusammenhang mit den technischen Grundlagen
von Peer-to-Peer Systemen bringen, da sich ihr Design erheblich auf die spätere Leistungsfähigkeit
und Zuverlässigkeit auswirkt. Auch die Wissenschaft beschäftigt sich seit geraumer Zeit mit den technischen Problemen die ein Peer-to-Peer System mit sich bringt.
Die Ziele eines Peer-to-Peer Systems nach [Jan04] sind vereinfacht:
• Sichere Ortung: Da es per Definition keinen Server gibt, muss auf anderem Weg gewährleistet
werden dass, falls ein Objekt im Netzwerk vorhanden ist, es auch gefunden wird.
• kürzeste Routingstrecke: Das Routing zu einem Zielknoten sollte auf dem schnellst möglichen
Weg im Sinne der Nachbarschaftsmetrik1 der darüberliegenden Schicht erfolgen.
• Lastausgleich: Kein Knoten im Netzwerk sollte übermäßig belastet werden.
• Skalierbarkeit: Das System muss auf ankommende und gehende Knoten gut reagieren können.
• Gleichberechtigung: Kein Knoten darf besondere wichtige Kompetenzen allein besitzen, da
sonst ein Single Point of Failure2 möglich wird.
Im Folgenden will ich zwei Systeme vorstellen, die diese Eigenschaften erfüllen. Damit sind Pastry und Tapesty echte Peer-to-Peer Systeme, im Gegensatz zu hybriden Systemen wie Napster. Auf
Ähnlichkeiten und den Vergleich der beiden Systeme will ich am Ende der Ausarbeitung eingehen.
2
Pastry
Pastry wurde 2001 von Antony Rowstron (Microsoft Research, Cambridge, GB) und Peter Druschel
(Rice University, Houston, Texas) entwickelt und veröffentlicht [RoDr01]. In Pastry wird jedem Client
eine eindeutige 128-bit lange Identifikationsnummer (NodeID) zugewiesen. Dies geschieht entweder
durch bildes eines Hashwerts der IP-Adresse oder durch Zufallsgeneratoren. Im Weiteren müssen wir
2 Metriken unterscheiden:
1
2
In unserem Fall meist die Messung der Pingzeit oder die Anzahl der Routing Hops.
Ein Knoten mit zuvielen Kompetenzen kann die Integrität des Netzes beschädigen, falls er ausfällt.
1. Nachbarschaftsmetrik : Eine Nachbarschaftsmetrik distP (x, y) gibt den Abstand zweier Knoten x und y auf Basis von Pingzeiten oder Hops an.
2. numerische Metrik : Eine numerische Metrik distN (x, y) gibt den Abstand zweier Knoten in
Form von der numerischen Nähe von x und y wider. In der Praxis bedeutet das dass die Präfixe
von N odeID(x) und N odeID(y) verglichen werden und je größer die Übereinstimmung ist,
desto näher sind sich die Knoten nach der numerischen Metrik.
Durch die zufällige Verteilung der NodeIDs wird gewährleistet, dass numerisch nahe Knoten nach der
Nachbarschaftsmetrik gut gestreut sind. Eine Verklumpung“ hätte erhebliche Routingnachteile, da
”
bei einem Ausfall von z.B. einem IP Segment möglicherweise einige Knoten komplett vom Restnetz
abgeschnitten werden könnten.
2.1
Der Pastry Node
Ein Pastry Node besteht aus drei Tabellen:
• Die Routing Table: Die für das Routing zuständige Tabelle
• Das Neighborhood Set: Hier ist Nähe im Sinne der Nachbarschaftsmetrik gemeint.
• Die Leaf Set: Hier ist Nähe im Sinne der numerischen Metrik gemeint.
Mit der Routing Table wird später, wie der Name schon
sagt, das Routing ausgeführt. Basierend auf einem Konfigurationsparameter b besteht die Routing Table aus dlog2b N e
Zeilen mit je 2b −1 Einträgen. Die Zahl N entspricht im weiteren der Anzahl der möglichen Nodes im NodeID Space.
Die genaue Herkunft dieser Zahlen wird bei praktischer Betrachtung klar. Des weiteren ist die NodeID dargestellt als
Sequenz von Ziffern der Basis 2b . In unserem Fall wählen
wir b = 2 und einen NodeID-Bereich von 0 bis 2128 − 1 an,
dann hat unsere Routing Table acht Zeilen zu je drei Einträgen. In der nullten3 Zeile der Routing Table stehen IPAdressen von NodeIDs, deren NodeID keinen Präfix mit der
NodeID des Knotens selbst gemeinsam hat. In der ersten
Zeile die IP-Adressen deren NodeID Präfix eine Ziffer mit
Abbildung 1: PastryNode 10233102 der NodeID des Knoten gemeinsam hat und so weiter. Allgemein gesagt: Die Knoten in der Zeile n teilen mit dem
nach [RoDr01]
Knoten selbst die ersten n Ziffern im Präfix. In jeder Zeile
muss auch gewährleistet sein, dass das n + 1te Zeichen nicht doppelt vorkommt.
Das Neighborhood Set enthält NodeIDs und die dazugehörigen IP-Adressen von nahen Knoten im
Sinne der Nachbarschaftsmetrik. Diese Tabelle ist am Routingvorgang nicht beteiligt, aber wichtig für
das Erhalten der Lokalitätseigenschaft (z.B.: beim Einfügen).
Das Leaf Set enthält die numerisch nahen Knoten, die für das Routing wichtig sind. Es werden zu
gleichen Teilen Knoten mit größerer und kleinerer NodeID gespeichert. Die Bezugsgröße ist hier die
3
Man zählt die Zeilen ab 0, dies ist reine Definitionssache.
eigene NodeID. Die Anzahl der Einträge im Leaf Set und im Neighborhood Set sind Definitionsssache
und werden meist 2b oder 2b+1 gewählt.
2.2
Routing
Das Routing beschreibt den Vorgang wie ein Knoten auf das Ankommen einer Nachricht, die nicht
für ihn bestimmt ist, reagiert. Eine Nachricht ist in Pastry immer mit einer NodeID für den Empfänger
(auch MessageID genannt) gekennzeichnet. Für die Weiterleitung der Nachricht werden zwei der drei
vorhanden Tabellen die im Knoten vorgehalten werden gebraucht, nämlich die Routing Table und das
Leaf Set. Der Algorithmus sieht wir folgt aus:
1. Vergleiche die NachrichtenID zuerst mit allen NodeIDs im Leaf Set, falls die MessageID der
Nachricht in dem vom Leaf Set abgedeckten Bereich fällt wird die Nachricht sofort zugestellt.
2. Ermittele Präfixübereinstimmung i mit eigener NodeID. (z.B.: wäre bei NodeId 10222102 und
NachrichtenID 10232120 die Präfixübereinstimmung i = 3)
3. Gehe nun in die i-te Zeile der Routing Table und wähle diejenige NodeID aus, deren i + 1-tes
Zeichen mit dem i + 1-ten Zeichen der MessageID übereinstimmt. Leite nun die Nachricht an
diesen Knoten weiter.
4. Falls kein so ein Knoten gefunden wird, schicke die Nachricht an den numerisch nächsten Knoten aus Leaf Set ∪ Routing Table ∪ Neighborhood Set. Nach [RoDr01] liegt die Wahrscheinlichkeit pro Nachricht dass dieser Fall auftritt zwischen 0.02 und 0.006.
2.2.1
Performance
Da im ersten Fall der Knoten sofort gefunden ist und im zweiten Fall die Anzahl von Knoten mit
längerem Präfix immer um 2b sinkt, hat dieser Algorithmus eine Laufzeit von dlog2b N e. Der dritte
Fall kann vernachlässigt werden, da er nur selten auftritt (siehe oben).
2.3
Einfügen eines Knoten
Zuerst braucht der neu hinzukommende Knoten φ die NodeID eines vorhanden Knoten ϕ, der nach
der Nachbarschaftsmetrik nah sein sollte. Warum dies der Fall sein sollte wird später klar. Diesem
Startknoten (hier ϕ) sendet φ eine spezielle Join-Message deren Aufgabe es ist die 3 Tabellen von φ
zu füllen. Die Beitrittsnachricht hat als Zieladresse die NodeID von φ selbst. Dies garantiert dass die
Nachricht die Knoten erreicht, die dem neuen Knoten nach der Umgebungsmetrik nahe sind. Das Leaf
Set kann der neue Knoten einfach von dem letzten Empfänger der Beitrittsnachricht kopieren, da er φ
numerisch am nächsten ist. Das Neighborhood Set lässt sich ebenfalls einfach füllen, da angenommen
wird dass ϕ topologisch nah zu φ ist. Falls das nicht gegeben sein sollte, hat dies nur zur Folge dass
das Netzwerk langsamer wird.
Die Join-Message versucht nun die eigene NodeID zu finden. Auf dem Weg verlängert sich das gemeinsame Präfix immer weiter um eins und der Knoten kann so aus den Routing Tables der anderen
Knoten die Einträge übernehmen. Anschaulich gesagt, wenn der Knoten die ersten k Stellen mit dem
neu hinzugekommenen Knoten gemeinsam hat, kann dieser die ersten k Zeilen seiner Routing Table
übernehmen.
2.4
Löschen eines Knotens
In einem dezentral organisierten Netz muss gewährleistet werden, dass Knoten ordnungsgemäß aus
dem System gelöscht werden. So ist gewährleistet dass keine Nachrichten ins Leere laufen. Hier
kommt nun das Neighborhood Set zum tragen, denn hier sind alle topologisch nahen Knoten verzeichnet. Jeder Knoten sendet in periodschen Abständen eine Art Ping“ an die Knoten im Neighbor”
hood Set, denn hier ist die Laufzeit eines Lebenssignals“ am kürzesten. Falls nun ein Knoten nicht
”
antwortet, wird eine Nachricht an die NodeID des betreffenden Knoten geschickt. Diese erhält nun
ein Knoten in dessen Leaf Set der tote Knoten steht. Dieser wird gelöscht und alle anderen Knoten
im Leaf Set erhalten eine Nachricht diesen Knoten zu löschen. Falls es sich nun bei einem Routingvorgang rausstellt, dass ein Eintrag der Routing Table defekt ist, muss dieser repariert werden. Dazu
werden nach und nach die anderen Knoten in der selben Zeile der Routing Table nach ihrem Eintrag
an dieser Stelle befragt. Falls ein Eintrag passt wird dieser übernommen. Falls nun in der gesamten
Zeile kein Knoten diesen Eintrag vorhält wird in der nächsten Zeile inspiziert und so weiter.
2.5
Objektmanagement
Um das Management von Objekten vergleichen zu können wird in [Her02] beschrieben wie Past,
eine Anwendung des Pastry Algorithmus, Objekte verwaltet. In Past wird einem Objekt ein Hashwert
zugeordnet und dann das Objekt an den, dem Hashwert numerisch nächsten Knoten geschickt. Von
dort aus wird die Datei noch an k Nachbarknoten verteilt. So ist statistisch eine gute Verteilung des
Objekts gewährleistet.
2.6
Experimentelles
In [RoDr01] werden noch ausführliche Experimente mit Pastry dokumentiert. Die wichtigsten Punkte
sind:
• Skalierung: Die Anzahl der Hops zur Anzahl der Knoten verhält sich logarithmisch. Das kann
als optimal bezeichnet werden. Die meisten Knoten konnten in einem großen Netz über nur 4
Hops erreicht werden, kein Routingvorgang hat mehr als 5 Hops gebraucht.
• Distanz: Die Distanz zweier Knoten ist um nur 30-40% größer als bei direktem Routing. Sie
nimmt auch bei steigender Knotenmenge nicht zu.
3
Tapestry
Tapestry wurde 2001 von Ben Y.Zhao, John D. Kubiatowicz und Anthony D. Joseph von der University of California in Berkely (CA) entwickelt und veröffentlicht [ZhKuJo01]. Dieses System besteht
ebenfalls aus Knoten (englisch Nodes), die ebenfalls durch NodeIDs gekennzeichnet sind. In Tapestry sind diese NodeIDs 40-stellige Hexadezimalzahlen zur Basis 16. Die NodeIDs werden ebenfalls
zufällig mittels Hashing vergeben.
3.1
Der Tapestry Node und das Objekt
Der Tapestry Knoten hat im Gegensatz zu einem Pastry Knoten nur eine Tabelle: die sogenannte
Neighborhood Map. Dort sind, nach Leveln geordnet, die Nachbarknoten gespeichert. Die Level geben an, ab der wievielten Stelle sich die NodeID von der eigenen NodeId unterscheidet. Bei Level
4 ist die 4. Stelle eine andere (Siehe Abbildung 2). Der numerisch nächste Knoten in einem Level
wird primärer Knoten genannt. Diese werden aktiv beim Routing benutzt, die restlichen Einträge dienen nur der Ausfallsicherheit. Jeder Knoten hält auch noch Referenzen, sogenannte Vorgänger-Zeiger
(im englischen backpointer) auf Knoten die auf ihn verlinken. Diese Verweise sind aber nur für das
Einfügen von Knoten relevant.
Bei Tapestry gibt es, im Gegensatz zu Pastry, eine ausgefeilte Objekt-Behandlung. Objekte sind Dateien oder Pakete mit Inhalt die ausgetauscht werden sollen. Jeder Knoten
der ein Objekt vorhält nennt man Speicher-Server (im englischen Storage-Server). Jedes Objekt hat eine Menge von
sogenannten Wurzel-Knoten (im englischen Root-Peers) deren Aufgabe die Verwaltung der Links der Speicher-Server
ist. Im System existiert eine Funktion M apRoots(ϑ) die für
jeden Knoten ϑ die zugehörigen Wurzel-Knoten zurückgibt.
Die genaue Funktionsweise dieses Algorithmus wird im Abschnitt Surrogate-Routing beschrieben.
3.2
Routing
Abbildung 2: Tapestry Knoten 4231
frei nach [Roe05]
Das übermitteln von Nachrichten in Tapestry geht ähnlich
von statten wie bei Pastry. Wenn eine Nachricht an einen
Knoten ζ weitergeleitet werden soll, wird in der Neighborhood Map der Knoten gesucht dessen NodeID in den meisten Stellen mit der NodeID von ζ übereinstimmt. So kommt die Nachricht dem
Knoten ζ in jeden Schritt um ein Stelle in der NodeID näher.
3.3
3.3.1
Surrogate Routing
Objektbereistellung
Falls ein Knoten nun ein Objekt in das Netzwerk einbringt besteht die erste Aufgabe darin bei dem
Wurzel-Knoten bekannt zu machen dass er nun als Speicher-Server fungiert. Dazu werden als erstes
die eindeutigen Wurzel-Knoten mittels M apRoots(·) bestimmt. Nun wird an die Wurzel-Knoten eine
Nachricht geschickt, dass ein neuer Speicher-Server vorhanden ist. Alle Knoten, die die Nachricht auf
dem Weg zum Wurzel-Knoten passiert, speichern nun auch ein Tupel aus Referenz auf den SpeicherServer und der ObjektID. Die Referenzen auf Speicher-Server verfallen nach einiger Zeit, sodass der
Speicher-Server diese Prozedur in regelmäßigen Abständen wiederholen muss.
3.3.2
Objektsuche
Falls nun ein Knoten ein Objekt sucht bestimmt er als erstes die dazugehörigen Wurzelknoten. Nun
schickt er eine Nachricht an die Wurzel-Knoten in denen er die Adressen der Speicher-Server anfordert. Da die Nachricht aber über mehrere andere Knoten geht ist sehr wahrscheinlich dass einer der
Knoten zwischen dem Wurzel-Knoten und dem Anfordernden Knoten schon eine Referenz auf den
Speicher-Server vorliegen hat. Das kommt daher dass ja beim Bekanntmachen des Speicher-Servers
mit dem Wurzel-Knoten alle Knoten die auf dem Weg zwischen den beiden liegen auch eine Referenz
auf den Speicher-Server speichern. In so einem Fall wird die Nachricht sofort an den Speicher-Server
weitergeleitet.
3.3.3
Surrogate Routing
Das Tapestry System kann nur funktionieren wenn M apRoots(·) immer die selben Wurzel-Knoten
zurückliefert. Hier kommt das sogenannte Surrogate Routing (zu deutsch Stellvertreter-Routing“)
”
zum tragen. Die NodeIDs der Wurzel-Knoten werden durch verschiedene Hashfunktionen oder Varianten bestimmt. Nun kann aber vorkommen dass die so errechnete NodeID des Wurzel-Knotens
nicht existiert. Beim Routing auf solche nicht existenten Wurzel-Knoten können die Nachrichten auf
Löcher“ in den Neighborhood Maps stoßen. In so einem Fall wird das erste Zeichen das nicht gerou”
tet werden kann um eins erhöht und dann erneut ab diesem Knoten das Routing versucht. Dies wird
solang wiederholt bis kein Knoten mehr in dem momentanen Level oder den darüberligenden Leveln
zu finden ist. So ist gesichert dass M apRoots(·) immer die selben Wurzel-Knoten zurückgibt.
3.4
Einfügen von Knoten
Falls ein neuer Knoten α das Tapestry System betritt braucht er einen Zugangsknoten β, über den er
Zugang zum Netz bekommt. α sucht nun im Netz nach der eigenen NodeID und erreicht so seinen
Stellvertreter-Knoten. Auf dem Weg von α zu seinem Stellvertreter-Knoten bekommt α alle Neighborhood Maps aller Knoten dazwischen. Aus diesen Knoten extrahiert der Algorithmus die besten
Knoten für die eigene Neighborhood Map unter Berücksichtigung der toplogischen Parametern.
3.4.1
Bekanntmachen eine neuen Knoten
Da einige Knoten möglicherweise auch Löcher in ihrer Neighborhood Map haben können, in die
unser α passen könnte, ist es nun die Aufgabe von α seine Existenz anderen Knoten mitzuteilen. Wie
wir noch aus dem Abschnitt Surrogate Routing wissen, wurden dort für nicht existierende Knoten
Nachfolger gesucht. Falls nun nach unserem neuen Knoten α gesucht wurde finden wir die Knoten
mit einem Loch in der Neighborhood Map indem wir die Vorgänger-Zeiger des Knoten Nachfolgers
zurücklaufen und dort den neuen Knoten α eintragen. Da laut experimentellen Messung das Surrogate
Routing in der Mehrzahl der Fällen [ZhKuJo01] nach 2 Hops beendet ist, genügt es in den allermeisten
Fällen die Vorgänger-Zeiger 3 Schritte zurückzuverfolgen. Des weiteren wird eine Nachricht an alle
primären und sekundären Nachbarn geschickt und diesen die Gelegenheit gegeben einen vorhanden
Knoten in der Neighborhood Map durch α zu ersetzen. Hier ist primär die topologische Entfernung
von Bedeutung.
3.5
Löschen von Knoten
Das Löschen von Knoten ist dank der Vorgänger-Zeiger trivial, da so alle auf einen Knoten zeigenden
Knoten informiert werden können.
4
Vergleich
Wenn man Pastry und Tapestry vergleicht fällt einem Zuerst auf, dass der eigentlich Aufwand für
verschiedene Operationen auf dem Papier identisch ist [Jan04]:
System
Pastry
Tapestry
Einfüge-Kosten
O(log 2 (N ))
O(log 2 (N ))
Routing-Hops
O(log(N ))
O(log(N ))
Balance
ja
ja
Abbildung 3: Nodesuche nach Node 022
Des weiteren fällt sofort ins Auge dass
die Routingmechanismen sich vom Prinzip
her sehr ähnlich sind, da sie beide auf
dem selben Prinzip, dem sogenannten Plax”
ton Routing“ 4 basieren. Doch wenn man
sich die Systeme genauer anschaut sind einige Unterschiede zu erkennen. Es ist zu
erkennen dass Pastry mehr auf heuristische
Methoden setzt deren Funktion aber dennoch durch Experimente gut nachgewiesen
ist. Tapestry dagegen ist wesentlich analytischer aufgebaut, aber damit auch komplizierter [Sch06].
Es fällt besonders auf dass Pastry selbst kein wirkliches Objekt-Management besitzt. Dies wird
vollständig der Implementierung überlassen (z.B.: Past). Insbesondere die Past-Implementierung achtet viel weniger auf die Organisation von Objekten als es Tapestry tut. In Pastry sind daher Duplikate
von Dateien möglich ohne dass eine Kontrollinstanz davon etwas mitbekommt. Das Verteilen basiert
darauf, dass ein Objekt in einem Bereich des Netzes gestreut wird in dem später andere Knoten suchen
werden. Solche eher unsicheren Algorithmen bezeichnet man als heuristisch. Tapestry dagegen hat ein
ausgeklügeltes System aus verschiedenen Knotentypen die es ermöglicht die Verteilung von Dateien
besser zu kontrollieren und bei massiven Anfragen auf einzelne Dateien flexibler reagieren kann. Bei
Past wird in dem Fall eine große Nachrichtenkaskade auf ein bestimmtes Knotengebiet zukommen,
während bei Tapestry diese Kaskade durch Referenzen in anderen Knoten abgeschwächt wird.
Auch haben Experimente gezeigt [Roe05] dass es kein perfektes Peer-to-Peer-System gibt. Keines
der Systeme kann bei minimaler Anzahl an Hops mit minimaler Bandbreite auskommen, auch nicht
Konkurrenten wie Chord oder Can. Es ist immer ein Trade-Off zwischen Leistung und Kosten.
5
Fazit
Die in der Einleitung geforderten Eigenschaften erfüllen beide Systeme mit Bravour. Diese beiden Systeme sind eine neue Generation von Peer-to-Peer Systemen und logische Weiterentwicklungen von System der alten Generation wie Napster. Der Verzicht auf die Hybridität bringt ernorme Ausfallsicher, geschickte Routing-Algorithmen erzeugen angemessende Geschwindigkeit und
die Erweiterbarkeit ist durch variable Strukturen gesichert. Inbesonders die Beachtung der zugrundeliegenden Netzwerkstruktur macht beide Algorithmen sehr schnell. Tapestry hebt sich durch sein
Objektmanagement-System zusätzlich in den Vordergrund, ist aber im Vergleich zu Pastry wesentlich
komplizierter.
Dennoch sind einigene Probleme noch ungelöst; so ist bei beiden Systemen kein Ansatz zum erkennen und behandeln von potentiell systemschädigenden Knoten vorhanden. Des Weiteren kann beim
Aufall einer großen Anzahl von Knoten es immer noch zu Instabilitäten kommen.
Neue Tendenzen zeigen dass Peer-to-Peer-Systeme immer mehr Einfluß im Internet bekommen wer4
Dieser Algorithmus basiert darauf, dass das Routing sich an den aufsteigen Präfixen der NodeIDs orientiert. Die Laufzeit von O(log(N )) wird an der Abbildung 3 deutlich. Das Ganze ähnelt der Suche in einem binären Suchbaum.
den, und das nicht nur im illegalen Bereich. Inbesondere Linux Distributionen werden heute schon
über Peer-to-Peer-Systeme angeboten um die Server zu entlasten.
Literatur
[ZhKuJo01] Ben Y.Zhao, John D. Kubiatowicz, Anthony D. Joseph, Tapestry: An Infrastructure for
Fault-tolerant Wide-area Location and Routing, University of California, Berkely,
[RoDr01] Antony Rowstron, Peter Druschel, Pastry: Scalable, decentralized object location and routing for large-scale peer-to-peer systems, Microsoft Resarch (Cambridge, UK), Rice University
(TX), 2001
[Jan04] Peter Janacik, Theorie von Peer-to-Peer-Netzwerken: Tapesty, Seminararbeit, Universität Paderborn, 2004 http://wwwcs.uni-paderborn.de/cs/ag-madh/vorl/Perlen/
PJANACIK.pdf
[Sch06] Christian Schindelhauer, Skript zur Vorlesung Peer-to-Peer-Netzwerke SS06, Universität Freiburg, 2006 http://cone.informatik.uni-freiburg.de/teaching/
vorlesung/peer-to-peer-s06/skript/p2pb00k.pdf
[Her02] Andre Herms, Seminararbeit - Pastry, Universität Magdeburg, 2002 http://ivs.cs.
uni-magdeburg.de/bs/lehre/wise0102/sem_bs/t13/pastry.pdf
[Roe05] Holger Röder, Peer-to-Peer Internet Systeme, Hauptseminar Autonomic Computing,
Universität Stuttgart, 2005 http://www.ipvs.uni-stuttgart.de/abteilungen/
vs/lehre/lehrveranstaltungen/hauptseminare/WS0405/Autonomic_
Computing_termine/dateien/Roeder_P2P_2.pdf
Herunterladen