Too Cool for Internet Explorer

ssh von unterwegs


Es kam schon oft vor, dass ich unterwegs gerne einen Blick auf einen Server geworfen hätte. Wie habe ich mich deshalb gefreut, als vor ca. einem Jahr die erste SSH Software für das iPhone angekündigt wurde -- pTerm. Leider erwies sich diese Bezahlsoftware als nicht wirklich ergiebig. Die ersten Versuche zu Hause im WLAN waren zwar erfolgreich, unterwegs über edge wollte das ganze jedoch leider nicht funktionieren. Bis heute weiss ich nicht warum -- die pTerm Diskussionsgruppe war in dieser Hinsicht auch nicht wirklich hilfreich. In der Zwischenzeit wartet man auf ein Update der Software, das nicht kommt.

Nun habe ich kürzlich ein wenig im App-Store gestöbert und bin bei dieser Gelegenheit auf das Tool TouchTerm gestossen, welches -- im Gegensatz zu pTerm -- auch in einer kostenlosen Variante erhältlich ist.

Auf den ersten Blick bietet TouchTerm in der kostenlosen Variante ähnliche Features wie pTerm, mit dem einen Unterschied: es funktioniert auch von Unterwegs über edge problemlos. Die kostenpflichtige Pro Variante -- derzeit mit einem Preis von € 11,99 nicht gerade eine der günstigen Anwendungen im App-Store -- bietet zusätlich Gestures (wobei ich da bei einer Shell Angst hätte versehentlich rm -rf zu erwischen ;-) ), Copy/Paste innerhalb der Anwendung, grafische Verzeichnisnavigation sowie umfangreichere Einstellungsmöglichkeiten.



gearman job queue server


Die letzten Wochen habe ich für die Arbeit einen Workflow Prozessor entwickelt, über den wir zukünftig unsere Medien Uploads verarbeiten werden. Unter anderem gehört zu seinen Aufgaben die Erzeugung von Still Images aus Videos, Wave- und Spektrum-Analyse aus Audiodateien, Erzeugung von Thumbnails und Previews für die Darstellung auf der Web Seite. Der Workflow Prozessor arbeitet von der Console bzw. als Hintergrundprozess (Dämon) und ist in PHP entwickelt. Wird er auf einem Server gestartet, kann der Hauptprozess beliebig viele Worker-Prozesse forken.

Ein Problem bei der Entwicklung war die Verteilung der Prozesse an die Worker. Hier habe ich zunächst auch auf eine reine PHP basierte Lösung gesetzt und die Verteilung der Aufgaben vom Hauptprozess übernehmen lassen, der über Socket-Pairs mit den Worker-Prozessen kommunizieren kann. Bei Tests während der Entwicklung im kleinen Rahmen und mit wenigen Jobs hat das auch wunderbar funktioniert. Bei umfangreicheren Tests traten dann allerdings die Probleme zu Tage.

Einmal abgesehen davon, dass die ganze Sache nicht wirklich der Performance-bringer war, war sie dank der direkten Kommunikation Client->Worker auch fehleranfällig und schlecht bzw. gar nicht skalierbar, denn wie soll man mit dieser Methode mehrere bzw. beliebige Worker-Server transparent ansprechen?

Zum Glück habe ich schon seit einiger Zeit gearman in meinen Lesezeichen. Gearman ist eine Serversoftware, die -- einfach ausgedrückt -- den Zweck erfüllt Jobs zu verteilen. Mögliche Einsatzgebiete sind z.b. die Verteilung von Jobs an Systeme, die besser für die jeweilige Aufgabe geeignet sind, die Lastverteilung auf beliebige Server oder um Jobs parallel abarbeiten zu können. Hierfür wird seitens gearman eine (persistente) Warteschlange implementiert, an die ein Client einen Job senden kann. Ein Worker kann sich einen Job aus der Warteschlange holen und diesen abarbeiten.

Gearman wurde ursprünglich von Brian Fitzpatrick (livejournal, memcached, ...) in Perl entwickelt und vor einiger Zeit von Brian Aker und Eric Day nach C portiert. Der Entwicklungsstand des C Zweiges von gearman ist seiner Version (0.3) zu schliessen noch relativ jung. Insgesamt aber scheint sich das ganze schon derart bewährt zu haben, dass gearman produktiv und unter hoher Last z.b. von digg und yahoo eingesetzt wird.

Zum erfolgreichen Kompilieren von gearman unter OSX muss sichergestellt sein, dass man eine aktuelle Version der libevent Bibliothek installiert hat. Danach lässt sich auch gearman problemlos unter OSX bauen und installieren. Sowohl für libevent wie auch für gearman funktioniert das wie üblich:

$ ./configure
$ make
$ make install

Anschliessend kann man gearman starten. Der Parameter "-d" bewirkt dabei, dass gearman als Hintergrundprozess losgelöst vom Terminal gestartet wird:

/usr/local/bin/gearman -d

Per default lauscht gearman auf Port 4370. Dies ist der offizielle Port, der von der IANA für gearman reserviert wurde.

Client/Worker APIs zur Kommunikation mit gearman gibt es bereits für C (enthalten im gearman Download), Perl, MySQL (als UDF) und PHP. Zum Ansprechen von gearman unter PHP gibt es ein pear package, net_gearman, sowie seit kurzem auch eine native Erweiterung für PHP, die allerdings noch experimentell ist und dementsprechend mit Vorsicht zu geniessen ist. Unter OSX konnte ich sie z.b. zwar einwandfrei kompilieren und installieren, die ersten Versuche scheiterten dann aber an segfaults. Nicht weiter tragisch, da das pear paket bisher einwandfrei seine Arbeit macht.

Eine Client Anwendung, die Jobs an den gearman Server übergibt, ist mit dem pear Paket schnell entwickelt:

#!/usr/bin/env php
<?php

require_once('Net/Gearman/Client.php');

$set = new Net_Gearman_Set();

function result($resp) {
    print_r($resp);
}

$files = array(
    '/Users/harald/Movies/00001.mov'
);

foreach ($files as $file) {
    print "adding task $file\n";
    
    $task = new Net_Gearman_Task('test', array(
        'filename'  => $file
    ));

//    $task->attachCallback('result');
    $set->addTask($task);
}

print "running set ...\n";

$client = new Net_Gearman_Client(array('127.0.0.1:4730'));
$client->runSet($set);

?>

Der erste Parameter beim Instanzieren der Klasse Net_Gearman_Task spezifiziert übrigens den Namen des Jobs, der vom Worker registriert werden muss, damit dieser entsprechende Jobs annehmen und verarbeiten kann. Normalerweise wartet der Client, bis alle Jobs abgearbeitet sind und kann wenn eine Callback Methode registriert wurde, mögliche Ergebnisse verarbeiten, die die Worker zurückliefern. Dieses Verhalten kann möglicherweise unerwünscht sein, z.b. wenn viele Jobs eingereicht werden und der Client während der Abarbeitung nicht blockiert sein soll. Hierfür implementiert die Net_Gearman_Task Klasse ein öffentliches Property:

public $type = self::JOB_NORMAL;

Um das Verhalten zu ändern, sind folgende Typen definiert:

  • Net_Gearman_Task::JOB_NORMAL -- Standardverhalten: Client blockiert während der Abarbeitung der Jobs
  • Net_Gearman_Task::JOB_BACKGROUND -- Client reicht Jobs an gearman weiter und blockiert nicht
  • Net_Gearman_Task::JOB_HIGH -- selbes Verhalten wie JOB_NORMAL, allerdings wird die queue übersprungen und ein Job kann sofort von einem Worker verarbeitet werden. Sinnvoll z.b. für höher priorisierte Jobs

Eine einfacher Klasse, die die Aufgabe 'test' implementiert, könnte folgendermassen aussehen:

<?php

class test extends Net_Gearman_Job_Common {
    function run($args) {
        print_r($args);
        
        print "sleeping 5 seconds ...\n";
        sleep(5);
        print "done!\n";
        
        return "OK!";
    }
}

?>

Der Worker selbst wird folgendermassen aufgebaut:

#!/usr/bin/env php
<?php

define('NET_GEARMAN_JOB_CLASS_PREFIX', '');
define('NET_GEARMAN_JOB_PATH', '.');

require_once('Net/Gearman/Worker.php');

$worker = new Net_Gearman_Worker(array('127.0.0.1:4730'));
$worker->addAbility('test');
$worker->beginWork();

?>

Normalerweise erwartet Net_Gearman Klassen, die Aufgaben implementieren unterhalb eines Verzeichnisses, das in der PEAR Verzeichnisstruktur angesiedelt ist. Weil man dies normalerweise nicht möchte, gibt es folgende Konstanten, die man mit eigenen Werten füllen kann um so alternative Verzeichnisse zu setzen:

// spezifiziert ein Prefix für den Namen der Klasse
define('NET_GEARMAN_JOB_CLASS_PREFIX', '');

// spezifiziert das Verzeichnis, in dem Klassen gesucht werden
define('NET_GEARMAN_JOB_PATH', '');

Unschön ist, dass für einzubindende Klassen Dateien mit folgendem Namensschema erwartet werden, z.b: test.php. Da ich meine Klassen immer z.B. -- test.class.php -- benenne, ist dies also leider eine kleine Unschönheit, mit der man offenbar leben muss.

Zum Abschluss sei noch zu sagen, dass gearman in den ersten Tests mit mehreren 1000 Jobs in der Queue seine Arbeit tadellos und sehr zufriedenstellend erledigt hat.

Empfohlene weiterführende Links:



JavaScript: The World's Most Misunderstood Programming Language


Eigentlich sollte ich stolz sein: es gibt tatsächlich Leser meines Blogs, die meine Artikel aufgreifen und sich darüber kritische(?) Gedanken machen. Gestern bin ich zufälligerweise auf einen Artikel in einem Blog gestossen, in dem sich der Autor über meine Einstellung zu JavaScript lustig macht. Bei all seinem Sarkasmus vergisst der Autor leider Argumente vorzubringen warum er meine Einstellung so lächerlich findet. Die im Text eingestreuten Klischees, die man ähnlich so eigentlich nur aus dem Heise-Forum kennt, können es kaum sein.

Dabei kann ich die Einstellung tatsächlich sogar verstehen: In JavaScript lässt es sich ähnlich schmutzig und disziplinlos programmieren, wie mit PHP. Nur: dort wo PHP einem Steine in den Weg legt, weil der Parser viele dinge einfach nicht zulässt, ist JavaScript in der Regel offen und man kann einen sehr sauberen und eleganten Programmierstil fahren. Nun ist es bei JavaScript so wie mit jeder Programmiersprache: man muss sich damit beschäftigen. Was PHP und JavaScript darüberhinaus gemein haben ist, dass es für beide Sprachen wenig gute Literatur, dafür aber umso mehr schlechte Literatur gibt.

Bei ernsthaftem Interesse an JavaScript möchte ich an dieser Stelle auf einen Artikel von Douglas Crockford verweisen -- JavaScript: The World's Most Misunderstood Programming Language. Dieser Artikel räumt mit einigen Vorurteilen gegenüber JavaScript auf und macht vielleicht auch verständlich, warum JavaScript einen derart schlechten Ruf hat.



ClipDealer Widget Test




Todo für 2009


Das Jahr neigt sich dem Ende und es wird Zeit eine Liste der Dinge zu erstellen, mit denen man sich 2009 beschäftigen möchte. Auf meine Liste setze ich folgende Punkte:

  • php 5.3 -- ich möchte einen Entwicklungszweig meines PHP Frameworks einrichten, in dem ich mein Framework auf einige der tollen Features von PHP 5.3 vorbereite. z.B.: Operator Overloading und Closures.
  • Ich möchte weiterhin die äusserst interessante dokumentbasierte Datenbank couchDB im Auge behalten und evtl. eine kleine Anwendung dafür schreiben. Ich plane eigentlich schon seit Ewigkeiten meinem Blog eine andere Software zu spendieren -- evtl. wird es also eine simple couchDB basierte Blog Software.
  • Es gibt jetzt eine Installation der Datenbank DB2 Express-C von IBM für OSX -- da mich Datenbanken generell interessieren, möchte ich also einen Blick auf DB2 werfen.
  • Ich möchte mir ein paar Javascript Bibliotheken ansehen. Insbesondere script.aculo.us. Da diese Bibliothek auf prototype aufsetzt, muss ich mich auch mit eben jenem beschäftigen. Ich bin eigentlich kein Freund von Prototype und vergleichbaren Dingen, da ich der Meinung bin, dass man nicht alles machen muss, was man machen kann und ich eigentlich auch nicht wirklich den Sinn darin sehe Javascript eine andere Form der Objektorientierung überzustülpen, wo die bestehende Form doch so wunderbar funktioniert. Aber mal sehen ... vielleicht kann ich mich ja doch damit anfreunden.
  • Vielleicht gelingt es mir ja 2009 mich ein wenig mit Cocoa und Objective-C zu beschäftigen.

Das ist eine lange Liste und ich bin gespannt, welches Fazit ich in einem Jahr ziehen kann ...



Microsoft vs. Apple


Völlig untypisch für mich und eigentlich fast unglaublich, dass ich jemandem mal ein Produkt von Microsoft empfehle. Aber es ist in der Tat passiert ...



multiple ssh


Heute wollte ich mal den "slave status" unserer 5 Datenbank Server bei Pixelio ansehen. Natürlich ist es schrecklich umständlich sich auf allen Servern einzeln anzumelden um immer und immer wieder den gleichen Befehl auszuführen. Hierfür gibt es Tools, die ein Kommando auf mehreren Servern ausführen können (sollen). Ausprobiert habe ich pssh und dsh. Jedoch, mit beiden bin ich nicht klar gekommen. Da ich wie gesagt nur eben schnell mal einen Befehl auf den Slaves ausführen wollte, habe ich auch keine Lust gehabt, mich intensiv mit den Tools zu beschäftigen:

pssh ist python basiert, schon der erste Minuspunkt -- aber gut, da OSX eh python mitbringt, hab ich's halt installiert und konfiguriert. Allerdings wollte pssh keine Ausgabe zurück liefern und nach dem Ausführen hatte ich plötzlich jede menge SSH prozesse laufen, die ich manuell killen musste.

dsh hat imo eine mehr als rudimentäre Dokumentation. Nach Ausgiebigem googeln wusste ich dann auch, dass man offenbar eine Liste der Server benötigt, mit denen man sich Verbinden will und ausserdem noch Servergruppen anlegen kann. Klingt gut. Allerdings kann dsh offenbar meine "~/.ssh/config" nicht lesen und kennt daher auch meine hosts nicht. Da ich auf die Schnelle nicht in Erfahrung bringen konnte, wie ich in der Serverliste den Port spezifiziere (mit ":" ging es jedenfalls nicht), habe ich das also auch sein lassen.

In der Zwischenzeit hätte ich sicherlich auch 5x manuell den slave status ausführen können -- aber gut, man ist ja faul. Nachdem ich also pssh und dsh von meiner festplatte verbannt hatte, habe ich mir dann in ca. 1 Minute selbst ein kleines Shell-Script zusammengezimmert, das zwar bei weitem nicht die Features von pssh und dsh bietet, aber dafür für meine Zwecke problemlos funktioniert:

#!/usr/bin/env sh
SERVERS=`cat ~/.dsh/$1`

for i in $SERVERS
do
        echo $i
        ssh $i $2
done

Das Script habe ich unter "/usr/local/bin/dsh" abgelegt und ist damit in meinem Pfad erreichbar. In der Datei "~/.dsh/dbslaves" habe ich mir eine Liste meiner Slaves angelegt:

slave-1
slave-2
slave-3
slave-4
slave-5

Diese Hosts habe ich ohnehin schon in meiner "~/.ssh/config" drin. Ausserdem sind auf den Servern meine Keys für passwortloses Login installiert. Nun kann ich über:

dsh dbslaves "mysql -uroot -p... -e 'SHOW SLAVE STATUS\G'"

bequem meinen Slave Status abfragen oder andere Kommandos ausführen.



reactable


Eigentlich mag ich in diesem Blog keine youtube Videos posten, aber da ich ein Fan von innovativen und revolutionären Eingabe- und Bedienungsmethoden bin, muss ich das hier einfach posten.

Über ein paar Umwege bin ich darauf gestossen, als ich Videos zu dem Software-Synthesizer psychosynth gesucht habe, dessen Eingabemethode ganz offensichtlich von reactable inspiriert ist.

Gigantisch mit einem hohen "haben-will"-Faktor -- wer mag mir einen reactable zu Weihnachten schenken? ;-)


Neue Serverlandschaft bei pixelio.de


In den vergangenen fünf Jahren ist pixelio.de aus einem Hobbyprojekt eine kleine Firma geworden und aus ehemals einem Server ist eine Serverlandschaft mit 10 Servern und einem Loadbalancer entstanden. Die letzten Wochen haben wir endlich unser Netzwerk umstrukturiert und auf eine solidere Basis gestellt, sodass wir es zukünftig leichter haben weitere Server aufzunehmen. Dabei muss man mal ein Lob an HostEurope aussprechen, die im Service und Support eine wirklich gute Arbeit leisten -- es hat schon einen Vorteil, wenn man direkt mit Technikern am Telefon reden kann im Vergleich zu anderen Providern, wo das nur über einen Call-Center möglich ist und das ganze einem Stille-Post ähnelt (und die Ergebnisse entsprechend ausfallen).

Wir nutzen nachwievor eine alte Software, die eigentlich nicht für eine derart grosse Bilddatenbank ausgelegt ist und ausgesprochen schlecht skaliert. Um das Datenbankproblem in den Griff zu kriegen, setzen wir seit einiger Zeit schon den mysql-proxy von Jan Kneschke ein. Dieser Proxy kann transparent zwischen die Datenbank und eine Web-Anwendung gehängt werden und lässt sich über lua voll steuern. Über ein lua Script können wir so alle Queries parsen, der Proxy entscheidet dann welche Queries an den Master gehen müssen und welche an die Slaves. Das funktioniert sehr gut -- viel besser als eine reine PHP basierte Lösung, die wir ursprünglich mal in die Datenbankschicht der Bilddatenbank-Software gestrickt hatten.

Für nächstes Jahr ist endlich die Einführung einer von Grund auf neu entwickelten Software angedacht, die wir derzeit schon bei clipdealer.de im Einsatz haben. In diesem Zuge werden wir dann auch einen memcache-Cluster aufbauen, der die Datenbank massiv entlasten soll.

Ich bin gespannt, wie das Server-Diagramm in weiteren fünf Jahren aussehen wird ...



Ich liebe JavaScript


Ich gestehe: ich liebe JavaScript -- ich habe noch keine Programmiersprache kennengelernt, in der es sich ähnlich intuitiv entwickeln lässt. Die Sprache ist absolut logisch aufgebaut, es gibt eigentlich nie aha-Effekte, dass etwas unerwarteterweise nicht möglich ist. Ähnlichen Spass am Programmieren hatte ich zuletzt mit Turbo Pascal vor vielen Jahren. Sollte es irgendwann eine ernstzunehmende serverseitige JavaScript Implementation geben, die sich über fastCGI in meinen bevorzugten Webserver einhängen lässt, werde ich (wahrscheinlich :-)) nicht zögern PHP hinter mir zu lassen um auch serverseitig JavaScript einzusetzen.

Mit der neuen JavaScript Engine von Google -- v8 -- die erstmals im Google Browser Chrome zum Einsatz kam, ist der Traum vielleicht irgendwann keiner mehr. Da die JavaScript Engine im Quellcode erhältlich ist und die Bibliothek sich ohne Probleme unter den wichtigsten Betriebssystemen compilieren lässt, sind schon zahlreiche Projekte entstanden, die v8 auch serverseitig einsetzbar machen sollen.

Im Moment spiele ich hier ein wenig mit llv8call -- eine Bibliothek, die v8 um Zahlreiche Funktionen erweitert, wie z.B.:

  • File und Dir
  • System Variablen
  • STDIN, STDOUT und STDERR
  • Process Management / Prozess Kontroll Funktionen
  • Dynamic Library Loader
  • Sockets
  • libxml Support

... und ausserdem noch folgende Erweiterungen mitbringt:

  • memcached
  • fastCGI Support
  • sqlite 3 Support
  • Curl

Auch eine Verbesserte Shell (im Vergleich zur mit v8 Verfügbaren Shell) ist vorhanden, die nun Dank libreadline mehr Komfort mitbringt (z.b. die Befehlshistorie).

Zur Installation unter OSX sind evtl. folgende Tools und Bibliotheken notwendig:

Sofern man die Entwicklungstools von OSX installiert hat, lassen sich die genannten Bibliotheken ohne weiteres installieren. Lediglich bei ClearSilver musste ich den Ruby-Support deaktivieren:

./configure --disable-ruby

... und nach der Installation einen Symlink erstellen, damit llv8call beim Compilieren die Header-Dateien von ClearSilver einbinden konnte:

ln -snf /usr/local/include/ClearSilver /usr/include/ClearSilver

Wenn scons ohne Fehler durchgelaufen ist, kann man die erzeugten Binaries unter ./out/ im llv8call Quellverzeichnis finden.



Macbook


Das neue Macbook auf meinem Schreibtisch zu Hause. Sehr schick -- aber leider nicht mein eigenes, sondern das von Freunden. Vor einigen Monaten schon segnete der Windows PC von Freunden das zeitliche und ich konnte sie erfolgreich zum Kauf eines Macs überreden. So wurde damals ein Mac Mini angeschafft. Offensichtlich sind meine Freunde mit dem Mac und Mac OS derart zufrieden, dass der geplante Kauf eines zusätzlichen Portablen Computers -- ursprüngliche Planung: Sony Vaio -- nun auch im Kauf eines Macbooks endete ... ich bin sehr zufrieden :-)






Apple Store München


Jedes Jahr am 6.12. bringt der Nikolaus den kleinen Kindern Süssigkeiten und Leckereien. Dieses Jahr hatte er für die grossen Kinder noch den Apple Store mit im Gepäck -- offensichtlich waren die münchner Kinder besonders brav ;-). Zur Eröffnung war ich auch dort und habe nach über einer Stunde Wartezeit auch mein Begrüssungsgeschenk überreicht bekommen ...












php + ncurses + osx


Benötige ich demnächst bestimmt noch 1-2x, deshalb hier auf alle Ewigkeit festgehalten. Zunächst ncurses saugen und über ...

./configure --prefix=/usr/local --with-shared --disable-rpath --without-debug --without-ada --enable-safe-sprintf --enable-sigwinch --without-progs
make
make install

... konfigurieren und installieren. Anschliessend kann man ncurses über pecl installieren:

pecl install ncurses

... und mit der ncurses Entwicklung in Verbindung mit php loslegen.

p.s.: ich bin kein Fan von "(darwin)ports" oder "fink", aber manchmal sind sie ganz nützlich um sich die richtigen Konfigurationsschalter herauszusuchen: http://ncurses.darwinports.com/



Stellenangebot: PHP Entwickler in München


Zur Verstärkung unseres kleinen Teams in München suchen wir ab sofort einen erfahrenen PHP Entwickler (m/w) für unsere Webseiten clipdealer.de und pixelio.de. Bei clipdealer.de handelt es sich um einen neuen online Marktplatz für lizenzfreie Footage Clips und pixelio.de ist die größte kostenlose Bilddatenbank für lizenzfreie Fotos im deutschsprachigen Raum.

Ihre Aufgaben:

  • eigenverantwortliche Optimierung und Weiterentwicklung unserer Webseiten
  • die Konzeption und Erstellung neuer Module

Sie sind zuverlässig, teamfähig, haben Spaß am Programmieren und verfügen über folgende fachlichen Kenntnisse:

  • PHP5 sowie Kenntnisse mit einem objektorientierten Framework
  • MySQL 5 (u. a. Trigger, Stored Procedures)
  • Objektorientiertes JavaScript
  • Sicherer Umgang mit der Shell sowie mit den gängigen Tools
  • Linux und evtl. OSX Kenntnisse
  • Kenntnisse einer Versionsverwaltung z.b. Subversion oder CVS

Von Vorteil sind darüber hinaus folgende Kenntnisse:

  • Konfiguration eines Webservers (Apache, Lighttpd)
  • Performanceoptimierung (z.B. Lastverteilung bei Webservern und Datenbanken, Caching etc.)
  • Mehrjährige Erfahrung in der Entwicklung von PHP basierten, stark frequentierten Internetseiten

Wir bieten ein angenehmes Arbeitsumfeld in einem jungen, motivierten Team mit viel Eigenverantwortung und Raum für neue Ideen. Schicken Sie uns Ihre aussagekräftige Bewerbung bevorzugt per E-Mail oder Post mit einer detaillierten Beschreibung der bisherigen Tätigkeiten und Erfahrungen sowie allen erworbenen Zeugnissen und Ihren Gehaltsvorstellungen an folgende Adresse:

Per E-Mail:

m.hein@clipdealer.de

Per Post:

Clipdealer GmbH
Herr Markus Hein
Lipowskystr. 25
81373 München



input-monitor


wordle analyse meines RSS feeds -- könnte man sich fast ausdrucken und an die wand hängen ;-)