Martin Lantzsch
Software Entwickler
27. Mai 2013

PlayFramework 2.1 use Global in the complete application

27. Mai 2013 - Geschrieben von Martin - Keine Kommentare

While building the new Resigame game server I noticed, that I need a persistent Class for the complete lifetime of my application for storing tasks and other useful things like cached objects (e.g. socket connection to internal communication backend).

So I used the „Global“ class for this purpose. The only steps I had to do was adding the Global.java to a package (otherwise you can’t import it).
Global.java
Afterwards I added a reference to the application.conf:

application.global=core.Global

Now the Global object is accessible in all parts of my application. For example I added a static property called tasks for an object of my TaskScheduler, which is instantiated on application startup.

public class Global extends GlobalSettings {
    public static TaskScheduler tasks;
    public void onStart(Application app) {
        // start task scheduler
        Global.tasks = new TaskScheduler();
        Global.tasks.add(new UserSessionEndExpired());
        Global.tasks.start();
    }
}

this TaskScheduler is now controllable by my REST API:

public static Result stop(Integer taskId) {
    JsonResult result = new JsonResult();
    if(Global.tasks.stop(taskId))
        result.status = 200;
    else
        result.status = 400;
    return result.send();
}

29. März 2013

Django JSON POST decorator

29. März 2013 - Geschrieben von Martin - Keine Kommentare

Für den Django API Server bei Resigame habe ich einen neuen Decorator geschrieben, welcher Daten im JSON Format, die per POST übermittelt werden automatisch in eine Liste konvertiert und nach „request.POST“ schreibt. Brauchen wir, da die Daten via Backbone.js im JSON Format an die REST API gesendet werden.

resigame-api/decorators.py

from StringIO import StringIO
import json
 
 
def post_json(function):
    def wrap(request, *args, **kwargs):
        if request.raw_post_data is not '':
            try:
                request.POST = json.load(StringIO(request.raw_post_data))
            except:
                print 'invalid json in request.raw_post_data'
        return function(request, *args, **kwargs)
 
    return wrap

Einfach den Decorator @post_json anhängen, wenn z.B. folgende Daten gesendet werden:

{"x":100,"y":100,"name":"Resiiiiiiii"}

Anschließend befindet sich eine Liste mit den Daten in request.POST. Als wenn die Parameter und Werte normal übergeben worden wären.

8. Januar 2013

Umzug der resi:DATA Infrastruktur

8. Januar 2013 - Geschrieben von Martin - 4 Kommentare

Da seit dem letzten Post über die Infrastruktur einiges an Webspaces und Servern dazu gekommen ist, muss nun alles mal neu formiert werden.

Aktuell arbeite ich mit 2 Webspaces für Webseiten und Mail Hosting, einen Webspace für Backups, zwei Linux vServer für Webapplikationen, Webservices und Git Repositories und einen Windows vServer für ein C# Projekt, dass eine Serverapplikation und einen Team Foundation Server erfordert.

Das ganze soll im nächsten halben Jahr auf zwei vServer und einen Webspace zusammengezogen werden. Hierzu werden zwei Webspaces gekündigt und alle Domains auf einen großen Webspace Umgezogen, der auch gleichzeitig die Mail Struktur beherbergen soll (dann muss ich keine Mail Server mehr administrieren, was sehr entspannend ist :)). Die Linux vServer werden aus dem Momentanen Dienste Mix gelöst und einer wird für Lighttpd + PHP und der andere für MySQL, NodeJS und Git zuständig sein. Der Windows Server verschwindet ganz, da die Applikation mitsamt des Team Foundation Servers in ein Cloud Hosting migriert wird.

Im nächsten Schritt, der nicht so Zeitkritisch ist, werden alle Webseiten und Applikationen die derzeitig noch auf PHP Basis laufen auf NodeJS umgezogen. Ist dieser Schritt abgeschlossen wird vServer Nr 1. als dedizierter MySQL Server und vServer 2. als dedizierter NodeJS Server eingesetzt.

Soweit das vorhaben. Nun muss erst einmal eine Bestandsaufnahme bezüglich Domains, Apps, etc. gemacht werden. Anschließend heißt es Verträge kündigen, neue Server mieten, etc.

6. Januar 2013

NodeJS MySQL pool

6. Januar 2013 - Geschrieben von Martin - 2 Kommentare

Momentan arbeite ich daran Resigame 2.0 auf NodeJS Basis zu entwickeln. Da abzusehen ist, dass auf Resigame wieder sehr hoher Traffic entstehen wird, wird das ganze Performance optimiert entwickelt. Cluster, SQL Pooling, etc.

Gestern habe ich mir ein Modul für den MySQL Pool geschrieben, denn bei vielen NodeJS Applikationen wird entweder für jeden HTTP Request eine MySQL Verbindung verwendet oder eine für alle.

Das Problem bei einer Verbindung pro HTTP Request ist, dass die Verbindungen bei hohen Nutzerzahlen sehr schnell ausgehen (MySQL max connections) und man ggf. ein paar Sekunden auf die nächste freie Verbindung warten muss.

Bei der Methode mit einer Verbindung, die für alle Nutzer verwendet wird, ist natürlich die Performance sehr bescheiden. Aufwändige Querys können die ganze Applikation stark ausbremsen.

Also habe ich einen MySQL Pool entwickelt, dieser wird beim starten der Anwendung erzeugt. Man übergibt die „poolsize“ – also die Anzahl der Verbindungen die hergestellt werden sollen (diese muss natürlich unter der maximalen Anzahl an Verbindungen, die der MySQL Server zulässt liegen) und die Zugangsdaten zum Datenbankserver. z.B.

var pool = new require('/helpers/pool')({
	poolsize: 50,
	mysql: {
		host: 'localhost',
		user: 'root',
		password: 'root',
		database: 'resigame'
	}
});

Querys können nun direkt an den Pool gesendet werden:

pool.query('SELECT * FROM users WHERE userID = ?', [userID], function(err, results) {
	// whatever
});

Der Aufruf fügt den Query zur Warteschlange hinzu und sieht nach ob gerade eine Verbindung frei ist. Wenn ja wird diese gleich verwendet, ansonsten dauert es bis wieder eine Verbindung frei wird. (Ergo, je mehr Benutzer die gleichzeitig auf die Datenbank zugreifen, desto höher die poolsize –> mehr Performance).

Hier der Quellcode, ich werde ihn die Tage noch ein wenig erweitern und dann auf GitHub stellen.

/** 
 * MySQL pool to hold multiple connections to the database server
 *
 * @author	Martin Lantzsch <martin@linux-doku.de>
 */
 
var mysql = require('mysql');
module.exports = function(config) {
	this.freeConnections = [];
	this.queryQueue = [];
 
	this.createConnection = function(details) {
		var connection = mysql.createConnection(details);
		connection.connect();
		connection.number = this.freeConnections.length;
		this.freeConnections.push(connection);
	};
 
	this.query = function(command, params, callback) {
		if(typeof(params) == 'function') {
			callback = params;
			params = [];
		}
 
		this.queryQueue.push({
			command: command,
			params: params,
			callback: callback
		});
		this.work();
	};
 
	this.work = function() {
		var self = this;
		if(self.queryQueue.length > 0) {
			if(self.freeConnections.length > 0) {
				var query = self.queryQueue.shift();
				var connection = self.freeConnections.shift();
				connection.query(query.command, query.params, function(err, results) {
					if(err) throw err;
					query.callback(err, results);
					self.freeConnections.push(connection);
					if(self.queryQueue.length > 0)
						self.work();
				});
			}
		}
	};
 
	for(i = 0; i < config.poolsize; i++) {
		this.createConnection(config.mysql);
	}
 
	return this;
}

13. Juni 2012

Resi ganz frisch

13. Juni 2012 - Geschrieben von Martin - Ein Kommentar

So sah unsere kleine Resi die letzten 3 Jahre aus

Und so sieht sie nun nach der Frischzellenkultur/Vektorisierung aus

ganz hübsch geworden die kleine. Da ich nun ein .svg habe kann ich sie natürlich beliebig skalieren. Die Poster sollten also nicht mehr lange auf sich warten lassen ;-)

9. März 2012

CodeIgniter und die Leerzeichen

9. März 2012 - Geschrieben von Martin - Keine Kommentare


Wie ich gestern feststellen musste, mag CodeIgniter keine Leerzeichen in der URL. Hat man ein solches, wie ich in „Resigame 2.0“ so wirft es einfach eine stupide 404 Fehlerseite.

404 Page Not Found
 
The page you requested was not found.

4. August 2010

jQuery idTabs bei Seitenwechsel speichern

4. August 2010 - Geschrieben von Martin - 2 Kommentare

In letzter Zeit beschäftige ich mich wieder verstärkt mit JavaScript, bzw. jQuery. Heute habe ich auf Basis von idTabs (@BissyDesign – Danke für den Tipp ;-)) ein Horizontales Menü beflügelt, dessen Untermenüpunkte nun bei klick auf einen Obermenüpunkt ohne Seitenreload wechseln. Lange Rede kurzer Sinn:

und so sieht es nach dem klick auf „Nachrichten“ aus:

Leider hat die Sache mit idTabs einen Haken, und zwar springt das Menü nach einem Seitenreload wieder zum ersten Tab zurück und bleibt nicht beim gewählten. Doch dafür habe ich eine ganz einfache Lösung gefunden.

Als erstes müssen wir das jQuery Cookie Plugin einbinden: http://plugins.jquery.com/project/cookie
Nächster Schritt ist die Modifikation des idTabs Plugins, man suche folgende Stelle im Code (im Unkomprimierten Zeile 95):

$(id).show();
return s.change; //Option for changing url

und ändere dies zu:

$(id).show();
// write to cookie
$.cookie('PAGENAME_menu', id.split('#')[1], { expires: 1 });
return s.change; //Option for changing url

(PAGENAME_menu ist der Cookie Name, welcher an die Gegebenheiten angepasst werden muss).

Anschließend muss noch der Funktionsaufruf von idTabs Modifiziert werden:

$("#menu ul").idTabs();

zu

id = $.cookie("PAGENAME_menu");
if(id == null) { id = 0; } // failback if no cookie is availible
$("#menu ul").idTabs(id);

Nun wird bei jedem Tabwelchsel der aktuelle Tab in einen Cookie geschrieben. Beim neu laden der Seite wird dieser ausgelesen und dessen Wert (der letzte Tab) an idTabs als default Tab übergeben. Es muss lediglich beachtet werden, das keiner der Tabs das Attribut „class=’selected'“ hat, da unser Hack Trick sonst nicht funktioniert.

15. Mai 2010

jQuery – Mehrere Elemente verbergen

15. Mai 2010 - Geschrieben von Martin - 3 Kommentare

Jeder der mehrere Elemente auf ein mal mittels jQuery verstecken will, die gleich benannt wurden, wird früher oder später feststellen müssen, das der Selektor immer das erste gefundene Element verwendet.

<div id='test'>Test</div>
<div id='test'>Test 2</div>
<div id='test'>Test 3</div>

Um dies zu umgehen habe ich mir folgende 2 Funktionen geschrieben, welche alle Elemente die auf den 1. Parameter zutreffen nacheinander abarbeiten. Hierzu hole ich mir alle zutreffende Elemente via „$(name).get()“, anschließend kann ich diesen Array mittels „$.each()“ durchlaufen (das jQuery Pendant zu foreach() unter PHP). Da ich nun den Index des Elementes habe kann ich nun das entsprechende Element verbergen.

function hideAll(name, speed) {
  elm = $(name).get();
  $.each(elm, function(index, value) {
    $(name, [index]).hide(speed);
  });
}

Und hier noch eine zum Wieder ein faden (sanftes einblenden) der Elemente:

function fadeInAll(name, speed) {
  elm = $(name).get();
  $.each(elm, function(index, value) {
    $(name, [index]).fadeIn(speed);
  });
}

Als 2. Parameter wird immer der Speed übergeben in der die Animation ablaufen soll, diese sind die jQuery typischen wie „slow“ und „fast“.

Noch mal zu unserem obigen Beispiel, sollen nun alle 3 div’s ausgeblendet werden so benötigen wir lediglich folgenden Code:

hideAll('#test', 'slow');

und zum wieder einblenden selbstverständlich:

fadeInAll('#test', 'slow');