Freitag, 14. Dezember 2012

No nodejs für arbeitsintensive Dauerleistung


Die Wunderwaffe für die Webentwicklung ist nicht als Arbeitstier-Skriptwerkzeug zu gebrauchen. Solange Events ankommen und Daten aus dem Arbeitsspeicher gesendet werden sollen, ist nodejs wirklich effizient. Aber stupides Datenauslesen, Dateien anlegen und diese wieder verarbeiten bringt nodejs schnell aus der Puste.

Klar es läuft nur auf einem Thread und das spürt man dann auch. Es arbeitet ineffizient bei vielen Datenzugriffen.

Durch die asynchronen Abläufe kommt es leider auch zu ungewünschten Nebeneffekten. Das Abarbeiten von grossen Aufträgen kann damit zur Tortur mit OutOfMemory werden.

Der Versuch ist einfach: 


- Lese 10.000 Datensätze aus der Datenbank
- Speicher die Daten in Dateien
- Öffne diese Dateien und lese Inhalt ein
- Versende Inhalt per Webrequest
- Erfasse Server-Antwort in Datei
- Benenne Datei mit Zusatz "closed" um

Was machen wir, um dieses Verhalten zu testen?

Dazu ein einfaches Skript,welches Daten aus einer Datenbank zieht. Ob wir nun alle Datensätze abholen und dann über diese iterieren, oder bei jedem Zeilentreffer die Verarbeitung starten ist egal. Diese minimalen Unterschiede können wir ignorieren.

Wir brauchen noch eine Funktion, die den Dateninhalt einer Zeile als JSON abspeichert. Zuerst wählen wir die asynchrone Variante. Die Funktion erstellt eine Datei mit Namen: Datensatz-Id_OPEN.txt. Am Ende dieser Funktionen rufen wir das Abarbeiten der Datei auf. 

Dazu erstellen wir eine weitere Funktion, welche die Datei öffnet, einliest und einen Webserver kontaktiert. Wir senden die Daten als POST-Request. Geht auch sehr einfach mit nodejs. Die Antwort vom Server hängen wir an die Datei und benennen diese wiederum um. Neuer Name: Datensatz-Id_CLOSED.txt.

Warum diese Aufwand? 

Dadurch, dass die schreibende Funktion nicht auch gleich versendet, können wir eine Prozess-Wiederaufnahme Option einbauen. So könnten wir eine separate Funktion starten, welche über die Dateien geht, welche nicht im Zustand (im Namen) CLOSED haben und diese dann verarbeiten. Im Grunde wäre es hier ratsam, noch einen weiteren Status einzuführen: PROCCESSING oder so, damit man auch erkennt, welche direkt in der Mache waren. Können wir uns sparen und für diesen Test nehmen wir an, dass der Server Duplikate verwirft.

Was passiert nach dem Starten vom Skript?

Wir starten das nodejs Skript (ohne Aufräumarbeit durch separeten Aufruf für abgebrochene Übertragungen) und zuerst werden die Daten aus der Datenbank geholt. Das geht sehr schnell, so schnell wie es die DB erlaubt. Dann iterieren wir über die Datensätze und rufen die Funktion auf, zum erstellen der Dateien.

Hier das erste interessante. Damit wir mitbekommen, was passiert geben wir bei jedem Funktionsaufruf eine Message auf der Konsole aus. Wir sehen, dass nun 10.000 mal die Funktionen für das Datei erstellen aufgerufen wird. Aber die Message für das schreiben in die Datei erscheint nicht. Ein Blick in den Ordner zeigt auch, dass tatsächlich keine Datei erstellt wird. Das Skript baut die JSON-Texte auf und hat vor dem  Schreiben scheinbar einfach alles liegen lassen. Nach den 10.000 Meldungen, schreibt er nun tatsächlich. Er beginnt die Dateien anzulegen. Nach dem Anlegen sollte die Verarbeitung starten, doch das passiert nicht. 

Ja klar, ist alles asynchron, jedoch sollte man meinen, dass er nicht erst alle Dateien anlegt, bevor er dann zum Verarbeiten (Senden der Daten an den Webserver) kommt. Doch genau das passiert. Bis hierher kann man noch mehr oder weniger das Verhalten akzeptieren.

Um sicher zu gehen, dass auch nicht unsere Vorgehensweise Schuld ist, ändern wir die Datenbankabfragen-Funktion so, dass nun jede Zeile verarbeitet wird. Doch genau das Gleiche Phänomen. Um nun mehr zu forcieren, wird auch das Schreiben nun synchron gemacht. Das verbessert ganz leicht den Ablauf, aber zu Lasten von Geschwindigkeit. 

Irgendwann beginnt nun dann die Verarbeitung des Sendens. Das Skript beginnt mit dem Lesen und meldet dies auch pflichtbewusst. 10.000 Dateien werden nun geöffnet, ohne dass scheinbar gelesen wird!!! Selbst wenn er die Daten gelesen hat, werden die weiteren Befehlszeilen nicht ausgeführt. Der Speicher läuft, das ganze System bekommt langsam Probleme. 

Erst nachdem scheinbar alle 10.000 Dateien gelesen wurden, beginnt er mit dem Versenden. Doch die Antwort wird nicht erfasst. Stattdessen müssen wir wiederum warten bis alle 10.000 Webrequests versendet sind. Selbst wenn wir das einlesen synchron machen, haben wir spätestens beim "immer" asynchron arbeitenden Web-Request die Probleme. 

Fazit

Nodejs ist super, wenn man einen eigenen flinken Webserver braucht, um einen Dienst bereit zu stellen, der wenig auf der Festplatte arbeitet. Alle File-Operationen sind grausam. Leider ist damit nodejs für mich nicht mehr in der engen Wahl, wenn es darum geht, grosse Datenmengen zu verarbeiten. Das ist nicht sein Metier und man sollte es auch nicht erzwingen. 

Mittwoch, 21. November 2012

Einfacher Webclient für POST und GET Formulare Senden Tests

Webclient für einfache POST und GET Requests

Wer kennt das nicht. Man will eben ein paar Abfragen an das neue Serverprogramm oder an die neue PHP-App senden und muss dabei die Parameter immer wieder anpassen. Mühselig, kann man die URL entsprechend in den Browser tippen, doch dann muss man plötzlich POST senden.

Damit man nicht immer wieder ein neues HTML-Formular oder ein spezielles PHP-Skript oder gar per Console eine Curl Aktion draus macht, wäre ein komfortables Webseiten-Tool doch ganz nett. Am besten gleich mit History, um einen Request nochmal senden zu können.

Voila:

 <html>  
 <head><title>Testclient für Webaufrufe</title>  
 <meta charset="utf-8">  
 <script type="text/javascript">  
 function makeURIParameterForm(text, form) {  
      // entferne alt lasten  
      // gehe über alle elemente  
      // wenn eines dyn gesetzt wurde entfernen, bevor die neuen kommem  
      var elemente = []; // werden entfernt
      for (var a=0;a<form.elements.length;a++){  
           var elem = form.elements[a];  
           if (elem.tag && elem.tag == "dyn"){  
                elemente.push(elem);  
           }  
      } 
      for (var a=0;a<elemente.lenght;a++)
           form.removeChild(elemente[a]);

      // extrahiere die elemente und  
      var teile = text.split("&");  
      for (var a=0;a<teile.length;a++){  
           var t = teile[a].split("=");  
           var elem = document.createElement("input");  
           elem.type = "hidden";  
           elem.value = t[1];  
           elem.name = t[0];  
           elem.tag = "dyn";   
           form.appendChild(elem);  
      }   
 }  
 function senden(form){  
           var object = {};  
           object.post = form.elements[1].checked;  
           object.target = form.elements[2].value.trim();  
           object.url = form.elements[0].value.trim();  
           object.body = form.elements[3].value.trim();  
           object.form = form;    
           liste.push(object);  
           var elem = document.createElement("option");  
           elem.value = liste.length - 1;  
           elem.text = "["+((object.post)?"POST":"GET")+"] "+object.body;  
           document.getElementById("liste").appendChild(elem);  
           fuelleForm(form, object);             
 }  
 function ladeForm(idx){  
      var object = liste[idx];  
      fuelleForm(object.form, object);  
      document.getElementById("info").innerHTML = "Form geladen: idx: "+idx;  
 }  
 function zeigeForm(idx) {  
      var object = liste[idx];  
      var text = "IDX: "+idx+"<br>";  
      for (var key in object)  
           if (key != "form")  
                text += '<b>'+key+"</b>: "+object[key]+"<br>";  
      document.getElementById("info").innerHTML = text;       
 }  
 function fuelleForm(form, object) {  
      form.target = object.target;  
      form.elements[3].value = object.body;  
      var url = object.url;  
      var body = object.body;
      if (url.indexOf("?")>0){
           var pos = url.indexOf("?");
           body = url.substr(pos+1)+"&"+body;
      }
      makeURIParameterForm(body, form);       
      if (object.post){  
           form.elements[1].checked = true;  
           form.method='POST';  
      } else {  
           form.elements[1].checked = false;  
           form.method='GET';  
      }  
      form.action = url;  
      document.getElementById("info").innerHTML = "Gesendet an: "+form.action;  
 }  
 var liste = [];  
 </script>  
 </head>  
 <body>  
 <div style="width:70%;float:left;">  
 Daten senden
 <form onsubmit="senden(this);return true;">  
 url: <input type=text style="width:70%;" hint="http://localhost/index.php"><br>  
 <input type=checkbox> POST<br>  
 target: <input type=text value="ziel"><br>  
 Body (bsp: para1=1213&amp;para2=2233):<br>  
 <textarea style="width:100%;"></textarea><br>  
 <div style="text-align:right;"><input type=submit></div>  
 </form>  
 </div>  
 <div style="width:25%;float:right;">  
 <form onsubmit="ladeForm(this.elements[0].options[this.elements[0].options.selectedIndex].value);return false;">  
      <select size=10 style="width:100%;" id="liste" onclick="zeigeForm(this.options[this.options.selectedIndex].value);">  
      </select><br>  
      <div style="text-align:right;"><input type=submit value="laden"></div>  
 </form>  
 </div>  
 <div style="clear:both;"></div>  
 <hr>  
 <div id="info"></div>  
 <hr>  
 <iframe name="ziel" style="width:100%;heigth:300px;boder:1px solid blue;"></iframe>  
 </body>  
 </html>  

Warum nur 300px für das Iframe? Weil ich den Debugger noch unten eingeblendet habe. Also nur ein Platz Problem. Könnt Ihr euch ja beliebig anpassen.

Viel Spass damit
Saso Nikolov

Freitag, 9. November 2012

Websockets scheinen tot, bevor diese richtig die Welt erblickt haben

Websockets Ade

Wer schon mal versucht hat Websockets einzusetzen, hat bestimmt schon erlebt, wie unnötig kompliziert alles ist. Hat man die Clientseite gemeistert, die zugegebener Massen einfach ist. Wird die Serverseite zum Horrorgang. Wohl gemerkt, immer mit der Prämisse, dass man alles selbst programmiert und keine Software anderer einbindet.

Wozu Websockets?

Man kann Websockets dazu nutzen, dass man im Webbrowser über Aktionen sofort informiert wird. Der gern genommen Chatserver soll hier als Beispiel dienen. Wäre doch cool, ohne Flash und anderem einfach ein Chat in HTML anzubieten. Würde dann auch auf allen mobilen Geräten funktionieren und im Browser deiner Wahl. 

Allerdings gibt es keine direkte Verbindung vom Server zurück zum Browser, wenn die Seite ausgeliefert wurde. Wenn nun einer im Chat was schreibt, müssten die anderen Chatter immer wieder nach Aktualisierungen beim Server fragen. Das ganze muss dann noch synchronisiert werden, damit die Antworten passen in der Reihenfolge angezeigt werden. Wenn das Intervall der Abfragen an den Server für Änderungen niedrig ist, dann kommt alles sehr verzögert an. Sind die Intervalle zu hoch, legen wir Browser und Server lahm.

Das alles wird mit den Websockets behoben. Der Browser baut eine Verbindung zum Server auf und hält diese offen. Sobald eine Aktualisierung im Server bekannt wird (ein Chatter hat was gesagt), sendet der Server diese Nachricht an alle offenen Websockets und der Browser schreibt mittels Javascript die Nachricht sofort auf die Webseite.

Das Ganze ist auch Ideal um Spiele und anderes Event bezogenen Überwachungen im Webbrowser zu realisieren. Kein Installieren und dennoch up2date.

Toll, doch was ist nicht gut?

Nicht gut ist, dass Websockets noch nicht von allen Browser unterstützt wird und noch weniger der Otto-Normal-Entwickler dies auf seinem angemieteten Server zusammen programmieren kann.

Wir brauchen eine Lösung, die mit jedem Browser (egal wie alt funktioniert). Allerdings benötigen wir mindestens Javascript bei allen Lösungen.

Alternativen

Ok, was können wir machen. Das übliche Überlegen. Nicht immer sofort das neueste implementieren, sondern sehen, ob wir es dem Kunden nicht so einfach wie möglich machen wollen.

Nach einer kurzen Analyse des Browser sehen wir einen Ansatzpunkt. Der Browser erstellt per Javascript ein Script-Tag im Header. Dieser dynamisch nachgeladene Javascript Code realisiert die Aktualisierungen. Das habe ich im Apicall-Verfahren schon demonstriert. Doch dann muss das Intervall hochgemacht werden?
Nein. Wir erinnern uns an damals, als das Internet noch sehr langsam war. Damals wurde einfach gewartet. Das tun wir in unserer Lösung auch. 

Die meisten Browser brechen nach 1 Minute Wartezeit ab. Also setzen wir hier an und wählen 30-40 Sekunden. Der Browser verbindet sich mit dem Server. Der Server antwortet nicht sofort. Sondern speichert diese Verbindung global. Kommt nun eine Aktualisierungsmeldung. Werden alle gehaltenen (verzögerten) Javascript -Code Wünsche durchlaufen und jeder bekommt die Aktualisierung gemeldet.

Der Browser baut daraufhin sofort wieder eine Verbindung mittels Apicall auf. Auf dem Server lassen wir einen Job laufen, der immer wieder die veralteten Verbindungen beantwortet. Einfach eine leere Meldung in die Apicall Antwort einbauen. Dann ist alles sauber. So kann man auch komplett abgemeldete Verbindungen killen. 

Das Ganze ist nicht so einfach mit PHP zu lösen. Doch es geht. Hier müsste man einen gemeinsamen Speicher nutzen. Meine Lösung ist in node.js realiesert. Ich bin einfach begeistert, was man mit nodejs machen kann. Schnell und einfach kann man wunderbare Sachen zaubern. Seht euch das Video auf der Seite an!

Code Beispiel für eine Chat-App:

HTML (Der Client):

Die Basics.js ist ein Sammlung von Hilfsfunktionen von Directpaylink:
http://directpaylink.com/javascript/basics.js
Da ist auch viel unnötiges drin, so dass ich immer eine Kopie herunter lade und diese dann von Ballast befreie.


 <html>  
 <head>  
 <meta charset="utf-8">  
 <style type="text/css">  
 li {list-style-type:none;padding-left:2px;}  
 .msg:nth-of-type(even) { background: #dedede; }  
 .msg:nth-of-type(odd) { background: #fefefe; }  
 </style>  
 <script type="text/javascript" src="basics.js"></script>  
 <script type="text/javascript">  
 var system = {};  
 system.url = "http://localhost:8080/";  
 system.nohtml = true;  
 function konnektieren(adresse, usr) {  
      system.url = "http://"+adresse+":8080/?usr="+encodeURIComponent(usr);  
      system.usr = usr;  
      system.adresse = adresse;  
      message("Verbinde mit: "+system.url);  
      verbinden(true);  
 }  
 function verbinden(force) {  
      message("Hole Sessionid");  
      // wenn kein session code vorhanden  
      if (!force && system.sessionid)  
           return true;  
      var url = system.url;       
      apicall(url,   
           function(h) {  
                // sessionid als antwort  
                var t = h.responseText.split(";");  
                system.sessionid = t[0];  
                message("sessionid bekommen: "+system.sessionid);  
                document.getElementById("chat").style.display = "block";  
                message("gehe über auf Events zu warten. Chatten ist möglich");  
                system.wartesekunden = t[1];  
                setInhalt("info", system.wartesekunden);  
                warteAufEventsVomServer(system.wartesekunden);  
                document.getElementById("tt").select();  
           },   
           function(h){  
                message("fehler sessionid abzuholen. Kein weiterer versuch");                 
           });       
 }  
 function warteAufEventsVomServer(wartesekunden) {  
      if (!wartesekunden)  
           wartesekunden = 20*1000;  
      if (!system.sessionid)  
           return alert("keine session vorhanden");  
      var url = system.url+"&sessionid="+system.sessionid;       
      // baue verbindung zum server auf  
      apicall(url,   
           function(h) {  
                if (checkResponse(h.responseText)) {  
                     //message("chattext erhalten [EVENT]: "+h.responseText);  
                     var code = h.responseText.substr(0,h.responseText.indexOf(":")).trim().toLowerCase();                      
                     switch(code) {  
                          case "reload":  
                               reload();  
                          case "chat":  
                               if (system.nohtml) {  
                                    message('<b>'+Date2Text()+" "+removeHTMLTags(decodeURIComponent(h.responseText.substr(6)))+'</b>');  
                               } else {  
                                    message('<b>'+Date2Text()+"</b> "+decodeURIComponent(h.responseText.substr(6)));  
                               }  
                               // bei antwort, erneut verbinden  
                               warteAufEventsVomServer();  
                     }  
                } else { // error  
                     system.sessionid = false;  
                     verbinden(); // neue session aufbauen       
                }  
           },   
           function(h){  
                //message("kein event. warte auf den nexten event");                 
                // bei timeout erneut verbinden  
                warteAufEventsVomServer(wartesekunden);  
           }, wartesekunden);       
 }  
 function reload() {  
      var t = "http://"+window.location.host+window.location.pathname+"?reload&usr="+  
           encodeURIComponent(system.usr)+"&sessionid="+  
           encodeURIComponent(system.sessionid)+"&adresse="+  
           encodeURIComponent(system.adresse)+"&wartesekunden="+  
           encodeURIComponent(system.wartesekunden);  
      document.location = t;  
 }  
 function usrlist() {  
      if (!system.sessionid)  
           return alert("keine session vorhanden");  
      var url = system.url+"&sessionid="+system.sessionid+"&cmd=usrlist";       
      apicall(url,   
           function(h) {  
                message(Date2Text()+" User-Liste:");  
                var liste = h.object;  
                for (var a=0;a<liste.length;a++)                                     
                     message(Date2Text()+" " + liste[a]);                 
           });  
 }  
 function senden(msg, elem) {  
      // sende den Text  
      if (!system.sessionid)  
           return alert("keine session vorhanden");       
      elem.select();  
      var url = system.url+"&sessionid="+system.sessionid+"&chat="+encodeURIComponent(system.usr+": "+msg);       
      apicall(url,   
           function(h) {                 
                message(Date2Text()+": " + msg);                 
           },   
           function(h){  
                message("chattext konnte nicht gesendet werden");                 
           });       
 }  
 function setNOHTML(wert) {  
      system.nohtml = wert;  
 }  
 function message(text) {  
      var elem = document.getElementById("inhalt");   
      elem.innerHTML += '<li class="msg">'+text+'</li>';  
      document.getElementById("container").scrollTop = elem.offsetHeight;  
 }  
 function starten() {  
      system.REQUEST = basics_ermittelURLParameter();  
      if (system.REQUEST['reload']) {            
 //          system.nohtml = system.REQUEST['nohtml'];  
           system.usr = system.REQUEST['usr'];  
           system.sessionid = system.REQUEST['sessionid'];  
           system.adresse = system.REQUEST['adresse'];            
           system.wartesekunden = system.REQUEST['wartesekunden'];  
           system.url = "http://"+system.adresse+":8080/?usr="+encodeURIComponent(system.usr);  
           document.getElementById("adr").value = system.adresse;  
           document.getElementById("usr").value = system.usr;  
           //warteAufEventsVomServer(system.wartesekunden);  
      }  
 }  
 window.onload=starten;   
 </script>  
 </head>  
 <body>  
 <form onsubmit="konnektieren(this.elements['adr'].value, this.elements['usr'].value);return false;">  
 chat.server: <input type=text id="adr" name="adr" value=""> <br>  
 username: <input id="usr" name="usr" type=text> <input type=submit value="connect">  
 </form>  
 <div id="chat" style="display:none;">  
      <input type=checkbox id="cnohtml" onchange="setNOHTML(this.checked)" checked> Keine HTML ausführen  
      <form onsubmit="this.elements['btn'].click();return false;">  
      <input type=text id="tt" name="tt"><button name="btn" onclick="senden(document.getElementById('tt').value, document.getElementById('tt'));return false;">senden</button>  
      </form>  
      <div id="container" style="height:300px;overflow-y:scroll;">  
           <div id="inhalt"></div>  
      </div>  
      <button onclick="reload()">reload</button>  
      <button onclick="usrlist()">usrlist</button>  
      <span id="info"></span>  
 </div>  
 <body>  
 </html>  

Javascript für nodejs (Der Server)


 var HTTP = require("http");   
 starten(); // damit kann man testzwecke besser steuern  
 function starten() {  
      webserver();   
 }  
 function webserver(meinserverport) {  
      if (!meinserverport)  
           meinserverport= 8080;  
      console.log("starte server");  
      function vb_clean() {  
           console.log("entferne abgelaufene sessions");  
           var ablauf = time() - (60*1000); // 1 minuten  
           for (var key in vb){  
                if (vb[key].lastconnect < ablauf) {  
                     console.log("entferne", vb[key]['usr'], vb[key].sessionid);  
                     var msg = vb[key].usr+" ist offline";  
                     delete(vb[key]);  
                     schreibeAllen("", msg);  
                }   
           }  
           setTimeout(vb_clean, 30*60*1000);  
      }  
      function schreibeAllen(sessionid, msg) {  
           if (!sessionid)  
                sessionid = "";  
           for (var key in vb) {  
                if (!vb[key].response)  
                     continue;  
                if (key == sessionid)  
                     continue;   
                var html = vb[key].callbackdaten['cbf']+"('"+vb[key].callbackdaten['cbid']+"',"+JSON.stringify("Chat: "+msg)+");";  
                vb[key].response.writeHead(200, {"Content-Type":"text/javascript"});  
                vb[key].response.write(html);  
                vb[key].response.end();  
                vb[key].lastconnect = time();  
           }  
      }       
      vb_clean();  
      var wartezeit = {'client':40*1000, 'server':45*1000};  
      var s = HTTP.createServer(function(request, response) {  
           console.log(request.connection.address());                 
             console.log("Verbindungsaufbau: "+request.connection.remoteAddress, request.connection.remotePort);  
                var url = require('url');  
                var rq = url.parse(request.url, true);  
                //console.log(rq);  
                  console.log(request.method, rq.pathname+rq.search);  
                var paras = rq.query;  
                // der weg über a kann später entfallen  
                // denkbar auch der Weg über die pfad angabe - url.parse(req.url).pathname  
                if (paras['js']) {  
                     // sende das JS  
                     // header('Content-Type: text/event-stream');                       
                     response.writeHead(200, {  
                          "Content-Type":"text/javascript"                           
                          }); // verbindung ok            
                     console.log("Starte Live-JS Eingaben:");  
                     // machen wir über die tastatur, die ruft cbfkt auf für die Übermittlung  
                     nehmeTastatur(function(t){  
                          response.writeHead(200, {"Content-Type":"text/javascript", "Content-Length":t.length});  
                          response.write(t);                           
                          }, function(){  
                               response.end();  
                     });  
                } else if (paras['cbf']) {  
                     // apicall kommt rein  
                     if (paras['sessionid']) {  
                          if (paras['chat']) {  
                               // msg ist eingetroffen  
                               // sender ok senden  
                               var html = paras['cbf']+"('"+paras['cbid']+"','OK;"+wartezeit.client+"');";  
                               response.writeHead(200, {"Content-Type":"text/javascript"});  
                               response.write(html);   
                               response.end();  
                               // verteile an alle erstmal  
                               schreibeAllen(paras['sessionid'], paras['chat']);  
                          } else {   
                               if (paras['cmd']) {  
                                    console.log("usrliste senden");  
                                    response.writeHead(200, {"Content-Type":"text/javascript"});  
                                    var html = "";  
                                    switch (paras['cmd']) {  
                                         case "usrlist":  
                                              var liste = [];  
                                              for (var key in vb) {  
                                                   if (!vb[key].response)  
                                                        continue;  
                                                   //if (key == paras['sessionid'])  
                                                   //     continue;  
                                                   liste.push(vb[key].usr);  
                                              }                                               
                                              html = paras['cbf']+"('"+paras['cbid']+"',"+JSON.stringify(liste)+");";  
                                              break;  
                                    }  
                                    response.write(html);   
                                    response.end();  
                               } else {                                  
                                    // wartet auf events  
                                    if (!vb[paras['sessionid']]) {  
                                         console.log("sessionid nicht bekannt: "+paras['sessionid']);  
                                         var html = paras['cbf']+"('"+paras['cbid']+"','ERROR: session nicht vorhanden');";  
                                         response.writeHead(200, {"Content-Type":"text/javascript"});  
                                         response.write(html);   
                                         response.end();  
                                         return;  
                                    }  
                                    vb[paras['sessionid']].response = response; // der browser wird nach 10sek abbrechen und sich erneut verbinden  
                                    vb[paras['sessionid']].lastconnect = time();  
                                    vb[paras['sessionid']].callbackdaten = {cbf:paras['cbf'],cbid:paras['cbid']};  
                                    setTimeout(function(){  
                                         console.log("schliesse veraltet Connection");  
                                         if (response) {  
                                              response.writeHead(200, {"Content-Type":"text/javascript"});  
                                              response.end();  
                                         }       
                                    }, wartezeit.server); // der client baut die verbindung für 20 Sek auf  
                               }  
                          }   
                     } else {  
                          if (!paras['usr'])  
                               paras['usr'] = "guest_"+request.connection.remoteAddress+'_'+request.connection.remotePort;  
                          // session erstellen und zurück geben  
                          response.writeHead(200, {"Content-Type":"text/javascript"});  
                          var sessionID = require("crypto").createHash("sha224").update("" + new Date().getTime() + Math.random() + '.sasoleindenys').digest("hex");  
                          vb[sessionID] = {  
                               'zeit': time(),  
                               'lastconnect': time(),  
                               'response':null,                                
                               'usr': paras['usr']  
                               };  
                          var html = "";  
                          html += paras['cbf']+"('"+paras['cbid']+"','"+sessionID+";"+wartezeit.client+"');";  
                          response.write(html);   
                          response.end();  
                     }                  
                } else {  
                     // normale HTTP verbindung  
                     // sende das HTML  
                     var html = "";  
                     if (rq.pathname.match(/chatapp/)) {  
                          console.log("sende die chatapp HTML datei");  
                          response.writeHead(200, {"Content-Type":"text/html"}); // verbindung ok  
                          html = require("fs").readFileSync('apicall_chat.html', "binary");  
                     } else if (rq.pathname.match(/basics\.js/)) { // javascript hilfsfunktionen  
                          console.log("sende die chatapp HTML datei");  
                          response.writeHead(200, {"Content-Type":"text/javascript"}); // verbindung ok  
                          html = require("fs").readFileSync('basics.js', "binary");  
                     } else {  
                          response.writeHead(200, {"Content-Type":"text/html"}); // verbindung ok  
                          html = '<html><head><script src="?js=1" type="text/javascript"></script></head><body>Versuch es mal mit /chatapp</body></html>';  
                     }  
                     response.write(html, "binary");            
                     response.end();  
                }  
                //response.writeHead(404, ""); // du bist hier falsch  
                //response.end();  
      }).listen(meinserverport);  
      function time(onlyseconds) {  
           var datum = new Date();  
           var milliseconds = datum.getTime();  
           if (onlyseconds)  
                return intval(milliseconds/1000);  
           return milliseconds;  
      }  
      var vb = {}; // webchat client  
 }  

Fazit

Es geht auch ohne Websockets. Gerade wenn man auf Nummer sicher gehen will und auch den Server entsprechen nutzen kann. Mein Versuch hilft euch hoffentlich für viel bessere Lösungen als hier zusammengestellt.

Viel Spass
Saso Nikolov

Dienstag, 9. Oktober 2012

Fortschritt hält die Evolution auf

Änderungen an uns, in unserer Evolution, war immer durch eine Unzulänglichkeit begleitet.
Unsere Art hat sich immer an etwas angepasst und damit haben wir uns weiter entwickelt.

Doch mit der ganzen Technologie, die uns alles abnimmt, welche uns vor bestimmten Ereignissen und Gegebenheiten schützt, werden wir uns eher zurück entwickeln, als nach vorne.

Warum sollte der Körper sich an etwas anpassen, wenn wir uns die Bedingungen anpassen. Wir helfen unserem Körper immer mehr nach, so dass er nicht die Notwendigkeit verspürt sich selbst in diese Richtung zu entwickeln.

Klar, dass nun nicht jeder seine Medikamente und Vitaminpillen entsorgen sollte, jedoch wäre es durchaus ratsam hier sparsamer zu sein.

Mal schnell umgeschaut: Immer mehr Menschen sind mit Brille ausgestattet. Immer mehr schwächeln vor sich hin. Vielleicht sollten wir uns mal selbst hinterfragen. Die ganzen Hilfsmittel werden nicht dazu beitragen, dass wir uns selbst verbessern.

gruss saso

Samstag, 22. September 2012

Stromsteuer für Solarenergie wird kommen

Heute habe ich ein Auto gesehen dass mit Strom fährt. Das sah richtig gut aus und hat auch gute Leistungswerte. Scheinbar haben bald alle Autohersteller solche Autos im Sortiment.
Mit einem Schlag wurde mir klar, warum in die Regierung in Deutschland gegen Solarenergie kämpft.

Erst werden die Einspeisungsvergütungen halbiert, so dass sich Solarzellen auf dem eigenen Dach kaum noch rechnen, dann wird China aufgefordert die Produktion und den Vertrieb von günstigen Solarzellen in Deutschland zu reduzieren oder gar auf zugeben.

Wenn jeder in wenigen Jahren nur noch Stromautos fährt, dann wird nicht mehr getankt. Benzin hat uns im Griff. Wir sind abhängig. Jeder Liter wird mit Steuern für den Staat, hohe Gewinne für die Mineralölkonzerne angereichert. Wenn der Strom durch die Sonne über unser Dach in das Auto fliesst, wer wird dann das viele Geld, was nun zuviel da ist abzocken wollen?

Ich bin überzeugt, dass die heile Welt, welche wir haben könnten, nicht Realität wird. Wir werden beobachten wie stetig die Strompreise wachsen. Jeder weiss, dass die Produktionskosten nicht in dem Masse steigen, wie man uns versucht zu erzählen. Die gleichen alten Anlagen produzieren den jetzigen Strom. Wenn das alles so teuer ist, dann wäre es damals erst recht nicht tragbar gewesen.

Die Strompreise werden explodieren, damit man den Wegfall der Benzin-Konsumenten kompensieren kann. Interessant wird die Argumentation für diese Ausbeutung. Freue mich schon jetzt auf das zusammen gelogene Betrugsspiel. Denn spätestens wenn 10% der Autos auf Strom sind, wird es Erklärungsbedarf seitens der Regierung geben.

Mein Wunsch? Alle Einrichtungen die uns alle dienen, sollten uns allen gehören. Verstaatlichung der Grundversorgung war auch ein Grund, warum wir nun in Staaten leben. Strom und Wasser gehört uns. Wir sollten nicht dafür von einzelnen Konzernen abgezockt werden, nur weil der eine oder andere Politiker uns für einen Billigflug oder einen neuen Job im Aufsichtsrat verkauft.

Elektrisierende Grüsse
saso

Mittwoch, 29. August 2012

Effizienz durch Kaizen

Kaizen ist die japanische Art, sich ständig zu verbessern.
Bekannt aus dem betriebwirtschaftlichen Bereich, ist es jedoch hier immer noch unterschätzt und missverstanden.

Kaizen in allen Lebensbereichen

Die Veränderung zum Besseren kann sich auf alle täglichen Bereiche beziehen. Damit sind nicht unbedingt nur grosse Optimierungen gemeint, sondern schon sehr kleine Optimierungen von alltäglichen Abläufen und Handgriffen. Im Vordergrund steht das Beobachten von sich selbst und ein stetiges Streben nach Verbesserung. Dabei fokussiert man sich auch mehr auf die Arbeit und die Handlung. Man beginnt Potenzial zu erkennen und das prozess optimierte Denken verbessert sich zunehmend. So können schon sehr kleine Dinge, wie zum Beispiel das hinstellen der Zahnpastatube die tägliche Zeit und die Glücksgefühl verbessert. Sehr viele kleine Verbesserungen bringen den grossen Nutzen. Klar dass eine minimale Optimierung kaum was direkt bewirkt, aber das soll es ja auch nicht. 20 solche Verbesserungen, die nur 1 Sekunde bringen, liefern vielleicht nicht gerade etwas mehr Zeit zum Schlafen. Hier ist aber vielleicht auch das "Nicht-Ärgern" was oft durch Optimierungen kommt auch sehr viel Wert. Bsp: Zahnpastatube liefert sofort immer die Pasta auf die griffbereite Bürste. Schon hat man diese Gefühl, dass alles gut läuft. Dass heute ein richtig guter Tag wird.

Kaizen für das Programmieren

Hier soll anhand eines Entwicklersleben gezeigt werden, wie man schon bei minimalen Sachen etwas optimieren kann. Dadurch auch schneller wird und anfängt vorauschauender zu Denken und zu Handeln.
Gerade bei HTML-Tabellen erkennt man schnell, dass der Aufbau der Zeilen, sich sehr ähnelt. Am Anfang tippt man den Quellcode für die Tabelle. Dann fügt man den Code für die Tabellenzeilen und Felder ein. Füllt diese mit Inhalt. Bei der zweiten Zeile bemerkt man, dass ziemlich viel genau das Gleiche ist. Also die eigene Arbeit auf Optimierungen, bzw. Verbesserungen hin beobachtet. Nun könnte man den Code für die Tabellenzeilen markieren und kopieren. Dann unter den Tabellenzeilen gehen und den Code einfügen. Damit ersparen wir uns die Tipparbeit für das Zeilengerüst. Wir müssen nur noch den falschen Inhalt ersetzen. Also die Inhalte markieren und überschreiben. Das Gleiche machen wir für die nächste Zeile. Dabei fällt uns auf, dass wir den kopierten Quellcode für die Tabellenzeile schneller eingeben können, wenn wir nicht ans Ende der kopierten Stelle fahren, sondern den schon markierten Text einfach mit unserem Inhalt aus der Zwischenablage ersetzen können. Der Inhalt ist exakt das Gleiche. Wir sind jedoch sofort an der richtigen Stelle und müssen den Inhalt aus der Zwischenablage nur nochmal einfügen. Das Markieren und Überschreiben der falschen Inhalte der hinzugefügten Tabellenzeilen kann man auch optimieren. Am Besten ist es doch den Zeilencode gleich mit leerem Inhalt zu kopieren. Also erstellen wir eine HTML-Tabellen-Zeile mit leerem Inhalt. Markieren und Kopieren diese in die Zwischenablage und fügen dann den Inhalt aus der Zwischenablage so oft ein, wie wir Tabellenzeilen brauchen (+ 1, da wir die kopierte Zeile auch benötigen).

Nutzen von Kaizen

Anhand dieses einfachen Prozesses, sind uns während der Arbeit verschiedene Aspekte aufgefallen, welche wir optimieren könnten. Das waren immer kleine Dinge und doch ist in der Summe ein neuer, zeitsparender Arbeitsprozess kreiert worden. Beim nächsten Mal erstellen wir gleich die HTML-Tabelle mit einer leeren Tabellenzeile und vervielfätigen diesen. Um im nächsten Schritt die Daten einzugeben. Wir erhalten also stetige Optimierungen, welche den Arbeitsprozess verbessern und das nur weil wir aufmerksam unser Tun beobachten und nach Einsparungen suchen. Kaizen und nutzen der Erfahrung sind das Selbe.

Erfahrung macht das Kaizen aus

Damit ist klar, dass ohne Erfahrung ein Kaizen nicht möglich ist. Es ist ein iterativer Prozess, der ständig sich wiederholt. Natürlich sind faule Entwickler hier im Vorteil, optimieren diese alles soweit wie möglich, um sich selbst arbeit zu ersparen. Das aber genau macht die Arbeit leichter und bringt uns mehr kreative freie Zeit.

Happy Beobachten.

Dienstag, 21. August 2012

Gewinne ein extra Leben durch effizientes Krafttraining

Training kostet zuviel Zeit

Man verbringt viel Zeit mit Training. Viele sind nicht selten 2-3 mal die Woche in der Mukkibude. Dann verbringt man dort 1-2 Stunden. Dabei trainieren die wenigsten wirklich auch solange. Die meiste Zeit verbringt man mit Ausruhen und dem Quatschen mit anderen Sportbegeisterten. Rechnet man dann noch die An- und Abfahrtzeit dazu, wird das Training schnell zu einer Last, statt zu einem Vergnügen. Gerade am Anfang ist man motiviert. Meist am Sommeranfang ;) Danach schwindet die Lust. Die unschöne Aussicht auf den hohen zeitlich Aufwand gibt immer mehr Grund zum Pausieren.

Was tun?

Ich wollte keiner dieser Quitter sein. Auch nicht eine Karteleiche, doch die Zeit wurde immer wertvoller. Ich musste mich entscheiden. Profibodybuilder wollte ich nie werden, aber doch stark bleiben und auch sonst eher eine breitere Figur darstellen. Aber war das alles die viele Lebenszeit wert, die man dafür opfern musste? Also stand ich vor der Wahl. In anderen privaten Bereichen zurück stecken oder mich gehen lassen. Zu dieser Zeit entdecke ich ein Trainingsbuch, das alles änderte. Mike Mentzer beschrieb in seinem Buch "Heavy Duty" eine ähnliche Situation. Sollte er weiter seinem Beruf nachgehen oder Profibodybuilder werden. Beides war von der Zeit her nicht mehr vereinbar. Er traf einen Mann, der sein Leben änderte. Er erfuhr von einem neuen Ansatz für das Krafttraining. Dieses System verschaffte ihm stetigen Wachstum und enorm viel Freizeit. Er entwickelte auf Basis der Idee einen Trainingskonzept und verfeinerte es in seinem zweiten Buch noch etwas.

Ein neues Trainingsleben beginnt

Ich war begeistert. Seine Ausführungen wirkten sehr logisch. Seine eigenen Erfolge wiesen fantastisches Ergebnis aus. Seine Ehrlichkeit lies mich mehr Vertrauen in seine Worte fassen. Fest entschlossen dieses neue Training zu machen und endlich wieder Zeit auch für andere Dinge im Leben zu haben, starte ich mein eigenes leicht abgewandeltes Set an Übungen. Und es war erstaunlich. Der Erfolg kam sofort. Selbst wenn man nicht dauernd sich in den Gewichten so schnell steigerte, war immer noch der enorme Gewinn an Zeit. Und das obwohl ich nicht schwächer wurde. Aber das Training ist hart. Es ist verdammt hart und Anfänger nicht zu empfehlen.

Das Konzept und die Idee

Das Konzept reduziert die Trainingszeit auf 20-30 Minuten Training und das maximal 2x die Woche. Um diese enorme Intensität zu nutzen, muss man die Übungen beherrschen. Die Idee dahinter kann man detailiert in seinem Buch nach lesen. Nur so viel: Das Gewicht sollte so hoch sein, dass man bis zu 12 Wiederholungen schafft und dabei zum Muskelversagen gelangt. Durch die geschickte Anordnung der Übungen, erspart man sich Wiederholungssätze. Die Wiederholungen sind eh nur dazu da, den Muskel zu ermüden. Denn nur die letzte und Vorletzte Wiederholung bewirkt das Wachstum. Man kann sich wie einen Grosshändler vorstellen. Der Körper ist der Produzent. Die Muskelkraft ist das Lager vom Grosshändler. Wir als Konsument verbrauchen die Muskelkraft, bis das Lager leer ist. Wenn das Lager leer ist, füllt der Grosshändler dies wieder auf. Aber nur soviel wie er verkaufen kann. Also wieder bis zum letzen Maximum. Erst wenn wir das Lager leeren und eine Wiederholung (oder nur eine halbe) mehr verlangen, als im Lager ist, wird der Grosshändler entsprechend viele Muskelkraft vom Körper anfordern, um das Lager erneut zu füllen.
Das ist super vereinfacht und dennoch sollte es für jeden BWLer verständlich sein. Das Training ist derart anstrengend, dass man nicht zuviel trainieren sollte, da sonst ein Übertraining alle Erfolge zu nichte macht.
Oft reicht auch nur 1x Training in der Woche. 

Beispiel für ein Trainingsprogramm

Die einzelnen Übungen sind jeweils immer nur 1 Satz! Sollte man einen Satz vermasselt haben, also zu früh beendet haben, kann man (ab und zu; nicht immer) einen Supersatz machen. Also nach dem Ablegen, das Gewicht reduzieren (10 Kilo oder 2 Steckplätze bei Maschinen) und sofort ohne Pause nochmal ein paar Wiederholungen bis zum Versagen machen. Zwischen den Sätzen sollte die Pause nur so kurz wie nötig sein. Oft reicht der reine Weg zum Gerät. Da das Konzept darauf beruht, dass die Vorermüdung der vorangegangenen Übung die Anzahl der benötigten Sätze so niedrig halten kann. Wenn man mit dem Satz die maximal genannte Anzahl an Wiederholungen erreicht hat, sollte man das Gewicht steigern, denn nur dann wächst es.
  • Bankdrücken (Aufwärmen)  (schräg oder gerade; wie man will) 10-12 Wiederholungen mit 50-60% vom maximalen Trainingsgewicht
    (Zum Aufwärmen und dem Muskel sagen: Achtung wir legen gleich los, sich also in Trainingsstimmung bringen)
  • Bankdrücken 6-12 Wiedholungen.
    Die Wiederholungen dienen dem weiteren Aufwärmen und Erschöpfen der Muskel. Erst wenn es nicht mehr geht, fängt das Training an. Die letzte (auch halbe) Wiederholung bewirkt den Anreiz zum Wachstum. Also alles geben und drücken!
  • Trizeps (Drücken oder Seilziehen) 10-12 Wiederholungen (Man ist schon warm, also gleich Max-Gewicht nehmen)
  • Seitheben (am Besten Maschine, da man da gut kontrollieren kann) 10-12 Wiederholungen
  • Seitheben nach hinten 10-12 Wiederholungen
  • Klimmzüge oder Rückenziehen von oben (Aufwärmen) 10-12 Wiederholungen mit 50-60% vom maximalen Trainingsgewicht
  • Klimmzüge oder Rückenziehen von oben 6-12 Wiederholungen
  • Rudern oder vorgebeugtes Seilziehen 6-12 Wiederholungen
  • Bizeps frei oder Maschine 6-12 Wiederholungen
  • Beinpresse (Aufwärmen) 10-12 Wiederholungen mit 50-60%  vom maximalen Trainingsgewicht
  • Beinpresse 6-12 Wiederholungen
Fertig. Wenn man das mal ansieht, versteht man die kurze Trainingszeit. Ich persönlich lasse oft Beine weg, da meine eh schon zu dick sind. Hängt von der persönlichen Zielsetzung ab.

Happy Training

Samstag, 18. August 2012

Mit Open-Source Geld verdienen

Kein Verdienst für Open-Source Projekte

OpenSource Projekte erzeugen Produkte an denen sie kaum oder nichts verdienen. In der Regel erwarten alle die Ergebnisse von diesen Projekten als kostenlosen Download. Einige Projekte, bzw deren Mitglieder verdienen teilweise über Support etwas Geld. 

Das Geld geht an die Zulieferer und Dienstleister

Wenn ein Projekt erfolgreich wird. Gibt es immer mehr Anbieter von Addon, Themes und Dienstleistungen rund um das Produkt. Interessierte laden sich die Software kostenlos herunter und erwerben zusätzliche Teile von anderen oder lassen sich die Software von anderen installieren. Bei diesen Umsätzen wird das OpenSource Projekt garnicht oder nur mit sehr wenig Beteiligung unterstützt.

Idee des Revenue Share

Da die User die Projekt Software von der OpenSource Webseite herunter laden, könnte man hier auch alle Anbieter von Addons und Dienstleistungen listen. Jeder über diese Webseite erzeugte Kauf, könnte durch den Anbieter vergütet werden. Das Problem ist nur die Erfassung der tatsächlichen Verkäufe. Darüber hinaus kann es sehr aufwendig werden, so einen Markplatz mit den Produkten und Dienstleistungen auf der OpenSource Webseite zu betreiben. Ständig kommen und gehen Produkte.

Am besten alles automatisieren

Gerade die Abrechnung sollte auch einfach und fair abgewickelt werden. Hier wird eine Plattform benötigt, in der die verschiedenen Anbieter sich selbst eintragen können und damit den Marktplatz automatisch füllen. Die Abverkäufe erfolgen über den Marktplatz und die Einnahmen werden an den Anbieter weitergeleitet. Dadurch werden die Verkäufe erfasst. Die Gewinnbeteiligung kann dabei gleich einbehalten werden und somit keine Probleme bei der Abrechnung bewirken.

Am Besten eine All-in-One Lösung

Die Marktplatzlösung sollte alles können. Die Anbieter registrieren sich. Hinterlegen die Produkte/Dienstleistungen und bestimmen den Verkaufspreis. Der Marktplatz wird in die bestehende Webseite integriert und die User können dort nach Benötigtem stöbern. Wenn ein Verkauf statt findet, erhält der Kunde anschließend auch den Download. Der Anbieter wird über den Verkauf benachrichtigt.

Möglichkeit

Directpaylink.com betreibt eine Verkaufsplattform. Dort können User eigene Dateien hinterlegen und diese zum Kauf auf der eigenen Webseite anbieten. Directpaylink plant nun die Plattform selbst zu öffnen. So könnten OpenSource Projekte das eigene Projekt mit dieser Plattform auusstatten. Die OpenSource Webseite bindet den Marktplatz ein. Interessierte Anbieter registrieren sich auf dem Marktplatz und platzieren die eigenen Produkte. Bei einem Verkauf wird die erhobenen Transaktionsgebühr vom Anbieter getragen. Diese Transaktionsgebühr erhält das OpenSource Projekt anteilig ausgezahlt.

Fazit

Umsatzbeteiligung für das OpenSource Projekt. Eine kostenfreie Bezahlplattform einbinden, welche auch einen Mitmach-Shop hat. So dass Hersteller rund um das OpenSource-Projekt Produkt, sich selbst eintragen können und der Verkauf über das OpenSource Projekt abgewickelt wird. Für das Betreiben der Plattform, über Directpaylink, selbst entstehen keine Kosten. Somit ist diese Idee risikolos und kann jederzeit wieder beendet werden.

Denkbar wären 3 Modelle:

  • Directpaylink betreibt die komplette Plattform.
    Also Server, Upload-Server und Support.
    Dabei werden die eingenommen Transaktionskosten zu einem Teil an das Open-Source Projekt ausgezahlt. Diese Variante ist könnte das Rund-um-sorglos Paket sein.
  • Directpaylink betreibt nur die Basis
    Also Server und Support,
    Dabei werden die Upload-Server (Storage-Server) von der Community betrieben.
    Auch hier werden die Einnahmen zu entsprechenden Teilen verfgeteilt. Diese Variante könnte das Basis-Paket sein.
  • Directpaylink betreibt nur die Software Basis
    Also nur Server.
    Upload-Server, Bilder-Server und statische Server für die Lastverteilung, sowie der Support wird von der Community übernomme. Diese Variante könnte man Enterprise Paket nennen.
    Hier fliessen die Einnahmen der Transaktionskosten direkt an das Open-Source Projekt als Betreiber der Plattform. Directpaylink erhält pro Verkauf eine geringen Anteil. Damit sollen Wartung und Weiterentwicklung abgegolten werden.
Nur wenn etwas tatsächlich verkauft wird, fallen auch Kosten an. Die laufenden Fixkosten trägt bei jedem Modell der Betreiber der entsprechenden Server selbst.

Dieses Modell würde es ermöglichen, Open-Source Projekte und Free(mium) Projekte lukrativ zu machen, ohne sich dem ursprünglichen, initialen Gedanken hinter dem Projekt zu entfremden.


Business Model Canvas für Umsatzbeteiligung für OpenSource Projekte
Business Model Canvas
Hier die Webseite von Directpaylink: http://directpaylink.com/

Hier ist mal ein Entwurf wie eine Lösung aussehen könnte:

Hier klicken, um die Presentation einzusehen (Google-Docs)

Anregungen willkommen!

Dienstag, 14. August 2012

Schlechte Mitarbeiter kommen durch schlechtes Management.

Stoppt die falsche Motivation. Der Arbeitnehmer spiegelt nur die Unternehmenskultur wider. Ist der Service schlecht? Ist der Mitarbeiter überheblich und kundenschädlich? Dann stimmt das Leitbild der Firma nicht mit den tatsächlich gelebten Gepflogenheiten überein. Hier muss dringend was getan werden, denn man vergeudet Potenzial. Mehr noch, manche gefährden sich und die eigene Firma.
Wie kommt das?

Der neue Mitarbeiter betritt die ihm in der Regel unbekannte Firma oder Abteilung und wird sich automatisch an die Gruppe, bzw das vorherrschende Rudel orientieren. Mehr oder weniger wird er sich bewusst oder unbewußt anpassen. Er übernimmt die Art und Regeln der anderen. Sein Handeln wird durch die Vorbilder und dem gelebten Verhaltenstandard bestimmt. Gerade zu Beginn ist jede negative Handlung oder gar Äusserung prägend. So werden Sätze wie: "Ach der schon wieder. Der kann warten.", wie eine Handlungsrichtlinie verstanden. Die Prioritäten bei Abarbeitung der Aufgaben geben sogar eine Bewertung des Auftraggebers (intern, sowie auch extern) ab. Dies wird der neue unweigerlich verinnerlichen. Der Mensch ist einfach so.

Jetzt sofort allen auf die Finger klopfen und versuchen das Ruder umzureissen?
Nein. Ein Firmenleitbild muss her. Dieses sollte ehrlich im Soll-Ist-Vergleich analysiert werden. Danach sollte man mindestens 2 Wege gehen. Denn die vorhandenen Mitarbeiter können nicht adhoc alles umstellen. Man muss sie dahin führen.
1. Negative Auswirkungen aktueller Gepflogenheiten veranschaulichen
2. Mitarbeiter die besseren, selbst ausgearbeiten Richtlinien vorleben

Ein Firmen Handbuch eventuell sogar 2 sind sehr hilfreich, auch für Neue. Ein Mitarbeiterhandbuch für allgemeines in der Firma und eines für die Fachabteilung. Diese sollten lebendig sein, also auch durch Optimierungen der Mitarbeiter stetig angepasst werden.

Auf ein besseres Business und angenehmeres Zusammenarbeiten.

Montag, 30. Juli 2012

Effizientes Kaffee bestellen, schwer gemacht

Immer auf der Suche, nach sinnlosen Optimierungen wollte ich nun auch mal das Bestellen meines Kaffees optimieren. Man wartet in der Regel sowieso schon sehr lange bei Starbucks, da jeder Kaffee ja frisch aufgebrüht wird. Jeder? Nein, eine einzige Kaffeeform nicht. Diese ist der Filterkaffee. Günstig und kann direkt an der Kasse erhalten werden. Also schon ansich sehr effizient serviert. Dennoch wagte ich den Selbstversuch.
"Einen kleinen Filterkaffee, schwarz. Zum Mitnehmen, bitte".
Zugegeben. Dieser plumpe Versuch, alles was in der Regel abgefragt ist in einem Satz zu formulieren, stösst oft an Grenzen, wo Menschen gewohnt sind viele Kunden zu bedienen. Die haben einen Fragetenor drauf, den man nur schwer stoppen kann. Klar, nach einer Weile hört der Verkäufer nicht mehr richtig hin. Sein Gehirn filter nur noch nach Schlüsselwörter: Kaffee, Latte, Muffin. So werden zusätzliche Adjektive oft überhört, da diese an der Stelle noch nicht erwartet werden.
Also die typische Frage auf meine obige Bestellung lautet:
"Mit Platz für Milch?"
Das resultiert auch daraus, dass man bei Starbucks die Milch selbst hinzufügt. Also ist mein "schwarz" nicht richtig angekommen.

Deutschland war schon immer ein Kaffeetrinker Land, aber nun sind wir endgültig im New-Coffee Land angekommen. Auf meinen Wunsch einen normalen Kaffee zu erhalten, gibts immer die Frage mit Zucker und Milch? Das ist allerdings vielleicht auch nur für mich nervig. Ich trinke meinen Kaffee gerne pur. Ja, schwarz. Ohne Milch und Zucker. Denkwürdig wird es erst, auf meinen Wasserwunsch mit Wasser bedient zu werden, wenn man hinzufügt: "Ohne Zusatzstoffe und Zucker". Ja, einfach nur Wasser. Normales. :)

Prost

Montag, 23. Juli 2012

Wie schaffe ich die theoretische Führerscheinprüfung spielend leicht?

Effizientes Lernen für die theoretische Führerscheinprüfung


Ich wollte unbedingt noch Während meiner Abiturzeit den Führerschein machen. Meine Zeit war jedoch schon knapp bemessen. Ich verbrachte viel Zeit mit dem Lernen für Klausuren und
mit dem Erstellen von Hausaufgaben. Das zusätzliche Lernen für die Theorie verbrauchte immer mehr meiner knappen Zeit.

Langsam stellte sich mir die Frage,
ob ich nicht lieber den Führerschein nach dem Abitur machen sollte.
In der Regel wird es aber nie den richtigen Zeitpunkt geben.
Darum musste ich entweder das Lernen für die Schule reduzieren oder
das Lernen für den Führerschein optimieren.

Nach einer Analyse des Lehrmaterials befand ich den Stoff schon als sehr optimal.
Jedoch machte ich einen sehr grossen Vorteil aus. Nämlich die Prüfung ansich.
Zum Testen der eigenen Lernergebnisse befinden sich auch die Testbögen im Lehrmaterial.
Für die Prüfung werden exakt die gleichen Fragen und Antworten der Testbögen verwendet.
Damit gestaltet sich die Vorbereitung einfacher, da man den abzufragenden Stoff genau eingrenzen kann. Jeder Bogen besteht aus mehreren Seiten mit Fragen. Jede Frage besteht aus mehreren Antwortvorschlägen und oft auch aus einem Bild. Für einige Fragen können gleich mehrere Antworten stimmen.

Durch den Aufbau der Fragen ist es einfacher, sich die Übungen zu merken. Man prägt sich Frage, Aufbau der Antworten und das Bild gemeinsam als Ganzes ein. Durch diese zusätzlichen Elemente in einer Frage,
kann sich das Gehirn diese leichter merken. Das ist auch ein Grund, warum selbst Analphabeten die Testbögen schaffen können, da Sie hier ein Bild, um ein paar Zeichnungen (Kreuze) ergänzen.

Meine hier vorgestellte Technik nutzt diesen Vorteil, um spielend und effektiv die Testfragen zu lernen.
Gleichzeitig muss man sich nicht mehr mit, der eigenen Meinung nach, logischen Fehlern auseinander setzen.
Denn oft wirken die Antworten zu bestimmten Fragen logisch falsch. Beim Theorie Unterricht haben wir auch ab und an Tests gemacht. Dabei ist mir aufgefallen, dass die meisten versuchen auf Grund des Lehrmaterials die Antworten herzuleiten. Ich wurde Zeuge wie sich sogar 2 Personen um die richtige Logik der Antworten gestritten haben.

Damit wurde mir immer klarer, dass hier das Auswendig erlernen aller Fragen und Antworten die einzige richtige Herangehensweise sein kann. Ich beschloss die Antworten zu wissen und nicht herzuleiten. Da mir für die richtige Logik wohl zu viel Erfahrung im Strassenverkehr fehlte. Und tatsächlich wurden einige damals verwirrende Antworten in der Realität logisch. Manches muss man wohl wirklich erleben, um den Sinn zu verstehen. Einige Zeit im aktiven Strassenverkehr ergeben die damals unlogisch erscheinenden Antworten
plötzlich einen Sinn und die tatsächliche Logik erschliesst sich einem. Also bitte hier nicht mit der falschen Grundeinstellung rangehen. Sondern man tut sehr gut daran sich nur auf das Lernen zu konzentrieren.

Die Lernmethode, welche ich gleich vorstelle, ermöglicht es einem, auch ohne Durcharbeiten des kompletten Lehrmaterials, die Theorie mit null Fehler zu bestehen.

Am Anfang ist die Methode etwas zeitaufwendig. Doch steigert sich das Lerntempo und schon nach kürzester Zeit, werden immer weniger Fehler gemacht, so dass man immer weniger Zeit zum Lernen benötigt.

Eine ideale Lerndauer ist 7 Tage. Wenn man 14 Tage vor der Prüfung beginnen möchte, ist der Lerneffekt noch viel grösser und die Antworten brennen sich förmlich in das Gehirn. Vielleicht für immer. Wer ein fotografisches Gedächtnis hat ist klar im Vorteil. Jedoch kann es jeder mit dieser Methode schaffen. Je leichter man sich etwas merken kann, desto weniger Zeit, in Lerntagen gerechnet, wird benötigt.

Hier aber eine Warnung nicht zu spät anzufangen. Auch zu früh mit dem Lernen anzufangen ist bei dieser Methode nicht optimal, da die Gefahr besteht zu viele Tage vor der Prüfung mit dem Lernen aufzuhören. Ideal ist es, wenn die 7-14 Lerntage so enden, dass man am nächsten Tag die Prüfung schreibt.

Wir verwenden hier das Lernen mit dem Überlernen. Also das ständige Wiederholen der Prüfungsfragen.
Übung macht den Meister. So gilt auch hier, dass durch das ständige Beantworten der Fragen der Lernerfolg eintritt. Ähnlich wie bei alten Monitoren brennen sich somit die richtigen Antworten in unsere Erinnerung.

Wie funktioniert die Methode?

Als kleine Vorbereitung benötigen wir:

  • Die Prüfungsbögen
  • Das Testergebnislineal zum Anzeigen der richtigen Antworten
  • Eine durchsichtige Folie
  • Einen wasserlöslichen Filzschreiber oder Marker
  • Tuch oder Papiertaschentuch
Die Testbögen und das Lineal zum Anzeigen der Antworten befinden sich im Lehrmaterial, welches man von der Fahrschule erworben hat. Bei der Folie ist eine OVerheadfolie sehr gut, es funktionieren aber alle durchsichtigen und auch halbdurchsichtigen Folien, also auch Hefterfolien, in denen man Blätter einstecken kann. Zur Not kann man auch einen Schnellhefter zerpflücken und die Vorderseite nehmen. Statt auf dem eigentlichen Testbogen zu schreiben, werden wir mit dem Filzschreiber auf die Folie die Antworten markieren.

Schritt für Schritt Anleitung

  • Alle Bögen als Stapel vor sich legen
  • Den obersten Bogen nehmen
  • Folie auf die Seite legen
  • Alle vermeintlich richtigen Antworten für alle Fragen auf der Seite mit dem Filzstift markieren
  • Wenn alle Fragen auf der Seite beantwortet sind mit dem Antwortlineal nachprüfen, ob alles richtig beantwortet wurde
  • Dann die Folie abwischen
  • Nach dem gleichen Verfahren alle Seiten des Bogens durcharbeiten
  • Wenn mindestens eine Antwort falsch auf dem Bogen war, wird dieser Bogen unter die anderen noch zu bearbeitenden Bögen zurückgelegt. Dieser wird an diesem Lerntag nochmal gemacht
  • Wenn alles Antworten richtig waren, wird dieser Bogen zur Seite gelegt. Dieser Bogen wird erst wieder am nächsten Lerntag gemacht.
Gerade am Anfang macht man mehr Fehler, so dass man oft 1 oder gar mehrere Durchläufe benötigt,
bis man alle Bögen fehlerfrei abschliessen kann. Aber mit jedem Tag reduzieren sich die Fehler und damit auch die zu wiederholenden Bögen. Bis man nur noch einen einzigen Durchgang pro Tag benötigt. Ganz nebenbei wird man auch bei der Beantwortung der Fragen immer schneller. Teilweise muss man die Antworten und Fragen nicht mehr vollständig lesen und weiss schon wohin man die Kreuze setzen muss.
Mit jedem erfolgreicheren Tag macht es immer mehr Spass.

Als Belohnung benötigt man immer weniger Zeit. Man kann einen Wettkampfrausch erleben. Sollte das Lernen mit anderen zusammen erfolgen, kann gegeneinander um die benötigte Zeit spielen. Dies geht auch sehr gut alleine. Man spielt, dann gegen seine eigene gestrige Zeit.

Wenn eine Lernpause von 1-2 Tage eingebaut werden muss, dann sollte diese Zeit eher am Anfang der Lernperiode erfolgen, statt kurz vor der Prüfung. Selbst wenn man sich sehr sicher fühlt, ist es für den vollständigen Erfolg wichtig, alle Bögen jeden Tag einmal durch zu arbeiten. Wenn man nach 2 Tagen merkt, dass keine Fehler mehr zu machen, dann kann das Training unterbrochen werden. Man sollte dann 4-5 Tage vor dem Prüfungstermin das Training wieder aufnehmen. Wenn man sich nicht sicher ist, ob und wie fit man ist, kann man 14 Tage vor dem Prüfungstermin beginnen und erkennt so schnell ob das Lernen unterbrochen werden kann.

Das Lernen wird durch die stetigen Wiederholungen, vom blossen Auswendiglernen, in den richtigen Lernzustand gehoben. Nach dem die Fragen am Anfang noch mit Überlegen und innehalten beantwortet werden, beantwortet man mit der Zeit die Fragen intuitiv. Das Gehirn befasst sich mehr und mehr mit den Fragen und wenn die Antworten erst einmal sitzen, kann man auch über den Zusammenhang und Sinn, bzw. Einsatz der Fragen und Antworten nachdenken. Dies festigt das Erlernte nochmal und ermöglicht noch mehr ein Abweichen der erlernten Antworten spielend zu korrigieren. Man wird überrascht sein, wie gut das Erlernte abrufbar ist.

Mit mindestens 5 Tage Lernzeit kann man auch dann die Fragen beantworten, wenn die Antworten geändert wurden oder in die Reihenfolge der Antworten auf den Bögen abweicht. Das ist die Rückversicherung, sollte es genau am Prüfungstag zu einer Abweichung kommen. Man wird die Fragen können! Dann können auch abgeänderte Bögen einen nicht mehr aufhalten.

Dienstag, 10. Juli 2012

Microwave mir mal einen Kuchen - Effiziente Neuerungen stehen oft vielen Skeptikern entgegen


Viele neue Verbesserungen und Errungenschaften werden abgelehnt,
weil "sie ja gar nicht gut sein können" und "wie soll das funktionieren".

Es gibt immer wieder neue Ideen und Erfindungen,
welche gerade am Anfang schwer von der Allgemeinheit angenommen werden.
Oft aus mangelnden Verständnis oder weil man nicht bereit ist, dies unvoreingenommen zu betrachten und anschliessend seinen Nutzen zu bewerten.

Natürlich kann es auch durch das Marketing schlecht oder in das falsche Licht gerückt worden sein, weil die Verantwortlichen selbst nicht das grosse Potenzial erfasst haben und damit auch missverständlich die Botschaft kommuniziert wurde.

Welche Rat kann man hier geben, welche Leere soll hier gezogen werden?
Ich möchte zuerst mit ein paar Beispielen antworten und
da ich gerade Hunger habe kommen diese aus dem Bereich Küche.
Hier wurden fundamentale Errungenschaften gewonnen, welche effizienter und schneller zum gewünschten Ergebnis führten und dennoch sind diese auch heute noch teils Missverstanden und als "Teufelszeug" deklariert.  Man muss einen langen Atem habe, um solche Produkte aufblühen zu sehen. Auch wenn man dadurch noch soviel besser fährt, werden diese nicht immer von allen so verstanden. Hier kommen Gedanken, wie das hat meine Oma anders gemacht und warum sollte die neue Vorgehensweise nun besser sein? Altes wird oft mit Tradition und besserem Verfahren verglichen. Aber wenn man Omi fragt, sind nur die Umstände, weil man die neuen Techniken nicht kannte, für das "alte System" verantwortlich. Dazu sollte man bedenken, dass die Menschheit nicht plötzlich seit dem 19 Jahrhundert die super kluge Rasse wurde, sondern, dass wir nach wievor lernen und uns verbessern (sollten).
Nehmen wir den Induktionsherd. Einige wissen heute noch nicht was das ist. Dabei wurden die erste schon Anfang 1900 gebaut. Das Kochen damit geht viel schneller und man verbraucht auch weniger Energie in der Gesamtsumme. Klar einige Einschränkungen sind zu beachten.
Mein Lieblingsgerät, die Mikrowelle kennt nun fast jeder.
Doch wieviel verziehen die Mine, wenn man behauptet damit zu kochen? Durch die Werbung und die Unkenntnisse am Anfang denken viele, dass ist nur ein Auftau, bzw Heissmach-Gerät. Jedoch kann man die Vorzüge der Zubereitung in der Mikrowelle selbst schnell testen. Dank youtube findet man auch unendliche viele Rezepte für schnelle Zubereitung und schmackhaftes Essen. Kein Fertiggericht sondern, richtig gemachtes Essen. Dabei ist das Gerät nicht nur effizient im Verbrauch, Reinigung und Zeit. Sondern kann manche Speisen auch schmackhafter sein lassen, da diese nicht im Wasser gekocht werden müssen.
Von Pellkartoffel über Kartoffelchips bis zum 2-Minuten Kuchen ist alles möglich. Ja auch ein Spiegelei. Nur das hart gekochte Ei, ist etwas unschön. Explosionsgefahr und damit eine Sauerei in der Mikro möglich.

Mein Rat ist einfach mal nach der Effizienz einer Sache schauen, bevor man diese sofort verwirft. Gebt Neuem eine Chance und lasst eure Vorurteile, welche nicht durch eigene Erfahrung, sondern durch ungeprüfte Aussagen anderer entstanden sind zurück. Ihr könntet sonst das eine oder andere Wunderwerk verpassen und dem Zug der Zeit hinterher rennen, wenn es schon zu spät ist.

Hier noch ein kleines Rezept für Cupcake. Für den schnelle Kuchen-Hunger.
Man benötigt:
  • mikrowellenfähiges Geschirr (Tasse oder Mikrowellen-Schüssel)
  • 1 Ei
  • 3-4 Esslöffel Mehl
  • 3-4 Esslöffel Zucker
  • 3-4 Esslöffel Öl
  • 3-4 Esslöffel Kakao (geht auch mit in Mikro geschmolzener Blockschoki; 500-540Watt 2 Minute, am besten dann mit dem Öl zusammen schmelzen lassen)
  • 3-4 Esslöffel Wasser oder Milch (geht auch weniger, ist eher geschmackssache)
Die Zutaten kann man in der Menge auch anpassen. Zucker reduzieren etc. 
Das ganze zu einem Kuchenteig (oder Pampe gennant) ähnlichem Gebilde verrühren und ab in die Mikrowelle. Je nach Leistung 2-3 Minuten. Man kann mit 2 Minuten anfangen und den Zahnstochertest machen, um zu sehen ob der Kuchen fertig ist. Also reinstechen und wenn am Stocher nichts mehr klebt ist es fertig. 
Ich nutze eine Mikrowellenschüssel und der Kuchen löst schich komplett von selbst aus der Schüssel, wenn der Kuchen fertig ist. Eis oder Sahne drauf und fertig. Ist aber auch so zum Kaffee sehr lecker.

Dieses Rezept und viele andere (Bananenchips, etc) habe ich auf youtube gefunden. Am besten man beginnt mit "Mikrowelle" oder "microwave". Die USAner sind da schon viel weiter als wir hier.

PS: Das Spiegelei einfach in die Schüssel oder auf den Teller geben (Kein Öl nötig). 1.0-1.5 Minuten. Fertig. Aufs Brot schieben und ...

Viel Spass beim Ausprobieren.

Saso Nikolov

Donnerstag, 8. März 2012

DNA - der Mensch ist ein Computer mit eigenem Programm

Wenn man sich die DNA Geschichte ansieht und die Art und Weise wie Gene miteinander in Beziehungen stehen kommt man unweigerlich auf die Idee,
dass es eigentlich ein Programm ist, welches sich selbst anhand eines serialisiertem Code entwickelt.

Ohne sehr tief in die DNA (Gene und deren Arbeit) und den Strukturen eintauchen zu wollen, wirkt es doch interessant, dass Verbindungen immer passend ablaufen müssen. Ähnlich einem Funktionsaufruf, bei dem die Parameter entsprechend dem deklariertem Typ passen müssen.
Wenn man dann die DNA nimmt, welche sich scheinbar nur selbst produziert, könnte man dies mit einem Programm vergleichen, dass sich selbst immer wieder kopiert. Klar die meisten denken dann gleich an Viren, jedoch könnte man hier auch an Threads denken. Die 3,2 Milliarden Codebuchstaben der menschlichen DNA könnten sehr gut eine serialisierte Form von Daten darstellen. Ein Programm, nicht gespeichert auf Metall aber in biologischen Elementen. Damit konstruiert sich das Programm einen eigenen Computer mit mehr Leistungsfähigkeit, um mehr DNA zu produzieren und am Leben zu erhalten.

Wenn wir es schaffen können, dass Byte-Code in biologischen Elementen gespeichert werden, würden wir auch in diese Richtung gehen, dass das System autark von externen direkten Energiequellen wäre. Noch kann keiner den Code-Inhalt in der DNA ausreichend erklären. Was so ist als würde man irgendwo in einem Byte-Stream eines Programmes hineinblicken und damit dann auf den Quellcode schliessen wollen. Interessant sind auch die Genmanipulationen, welche teilweise keine oder sehr verblüffende Resultate hervor bringen. Wenn man das vom Programmieren aus anssieht, ist es weniger verblüffend. Hier sei die Geschichte über die Genmanipulation bei Fliegen angedeutet. Forscher verbanden ein Gen eines grösseren Tieres mit einer Fliege und erwarteten ein Riesenauge oder etwas anderes Abartiges. Doch es passierte nur eine Veränderung der Augenfarbe.
Angenommen, der serielle Code wird als Programm abgelaufen und man übergibt einen unsinnigen Parameterwert ein (Genmanipulation an einem bekannten Gen). Dieser wird dann durch die Funktion aufgerufen, da der Parameter ja vom Typ her passt. Im Programm könnte eine Exception ausgelöst und entsprechend ein Default-Wert initialisiert worden sein.

Auf diese Weise könnten wir Basisprogramme sein, welche sich anhand des Inputs entsprechend aufbauen. So könnte das DNA das Basisprogramm und die entsprechenden Daten enthalten. Das Basisprogramm könnte mit den Daten in der DNA, dynamisch entsprechen Funktionen erstellen. Ähnlich wie eine Funktion in Javascript selbst überschreiben darf und neue Funktionen hinzufügen kann. Wenn ein neues Baby geschaffen wird vermischen sich die Gene im Datenbereich und erzeugen einen neuen Menschen.

Wer weiss, vielleicht haben uns irgendwann Ausserirdische als Nanoroboter hier rausgelassen und sehen sich an, was alles aus dem Nano-Roboter werden kann. Wohl gemerkt arbeiten so auch alle Tiere und Pflanzen. Sie haben eventuell ein modifiziertes Basisprogramm, muss aber nicht sein. Die Daten sind anders.

Das sind meine Gedanken, nach der Lektüre von Bill Bryson "Eine kurze Geschichte von fast allem"

Saso Nikolov

Sonntag, 4. März 2012

Hilfreiche Funktionen und Lösungen für Probleme in Javascript als Buch

Ich habe mein eigenes Framework und Anwendungsbeispiele in ein eBook gepackt.
Dabei liegt der Schwerpunkt im problemorientierten Vorgehen. Der Leser erhält Javascript Funktionen für den alltäglichen Bedarf und auch komplexere Lösungsvorschläge für aufwendigere Probleme.
So sind die Lösungen mit mehreren Überschriften belegt. Dabei sind die Überschriften mögliche Problemstellungen. So kann man sich leicht die passende Lösungsidee anhand der gegeben Problemstellung ermitteln.

Das Werk dient als Anreiz für eigene weiterentwickelte Versionen der Lösungen. Ich verstehe es mehr als Ideengeber als ein allumfassendes Nachschlagewerk. Obwohl sehr nützliche und auch komplexe Lösungen angegegangen werden. So sehe ich die Lösungsvorschläge als Rezepte an und darum auch der Titel "Javascript Kochbuch - Effiziente Lösungsvorschläge für alltägliche Probleme".

Neben einem umfangreichen Angebot an Funktionen für den Alltag, bietet dieses Werk auch neue Wege, um Domain-Crossing zu umgehen und dennoch performant und einfach ein Cluster über verschiedene Server aufzubauen. Dabei kann auf AJAX verzichtet werden und gleichzeitig ungeheuer einfach das Cluster erweitert werden.

Das Buch kann man unter anderem bei Amazon erwerben:
http://www.amazon.com/dp/B007GELC46

Viel Spass und weiterhin Erfolg im Zubereiten von köstlichen Javascript-Lösungen.

Saso Nikolov