Wie man mit Varnish Webseiten noch schneller macht

by Maik
Categories: Allgemein
Tags: No Tags
Comments: 1 Comment
Published on: 03.04.2012

Auf der Symfony2 Doku Seite zum HTTP Cache wird sehr gut und ausführlich beschrieben, wie man aus einer Applikation noch mehr Speed durch Caching heraus holt. Dieser Geschwindigkeitszuwachs kann durch den eingebauten gateway cache oder durch einen externen Server/Dienst wie Varnish, der als Reverse Proxy fungiert, erreicht werden.

Die Funktionsweise des Cachings und der Edge Side Includes sind sicherlich einen extra Artikel wert. Da ich aber selbst bei Wikipedia nichts auf Deutsch dazu gefunden habe, habe ich kurzer Hand den englischen Artikel auf Deutsch übersetzt .  :-)

Varnish als Lastverteiler

Mir geht es aber um den Einsatz eines Varnish Servers als Lastverteiler. Nate Haug hat dazu einen sehr ausführlichen Artikel auf der Seite www.lullabot.com veröffentlicht. Er beschreibt die Konfiguration eines Varnish Servers (natürlich ausfallsicher) als Load Balancer.

Dieser versteht es nämlich den Verkehr nach bestimmten Anforderungen auf verschiedene, hinter ihm liegende, Webserver zu verteilen. Die Lastverteilung kann man sehr gut einstellen, so dass man auch einen Server, der mit anderen Aufgaben beschäftigt ist, als niedrig priorisierten zusätzlichen Knoten in den Webserver-Verbund mit aufnehmen kann. Es folgt die Übersetzung des o.g. Artikels.

Konfiguration von Varnish

Varnish hat normalerweise drei Dateien für die Konfiguration. Das Boot-Script, die systemweite Konfiguration und die VCL-Datei, die den Großteil der Arbeit übernimmt.

Das erste Script, welches gestartet wird, liegt – wie die meisten Startup-Scripte – unter /etc/init.d/varnish. Diese Datei benötigt kaum Änderungen, kann aber zum Verständnis beitragen und helfen, die weiteren Konfigurationsdateien zu finden, da das Startup-Script verantwortlich für deren Aufruf ist.

Die zweite Datei findet man normalerweise unter /etc/sysconfig/varnish (auf CentOS and RedHat Maschinen) oder unter/etc/default/varnish (auf Ubuntu).

Diese Datei definiert die globale Konfiguration für Varnish, z. B. auf welchem Port er laufen und wo der Cache gespeichert werden soll. Es gibt fünf verschiedene Möglichkeiten, das gleiche zu schreiben, wichtig ist nur, dass man die Cache Ablage von dem Default-Wert “file”, der den Cache auf die Festplatte auslagert, auf die Angabe “malloc” umstellt, da dies die Auslagerung in den Arbeitsspeicher gewährt. Nur letzteres macht wirklich Sinn. Wenn Sie nicht genug Arbeitsspeicher haben, (sagen wir ein paar Gigabyte) solllten Sie sich überlegen diesen aufzustocken.

Hier ist eine Beispiel-Konfiguration, die Nate Haug bei einem sehr populären Webauftritt benutzt, mit einer großen Anzahl Bilder und vielen Seiten, die gecached werden. Hier die Bespieldatei /etc/sysconfig/varnish :

DAEMON_OPTS="-a :80,:443 \
             -T localhost:6082 \
             -f /etc/varnish/default.vcl \
             -u varnish -g varnish \
             -S /etc/varnish/secret \
             -p thread_pool_add_delay=2 \
             -p thread_pools=<Number of CPU cores> \
             -p thread_pool_min=<800 / Number of CPU cores> \
             -p thread_pool_max=4000 \
             -p session_linger=50 \
             -p sess_workspace=262144 \
             -s malloc,3G"

Die letzte Zeile ist die Wichtigste, die gesetzt werden muss. In diesem Fall reservieren wir 3 Gigabyte Arbeitsspeicher für Varnish. Beachten Sie auch den Pfad, der anzeigt, welche VCL Datei genutzt werden soll. Es ist das Beste bei dem default Dateipfad der eingesetzten Distribution zu bleiben.  Bitte beachten Sie die Zeilen in denen <Number of CPU cores>, steht und setzen Sie die Werte Ihres Servers ein. Um Informationen über Ihr System zu erhalten können Sie in der Kommandozeile einfach grep processor /proc/cpuinfo, eingeben.

VCL Konfiguration

Die VCL Konfigurationsdatei ist das Herzstück um Varnish zu konfigurieren und dort werden alle wichtigen Änderungen vorgenommen. Es ist wichtig zu wissen, dass Varnish eine große Anzahl an Default Einstellungen enthält, die sich automatisch an die benutzerdefinierten Regeln anpassen.

Solange man keine speziellen Kommandos wie, “pipe”, “pass”, oder “lookup” benutzt, laufen die default Werte. Varnish wird mit einer komplett auskommentierten default.vcl Datei ausgeliefert, die als Referenz dient.

Wir arbeiten uns durch alle Sektionen. Hier ist eine Datei, die Nate Haug in seinem Unternehmen (Lullabot) zur Zeit einsetzt sowie eine Kopie der Varnish default Datei.

Lullabot’s default.vcl Datei für mehrere Web Server

defaults.vcl (von Varnish 2.1.3)

Jetzt wird`s richtig interessant – die VCL Datei!

Gesunheitschecks und Direktoren (“directors”)

Ein neueres Feature von Varnish (Version 2.x und höher) war das Hinzufügen von Direktoren, um den Datenverkehr auf eine beliebige Anzahl von Webservern aufzuteilen. Diese Direktoren haben Zugriff auf jeden Webserver, um zu überprüfen, ob der Server noch läuft und in welchem Zustand er sich befindet. Wenn Varnish eine Web-Anfrage erhält, die noch nicht gecached ist, wird die Anfrage nur an einen “gesunden” (d.h. einen niedrig ausgelasteten) Server weitergereicht.

Der Varnish Server wird normalerweise so konfiguriert, dass er auf HTTP und HTTPS Verkehr reagiert. Also benötigen wir die Liste von Webservern in “zweifacher Ausfertigung”, soll heißen einmal den Port 80 für den normalen Verkehr sowie den Port 443 für SSL-verschlüsselte Verbindungen. Wenn Sie den Apache anders konfiguriert haben, müssen Sie die Ports dementsprechend anpassen.

# Define the list of backends (web servers).
# Port 80 Backend Servers
backend web1 { .host = "192.10.0.1"; .probe = { .url = "/status.php"; .interval = 5s; .timeout = 1s; .window = 5;.threshold = 3; }}
backend web2 { .host = "192.10.0.2"; .probe = { .url = "/status.php"; .interval = 5s; .timeout = 1s; .window = 5;.threshold = 3; }}

# Port 443 Backend Servers for SSL
backend web1_ssl { .host = "192.10.0.1"; .port = "443"; .probe = { .url = "/status.php"; .interval = 5s; .timeout = 1 s; .window = 5;.threshold = 3; }}
backend web2_ssl { .host = "192.10.0.2"; .port = "443"; .probe = { .url = "/status.php"; .interval = 5s; .timeout = 1 s; .window = 5;.threshold = 3; }}

# Define the director that determines how to distribute incoming requests.
director default_director round-robin {
    { .backend = web1; }
    { .backend = web2; }
}director ssl_director round-robin {
    { .backend = web1_ssl; }
    { .backend = web2_ssl; }
}

# Respond to incoming requests.
sub vcl_recv {
    # Set the director to cycle between web servers.
    if (server.port == 443) {
      set req.backend = ssl_director;
    }
    else {
        set req.backend = default_director;
    }
}

Der letzte Teil der Konfiguration ist in der vcl_recv Sub-Routine. Er definiert, welches Server-Set benutzt wird – basierend auf dem Port, auf dem Varnish den Verkehr erhalten hat.

Es ist eine gute Idee den verantwortlichen “health check” (also ein kleines Programm für den Feedback des Gesundheitszustands) auf jedem Web-Server zu installieren, um sicher zu gehen, das der Server bereit ist Daten zu liefern. Nate benutzt eine lokale Datei, direkt im root Verzeichnis des Webservers, mit dem Namen “status.php” um den Gesundheitszustand des Servers zu testen. Diese Datei testet viele Dinge. Unter anderem die folgenden:

  • testen, ob Drupal geladen wurde
  • die Verbindung zu der Master Datenbank
  • die Verbindung zu der Slave Datenbank (wenn vorhanden)
  • die Verbindung zu Memcache (wenn es genutzt wird)
  • es wird überprüft, ob der Webserver Schreibrechte auf das /files Verzeichnis hat

Wenn eine dieser Überprüfungen fehl schlägt, wirft das Script einen 500er Fehler und Varnish nimmt den Server aus der Rotation. Die status.php Datei ist natürlich auch sehr hilfreich, um einen Webserver offline zu nehmen. Wenn man die Datei einfach umbenennt, nimmt Varnish den Server aus der Rotation, während der Server selbst noch weiter läuft. Dies ist zum Beispiel nützlich um Wartungsarbeiten durchzuführen. Dieses Verfahren hat sich auch für das Durchführen von Upgrades oder dem Installieren neuer Software durchgesetzt.

status.php von Lullabot

Die status.php ist zwar relativ allgemein gehalten, sollte aber an den jeweiligen Anwendungszweck angepasst werden. Wenn Sie also weitere Dienste für den Betrieb Ihres Server benötigen, nehmen Sie diese auch mit in das Script auf, so dass Varnish keine Daten von einem nicht funktionierenden Server entgegen nimmt.

Caching, auch wenn der Apache Webserver nicht mehr erreichbar ist

Selbst wenn eine Umgebung komplett redundant ausgelegt ist, ist es möglich, dass die Webseite, aus verschiedensten Gründen, nicht erreichbar ist. Ein Programmierfehler, ein Datenbank Fehler, ein Verbindungsfehler oder einfach sehr viel Datenverkehr. In solchen Situationen (der häufigste Fall ist, das der Apache Server überlastet ist und Anfragen ablehnt) kann Varnish einem den Hintern retten. Varnish arbeitet dann mit einer “Schonfrist”. Der Apache Web Server gibt jeder Antwort, die er ausliefert, ein Auslaufdatum mit. Das bedeutet, dass Varnish den alten Inhalt verwirft und eine neue Antwort vom Webserver anfordert, wenn das Auslaufdatum erreicht ist. Wenn der Server aus o.g. Gründen nicht erreichbar ist, ist es unmöglich eine neue Antwort zu erhalten. “Grace” (Gnade) ist eine Einstellung, die es Varnish erlaubt, Seiten weiterhin auszuliefern, obwohl diese eigentlich schon veraltet sind, aber der Webserver nicht erreichbar ist. Varnish liefert diese veralteten Daten solange aus, bis der Webserver wieder verfügbar ist.

Um “Grace” zu aktivieren müssen nur die folgenden Einstellungen in den Bereichenvcl_recv undvcl_fetchvor genommen werden:

# Respond to incoming requests.
sub vcl_recv
{
    # Allow the backend to serve up stale content if it is responding slowly.
    set req.grace = 6h;
}

# Code determining what to do when serving items from the Apache servers.
sub vcl_fetch
{
    # Allow items to be stale if needed.
    set beresp.grace = 6h;
}

Beide Einstellungen können das Gleiche bedeuten,

aber das Setzen der Option in der vcl_fetch Einstellung muss länger sein, als das in vcl_recv. Man könnte die vcl_fetch “grace” Einstellung als die maximale Zeit, die Varnish ein Objekt im Cache halten darf, betrachten. Die Einstellung in der vcl_recv definiert, wann Varnish ein veraltetes Objekt benutzen soll, insofern eines vorhanden ist.

Auch wenn die Vorteile von “grace” beeindruckend sind, so sollte man immer daran denken, dass die Seite bereits im Cache sein muss, damit dieser Mechanismus funktioniert. Dies kann ein Problem werden, wenn man mit authentifizierten Benutzern zu tun hat, die nur schwer cachbare Dateien ausgeliefert bekommen. Wenn Sie ungecachte Seiten an authentifizierte Nutzer ausliefern und all Ihre Webserver versagen den Dienst, sind das Letzte, was die Nutzer sehen sollen, Fehlermeldungen. Wäre es nicht besser, wenn Varnish einen Fallback zu anonymen Seiten hätte, die bereits gecached sind, bis der Webserver wieder online ist? Glücklicherweise gibt es diesen Fallback und so etwas einzurichten ist sehr einfach. Ergänzen Sie einfach den folgenden Code in der vcl_recv Subroutine:

# Respond to incoming requests.
sub vcl_recv
{
    # ...code from above.

    # Use anonymous, cached pages if all backends are down.
    if (!req.backend.healthy)
    {
        unset req.http.Cookie;
    }
}

Varnish setzt eine Eigenschaft "req.backend.health" , wenn einer der Webserver erreichbar ist. Wenn alle Server unerreichbar sind, wird dieses Flag auf “false” gesetzt. Varnish entfernt den Cookie eines eingeloggten Users von dem eingehenden Request und versucht eine anonyme Version der Seite zu bekommen. Sobald ein Server wieder erreichbar ist, wird Varnish den Cookie wieder mitgeben, so dass der Nutzer – mit etwas Glück, eingeloggt bleibt.

Wie man Varnish dazu bringt, bestimmte Antworten nicht zu cachen

Bei vielen Webseiten ist es nötig, bestimmte Seiten, bzw. Seitenbereiche niemals zu cachen. Ein gutes Beispiel dafür ist sicherlich die Anzeige der eigenen Kontodaten bei einem eingeloggten Nutzer. In diesen Szenarien kann man Varnish einfach das “pass” Statement mitgeben. Die Daten werden dann von Varnish unter keinen Umständen gecached. Hier ein typisches Beispiel für Drupal:

# Do not cache these paths.
if (req.url ~ "^/status\.php$" ||
    req.url ~ "^/update\.php$" ||
    req.url ~ "^/ooyala/ping$" ||
    req.url ~ "^/admin/build/features" ||
    req.url ~ "^/info/.*$" ||
    req.url ~ "^/flag/.*$" ||
    req.url ~ "^.*/ajax/.*$" ||
    req.url ~ "^.*/ahah/.*$") {
    return (pass);
}

Varnish wird natürlich immer noch den Verkehr zwischen der äußeren Welt und dem Webserver behandeln, aber das “pass” Kommando sichert, dass die Anfragen immer an den Webserver weiter geleitet werden.

In einigen Situationen ist es jedoch unerlässlich, dass Varnish eine direkte Verbindung zwischen der äußeren Welt und dem Webserver herstellt. Warum?

Varnish beantwortet alle Anfragen mit einer bestimmten “content-length” (“Dateigröße”). Diese Information erlaubt dem Browser, dem Nutzer den Fortschritt anzuzeigen. Aber einige Dateien haben keine vorhersagbare Länge. Streamingdaten (Audio/Video) aber auch zur Laufzeit generierte Dateien, wie zum Beispiel PDF-Dateien, haben eine unbekannte Größe und Varnish kann keine “content legth” ausgeben. Damit Varnish, auch mit solchen Situationen umgehen kann, muss man ihn mit dem Kommando “pipe” füttern. Dann werden die Anfragen direkt an den Webserver durchgeleitet, d.h. Varnish funktioniert eher als eine Art Gateway.

Damit Varnish weiterhin in solchen Situationen funktioniert, muss er mit dem Kommando “pipe” gefüttert werden.

# Pipe these paths directly to Apache for streaming.
if (req.url ~ "^/admin/content/backup_migrate/export") {
    return (pipe);
}

Varnish ist auch ein gute Möglichkeit, um den Zugriff auf bestimmte URLs zu verhindern. Da die VCL Datei so flexibel nutzbar ist, kann man solche Pfade leicht verstecken. Am Beginn unserer VCL-Datei haben wir eine Zeile, die eine Access Control List in Form von IP-Adressen enthält. Diese Adressen werden als intern betrachtet. Ein gutes Beispiel wäre z.B. Drupal´s cron.php Datei. Diese Datei darf nur von den lokalen Webserver angestoßen werden, da sie zeitintensive Tasks, wie das Aufbauen des Suchindex, ausführt. Da die lokalen Webserver alle den Adressbereich 192.10.X.X haben, wird ihnen der Zugriff gewährt; alle anderen erhalten eine “Zugriff verweigert” Nachricht.

Am Anfang der default.vcl Datei werden die lokalen Zugriffe gewährt:

# Define the internal network subnet.
# These are used below to allow internal access to certain files while not
# allowing access from the public internet.
acl internal
{
    "192.10.0.0"/24;
}

Und dann in dem vcl_recvBereich:

# Respond to incoming requests.
sub vcl_recv
{
    # ...code from above.# Do not allow outside access to cron.php or install.php.
    if (req.url ~ "^/(cron|install)\.php$" &amp;&amp; !client.ip ~ internal)
    {
        # Have Varnish throw the error directly.
        error 404 "Page not found.";
        # Use a custom error page that you've defined in Drupal at the path "404".
        # set req.url = "/404";
    }
}

Den Cache von Varnish optimieren

Als erstes nehmen wir eine immer wieder kursierende, aber misslungene Konfiguration, auseinander. Wenn es darum geht, wie der gecachte Inhalt an User mit Cookies ausgeliefert wird, empfehlen viele Quellen die folgende Konfiguration:

# Routine used to determine the cache key if storing/retrieving a cached page.
sub vcl_hash
{
    # Do NOT use this unless you want to store per-user caches.
    if (req.http.Cookie)
    {
         set req.hash += req.http.Cookie;
    }
}

Normalerweise ist dies keine sinnvolle Empfehlung, solange man nicht eine gecachte Seite mehrfach an einen User ausliefert. Diese Einstellung erkennt den einmaligen Key, z.B. von Drupal, und verhindert, dass gecachte Inhalte zwischen Usern ausgetauscht werden. Diese Einstellung ist aber eine Resourcenverschwendung, da Drupal explizit den Cache-Control Header für angemeldete User auf “unter keinen Umständen cachen” setzt:

Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0

In anderen Worten, vermeiden Sie diese Einstellung, es sei denn, Sie haben wirklich einen Grund userspezifische Seiten zu cachen. In den meisten Situationen wird diese Einstellung einen Overhead erzeugen, ohne dass etwas gespeichert wird. Im Worst-Case-Szenario wird Ihr Cache mit Einträgen von jedem authentifizierten Nutzer gefüllt und die wirklich wichtigen anonymen Caches, die für tausende Nutzer genutzt werden können, werden hinaus gedrängt.

In den meisten Fällen ist es am sinnvollsten einen Cache für alle User vorzuhalten. Jeder Inhalt, der nicht an alle User ausgeliefert werden kann, kann direkt an Apache weiter gereicht werden. Da Drupal einen Cookie nutzt um authentifizierte Nutzer zu unterscheiden, ist es das Einfachste danach Ausschau zu halten und diese Anfragen dann nicht zu cachen. Dieses Cookie wird allerdings auch an Bilder, CSS und JavaScript-Dateien (eigentlich alle Mediendateien) gehangen.

Die HTML Seite kann für eingeloggte Nutzer durchaus anders aussehen aber es gibt keinen Grund die o.g. Dateien nicht zu cachen. Die einfachste Möglichkeit dies zu erreichen, ist die Cookies für diese Dateitypen generell zu verwerfen.

# Respond to incoming requests.
sub vcl_recv
{
    # ...code from above.
    # Always cache the following file types for all users.
    if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$")
    {
        unset req.http.Cookie;
    }
}

So weit so gut. Wenn man die Cookies für statische Dateien entfernt, werden diese für alle Nutzer gecached. Dies funktioniert für Standardinstallationen von Drupal, aber unglücklicherweise gibt es noch weitere Möglichkeiten Cookies zu setzen. Die häufigsten Problembereiter sind statische Tracking Scripts (wie Google Analytics) und Werbeserver. Ad-Scripts haben eine schreckliche Art Cookies per JavaScript zu setzen.  Meistens werden Varnish und Drupal von diesen Cookies nicht behelligt. Da aber jeder Cookie, der Varnish vom Browser übergeben wird, Varnish dazu bringt den Request direkt an Apache weiter zu reichen, müssen wir auf diese Cookies gesondert achten.

Es gibt viele Ansätze sich diesem Problem zu nähren, und die meisten Administratoren starten mit einer Blacklist von Cookies, die ausgefiltert werden müssen, damit nur die gewünschten übrig bleiben. Das könnte dann wie folgt aussehen:

// Remove has_js and Google Analytics __* cookies.
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)=[^;]*", "");

Dieser Ansatz wird normalerweise für einen kurzen Zeitraum funktionieren, aber sobald ein Ad-Script oder ein neues Stück JavaScript einen Cookie hinzufügt (wie das Kommentar Modul, oder das Flag Modul oder eines der vielen anderen Module), wird Varnish wieder gezwungen das Cachen der Seite zu unterlassen. Man muss dann den Übeltäter suchen und manuell zur Blacklist hinzufügen.

Es wäre doch sinnvoller eine Art Whitelist zu führen, in der alle Cookies die nicht aufgeführt sind, automatisch verworfen werden. Die Logik ist umfangreicher aber sicherlich auch eine dauerhafte Lösung:

# Respond to incoming requests.
sub vcl_recv
{
    # ...code from above.
    # Remove all cookies that Drupal doesn't need to know about. ANY remaining
    # cookie will cause the request to pass-through to Apache. For the most part
    # we always set the NO_CACHE cookie after any POST request, disabling the
    # Varnish cache temporarily. The session cookie allows all authenticated users
    # to pass through as long as they're logged in.
    if (req.http.Cookie)
    {
        set req.http.Cookie = ";" req.http.Cookie;
        set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
        set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|NO_CACHE)=", "; \1=");
        set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");if (req.http.Cookie == "")
        {
            # If there are no remaining cookies, remove the cookie header. If there
            # aren't any cookie headers, Varnish's default behavior will be to cache
            # the page.
            unset req.http.Cookie;
        }
        else
        {
            # If there are any cookies left (a session or NO_CACHE cookie), do not
            # cache the page. Pass it on to Apache directly.
            return (pass);
        }
    }
}

Sobald Sie Cookies gezähmt haben, kommt auch schon der nächste Feind des Cachings um die Ecke, der “Accept-Encoding” Header, der von den Browsern mit gesendet wird. Jeder Browser sendet Informationen über seine Caching Fähigkeiten zum Server. Alle modernen Browser unterstützen zum Beispiel die “gzip” Kompression aber jeder informiert den Server auf eine andere Weise. Zum Beispiel so:

Firefox, IE: gzip, deflate
Chrome: gzip,deflate,sdch
Opera: deflate, gzip, x-gzip, identity, *;q=0

Zusätzlich zu den Headern, die vom Browser gesendet werden muss Varnish auch noch auf die Header vom Webserver achten, die normalerweise Zeilen wie die folgende enthalten:

Vary: Accept-Encoding

Das bedeutet, dass Varnish für jede Version von “Accept-Encoding” eine eigene Version, für jeden verbundenen Browser vorhalten muss. Das bedeutet, dass Varnish für jeden Browser und, im schlimmsten Fall sogar für verschiedene Versionen des selben Browsers, eigene Cache Dateien anlegt. Dies ist nicht nur eine Verschwendung von Speicherplatz sondern macht das Cachen auch recht ineffizient, da ja eigentlich jeder Browser “gzip” versteht, es nur unterschiedlich mitteilt. Um diesen Umstand zu vermeiden fügen wir das folgende Segment in die vcl_recv-Datei ein:

# Handle compression correctly. Different browsers send different
# "Accept-Encoding" headers, even though they mostly all support the same
# compression mechanisms. By consolidating these compression headers into
# a consistent format, we can reduce the size of the cache and get more hits.
# @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
if (req.http.Accept-Encoding)
{
    if (req.http.Accept-Encoding ~ "gzip")
    {
        # If the browser supports it, we'll use gzip.
        set req.http.Accept-Encoding = "gzip";
    }
    else if (req.http.Accept-Encoding ~ "deflate")
    {
         # Next, try deflate if it is supported.
         set req.http.Accept-Encoding = "deflate";
    }
    else
    {
         # Unknown algorithm. Remove it and send unencoded.
         unset req.http.Accept-Encoding;
    }
}

Fazit

Varnish ist ein erstaunliches und extrem effizientes Tool um Webseiten auszuliefern. Neben der tollen Eigenschaft, Ihre Seite schneller zu machen, erweitert Varnish Ihr System zusätzlich um Redundanz, da es als eine Art Backup fungiert und auch wenn der Webserver nicht erreichbar ist. Um Varnish zu diesem effektiven Backup- und Caching-System zu machen, muss er die eingehenden Header des Browsers säubern, überflüssige Cookies verwerfen und den “Accept-Encoding” Header konsolidieren.

Nach so ausführlichen Erläuterungen kann man leicht von der Problematik überwältigt werden aber die gute Nachricht ist, die hier angebotenen Dateien können “out of the box” für so ziemlich jede Drupal Seite verwendet werden und gleich ihre Arbeit aufnehmen. Für die meisten Seiten sind keine zusätzlichen Anpassungen notwendig und die Seiten, die noch etwas Feintuning benötigen, haben eine gute Grundlage. Varnish beantwortet, auf den Seiten von lullabot, ungefähr 85 % der Anfragen ohne den Webserver überhaupt anzufragen. Selbst in Hochlast-Zeiten mit hunderttausenden Anfragen pro Stunde, benötigt Varnish nur ca. 5 % der CPU auf einem durchschnittlichen 4-Kern-Server. Anstatt mehr Webserver zu kaufen und einzurichten, sollten Sie lieber ein oder mehrere Varnish Maschinen vor den Webservern einrichten. Dies kann eine unglaubliche Entlastung für die Webserver sein und gleichzeitig Ihre Webseite beschleunigen.

Wenn Sie die aktuelle default.vcl, die von lullabot eingesetzt wird, noch nicht herunter geladen haben, sollten Sie das jetzt tun. Mit den ausführlichen Erklärungen können Sie ihre eigene VCL Konfiguration starten. Happy Caching.

Javascript – String als Funktion ausführen

Categories: Allgemein, Javascript
Tags: No Tags
Comments: Comments Off
Published on: 21.03.2012

Wenn man die Google Maps Api einbindet, kann man damit ja viele schicke Sachen machen. Man kann z.B. einen Autocompleter für die Eingabe von Orten einbinden oder eine angepasste Google Map. Um diese Funktionen nutzen zu können, muss man die Google Maps API als JavaScript Datei laden.

Dies geht reicht einfach über die Codezeile:

<script src="http://maps.google.com/maps/api/js?v=3&sensor=false" type="text/javascript"></script>

Da Google aber die Verwendung der API auf 2500 Aufrufe pro Tag beschränkt, (Geo Coding API), wäre es ja sinnvoll, das Laden nur durch zu führen, wenn die Seite wirklich eine Google Map anzeigt bzw. wenn ein ein User sich lokalisieren möchte.

function googleMapsApi()
{
  scope = null;
  this.loadGoogleMapsApi = function (callback)
    {
        if (typeof(google) !== 'undefined' && typeof(google.maps) !== 'undefined')
        {
            // trick to execute the callback as a function
            window[scope][callback]();
            return;
        }
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = "http://maps.google.com/maps/api/js?sensor=false&callback=" + scope + "." + callback;
        document.body.appendChild(script);
    }
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "http://maps.google.com/maps/api/js?sensor=false&amp;callback=" + scope + "." + callback;
    document.body.appendChild(script);
    }
  }
  this.setScope(scopeToSet)
  {
    scope = scopeToSet;
  }
}

Der entscheidende Codeschnipsel ist der folgende:

window[scope][callback]();

scope ist in diesem Fall die Instanz der Klasse googleMapsApi.
callback ist die Funktion, die nach dem erfolgreichen Laden der GoogleMapsApi auf gerufen werden soll.
Damit kann man also einerseits eine JavaScript Datei direkt einbinden, andererseits aber auch einen Callback per String weiter geben.
Bevor jemand sagt, eval() hätte es auch getan:

Zitat http://www.jslint.com/lint.html

eval is evil
The eval function (and its relatives, Function, setTimeout, and setInterval) provide access to the JavaScript compiler. This is sometimes necessary, but in most cases it indicates the presence of extremely bad coding. The eval function is the most misused feature of JavaScript.

Symfonday 2011 in Köln

Categories: Symfony
Tags: No Tags
Comments: 1 Comment
Published on: 26.10.2011

Hier mal eine kurze Zusammenfassung zum Symfonyday 2011

Igor Wiedler – Silex – Symfony goes micro

Als erstes sprach Igor Wiedler über Silex, einem micro-framework, dass auf Symfony2 Komponenten aufbaut. Genauer gesagt, auf einzelnen Elmenten, die auch in Symfony2 genutzt werden. Es ist auch eines der ersten Projekte, die ich kenne, dass mit Phar gebaut wurde. Phar ist eine, in PHP5.3 eingeführte Erweiterung, die es erlaubt Mehrere Dateien, ja sogar Verzeichnisse, in eine, per PHP, ausführbare Datei zu verwandeln. Man erhält etwas ähnliches, wie eine Java “.jar” Datei.
Das Ziel von Silex ist es eine schnelle Entwicklung zu ermöglichen, z.B. Prototyping, oder auch kleine überschaubare Microsites zu bauen ohne gleich alle Funktionen von Symfony2 zu nutzen. Es ist aber auch ideal geeignet um z.B. einen REST-Webservice zu entwickeln, bei dem keine Seiten generiert werden müssen, sondern einfach eine Request eingeht und dieser mit geringem Aufwand über eine API beantwortet wird.
Igor hatte bei seiner Präsentation auch den ein oder anderen “eyeopener” dabei. Zum Beispiel wie man die, mit PHP5.3 eingeführten Lambda-Funktionen, (anonyme Funktionen) praktisch einsetzt. Weiterhin gab er einen Einblick, wie umfangreich dieses Microframework ist und welche Symfony2 Komponenten verwendet werden.

Marc Weistroff – Inspire yourself of Symfony2 to create better code

Marc Weistroff wollte uns mit Symfony2 inspierieren, besseren Code zu schreiben. Ich glaube, dass ist ihm gelungen. Er hat die wichtigsten “State of the Art” Paradigmen genannt und gezeigt, wie diese in Symfony2 eingesetzt werden. Zum Beispiel, wie wichtig Separation of Conserns ist. Eigentlich ist es aber auch ganz einfach, wenn man an die Trennung von HTML und CSS denkt. HTML ist für den Inhalt da, CSS für die Gestaltung. Ganz wichtig ist auch, dass man nicht mit “Magie” anfängt. Es gibt in der Programmierung so etwas wie Magie nicht, denn sobald man etwas als Magie bezeichnet, ist es nicht mehr nachvollziehbar und verletzt sicherlich das “Separation of Conserns” Prinzip. Er beschrieb anhand Symfony2 wie man z.B. sinnvolle Fehlermeldungen ausgibt, oder auch wie wichtig es ist Coding Standards zu folgen. Denn es ist wichtig, dass auch jemand anders den Code versteht. Wenn dies nämlich nicht der Fall ist, wird derjenige um diesen Code herum bauen und es entsteht ein Gewirr, was keiner mehr versteht. Marc sprach auch noch einen wichtigen Punkt an: Interfaces. Wenn man Interfaces nutzt, wird die Schnittstelle definiert. Wenn sich dann etwas an der Programmierung ändert, kein Problem. Mit einem Interface ist genau vorgegeben, was eine Klasse wie machen soll, bzw. wie diese reagiert. So kann man die zu Grunde liegende Klasse (z.B. Anbindung an eine Datenbank) ändern, ohne dass etwas anderes geändert werden muss. Tests dürfen natürlich in so einer Präsentation nicht fehlen. Symfony2 wird mit 5000 Tests ausgeliefert und es kommen immer neue dazu. Dies verhindert schlaflose Nächte für einen Programmierer, da man sich darauf verlassen kann, dass man sofort erfährt, dass die letzte Änderung nicht gut durchdacht war ;-). Ein weiteres “Paradigma” welches ich sehr gut finde, war “If standard sollutions exist use it”. Wenn es bereits eine Lösung für ein Problem gibt, dann sollte man diese auch nutzen. Zur Not kann man diese Lösung, wenn sie z.B. in einer anderen Programmiersprache vorliegt, migrieren. Alles ist besser, als beim Reißbrett an zu fangen. Wenn man, zum Beispiel wie Marc, einen Single-Sign-On realisieren möchte gibt es den Standard OAuth2 (ca. 40 Seiten Beschreibung). Er fand eine gute Umsetzung, jedoch in Ruby. Er kannte Ruby bis dato nicht, begann aber mit der Portierung. (Ruby U R so Weird => “Ruby, du bist so schräg”) Er hat als erstes die Tests migriert und sich dann daran durch gearbeitet. Wichtig dabei ist auch, zu überlegen, ob man die gesamte Implementierung benötigt, oder nur einen Teil. (YAGNI – You Ain’t Gonna Need It) Man sollte sich vorher überlegen, welche Methoden und Klassen man wirklich benötigt und nicht etwas schreiben, was man “bestimmt mal braucht” oder “wenn ich schon mal dabei bin”. Diese Zeit sollte man lieber in das Schreiben von Tests oder dem refaktorieren legen.

Stefan Koopmanschap & Christian Schaefer – Catching Opportunities with Open Source

Ein Highlight des Tages (neben dem Auftritt von @fabpot) war sicherlich der Talk von Christian und Stefan. Sie haben eine Stunde lang darüber geredet, wie wichtig es für einen selbst ist, wenn man sich an Open Source aktiv beteiligt und ich glaube sie haben bei so manchem eine “Inception” vorgenommen. Soll heißen sie haben uns gezeigt, was man alles gewinnt, wenn man sich aktiv beteiligt und nicht nur die Open Source Software nutzt. Stefan meinte z.B., dass man allein durch die Beteiligung an Reputation gewinnt. Dass klingt erst mal nach nicht viel, er hat sich aber inzwischen so einen Namen gemacht, dass er, als er eine neue Stelle suchte zehn Angebote bekommen hatte und sich seinen Arbeitgeber aussuchen konnte. Es bringt einem aber auch persönlich sehr viel, da man Feedback provoziert. Andere beschäftigen sich mit dem Beitrag (es muss nicht immer Quellcode sein, es kann z.B. auch Dokumentation sein) und geben Tipps oder freuen sich einfach nur, dass es jetzt eine neue Funktion gibt, die sie gebrauchen können. Und Christan und Stefan meinten, ganz nebenbei kann es passieren, dass man zu Konferenzen in der ganzen Welt eingeladen wird. Wir wollen ja keine Superstars werden, aber so ein klein wenig berühmt wäre schon nicht schlecht. Warum aber sollten Firmen in Open Source investieren, oder gar ihre Produkte offen legen? Wenn man diesen Schritt geht provoziert man auch hier Feedback und es ist wahrscheinlich, dass Bugs oder auch Sicherheitslücken gefunden werden. Meine Befürchtung habe ich dann auch in der Fragerunde zum Ausdruck gebracht. Die beiden beruhigten mich aber, indem sie mir sagten, dass es wahrscheinlicher ist, dass eine Sicherheitslücke von einem der Helfen möchte gefunden wird, als von Kriminellen. Zu guter Letzt haben sie den wohl wichtigsten Punkt angesprochen. Wenn man sich an Open Source beteiligt, ist man nicht allein und dieses Gefühl gibt einem Kraft. Man merkt, dass einem auch von anderen geholfen wird, wenn man mal nicht weiter kommt. Wenn man denen dann im Gegenzug auch hilft, wenn diese nicht weiter kommen, dann hat man nicht nur ein gutes Gefühl sondern es entwickelt sich ein richtiges “soziales Netzwerk”.

Lunch

Das  Mittagessen war gut, leider gab es nur zwei “Ausgabestellen”. Da ca. 300 Leute da waren, war die Schlange natürlich recht lang. Vermutlich war dies auch eine der wenigen Veranstaltungen, bei denen sich die Schlange vor den Männer-Toiletten bildet, es waren nämlich nur drei Frauen im Publikum. Gestärkt mit Currywurst ging es in die nächste Runde.

Thomas Rabaix – The Sonata AdminBundle

Eigentlich habe ich mich auf die Präsentation von Thomas Rabaix gefreut, da der Admin-Generator bei Symfony1 ein sehr mächtiges Tool ist und ich es gelernt habe, zu lieben. Das Sonata AdminBundle soll dieses beerben. Leider war die Aussprache von Thomas ein sehr französisches Englisch. Außerdem hat er sich in Details verloren, die einem das Gefühl gaben, dass dieses Tool mehr Aufwand als Nutzen bringt. Ich werde es mir auf jeden Fall ansehen und wir werden es auf Arbeit sicherlich einsetzen, aber der Vortrag hat mir ein wenig die Vorfreude genommen.

Hugo Hamon – Extending and Leveraging the Power of the Command Line Interface

Hugo wollte mit uns spielen und zwar Hangman. Anhand des einfachen Spielprinzips hat er uns gezeigt, wie einfach es ist mit Symfony2 Konsolenanwendungen zu schreiben. Dies ist sicherlich nicht immer von Nöten, aber gerade bei Cronjobs ist die Kommandozeile einfach unschlagbar. Oder auch immer wiederkehrende Sachen kann man damit sehr gut automatisieren. Man kann das Ganze sogar richtig hübsch machen, indem man Farben verwendet. Natürlich durfte auch in diesem Vortrag die Erwähnung von UnitTests nicht fehlen und Hugo zeigte auch gleich an seinem Hangman-Spiel wie man Code refakturiert und ihn testbar gestaltet.

Richard Miller – Dependency Injection and the Symfony2 Service Container

Richard Miller gab dann einen sehr guten Einblick in die Logik der DependencyInjection. Endlich ist mir klar geworden, wie dies genau funktioniert und warum man es einsetzt. Ich denke es ging einigen im Publikum so. Stück für Stück ging es weiter, jeder Schritt war nachvollziehbar und man konnte am Ende zu keinem anderen Schluss kommen: DependenyInjection ist die Zukunft der Softwareentwicklung. Zumindest im Symfony2 Umfeld. Richard erwähnte aber nicht nur die “Sonnenseiten”. Es ist nämlich sehr schwer Fehler zu finden, da der Container ja bei Symfony2 in einer XMLI(YML/PHP)-Datei liegt und nicht ohne weiteres mit dem restlichen Code verknüpft werden kann. Um den Überblick zu behalten gibt es aber das Kommando symfony container:debug und wer noch mehr Hilfe braucht das JMSDebuggingBundle.

Fabien Potencier – The State of Symfony

Symfony2 ist das Produkt einer Community und doch ist es ein Mann der dahinter steht und dieses Framework geformt und maßgeblich entwickelt hat (Contributors to Symfony2) Fabien Potencier! Er hat eine Keynote gehalten, die er auch noch mal unter “What is Symfony2” zusammen gefasst hat. Im Kern geht es darum, dass er mit Symfony die Weltherrschaft anstrebt. ;-) Nein, Scherz. Er möchte dass Symfony2 nicht als klassisches MVC-Framework verstanden wird, sondern als ein wiederverwendbares Set von einzelnen entkoppelten, zusammenhängenden PHP Komponenten die weit verbreitete Webentwicklungsprobleme lösen. Basierend auf diesen Komponenten ist Symfony2 auch ein vollständiges Framework.

First, Symfony2 is a reusable set of standalone, decoupled, and cohesive PHP components that solve common web development problems.

Then, based on these components, Symfony2 is also a full-stack web framework.

Die Frage, ob Symfony2 ein MVC-Framework ist, beantwortet Fabien in seiner unnachahmlichen Art: “I don’t care”. Er ist der Meinung, dass das MVC Pattern nicht die Art ist, wie das Web heute funktioniert. Symfony2 ist ein HTTP- nein, besser ein Frage/Antwort-Framework. Fabien verheimlicht nicht, dass er das Enterprise Umfeld im Blick hat und betont, dass dort nur 2-3 Frameworks überleben werden. Er wirft die Frage in den Raum, wie Symfony diese Herausforderung meistern kann. Natürlich hat er auch schon eine Antwort darauf. Er will alles mit den einzelnen Komponenten “infiltrieren”. Er brachte dann auch gleich noch ein paar Beispiele wo dies schon gelückt ist. Doctrine2, Propel2, PHPUnit Flow3, phpBB, Drupal8 und einige andere mehr. Diese verwenden Komponenten, z.B. den YML-Parser oder die Console oder als die wichtigsten Komponenten die httpFoundation und den DependencyInjectionContainer.

Alles in allem war es ein sehr guter Vortrag. Aber Fabien hat auch ein Live Beispiel gebracht, als er zu einer Firma gerufen wurde, bei denen eine Symfony2 Anwendung langsam lief. Fabien hat sich alles sehr genau angesehen und sich an Symfony1 erinnert. Dort gab es eine Möglichkeit um zu sehen, welche Aktionen, wie lang gedauert haben. Leider war dieses Tool immer recht ungenau, deshalb wurde es nicht in Symfony2 übernommen. Da hat sich Fabien noch einmal hingesetzt und eine “stopwatch” Implementierung gemacht, die sich gewaschen hat. Man kann sich jetzt mit der Developer Toolbar eine genauen Ablauf der Ladezeiten ansehen.

Bei der obligatorischen Fragerunde fragte jemand, ab wann denn dieses coole Feature verfügbar sei. Fabien wechselt auf seine Konsole und macht ein “git push” (er lädt dieses Feature sozusagen direkt zu github hoch und das live :-) ) und der ganze Saal ist begeistert. Man kam sich bei seinem Auftritt ein wenig an Steve Jobs erinnert vor, was aber durchaus positiv gemeint ist. Da auch Fabien ein Visionär ist. Als ein Teilnehmer ihn fragte, woher er die Zeit nehme so viel an Symfony zu bauen, sagte er nur “I don play games”. (Er spielt keine Computerspiele und hat somit offensichtlich viel Zeit).

Fazit

Es war ein rundum gelungener Tag. Da der nächste Symfonyday in Berlin sein wird, werde ich sicherlich wieder mit dabei sein. Es ist einfach toll auch mal mit dem ein oder anderen in Gespräch zu kommen oder zumindest die Köpfe hinter den Twitternamen zu sehen. Die Veranstaltung, insbesondere Stefan und Christian, haben mir gezeigt, dass es eine tolle Sache sein kann sich aktiv ein zu bringen und zu helfen. Man macht zwar gern einmal Versprechen, die man nicht hält, aber ich denke dass ich mich in Zukunft auch einbringen werde, wie das sei erst einmal dahin gestellt.

Die Slides kann man sich übrigens bei symfony-zone.com ansehen.

eigenen Symfony (1.4) formFilter erstellen

by Maik
Categories: Symfony
Tags: No Tags
Comments: Comments Off
Published on: 20.09.2011

Der Admin Generator von Symfony ist schon toll, wenn man Daten schnell anzeigen und bearbeiten möchten. Manchmal stößt man allerdings an die Grenzen der Automatisierung, z.B. wenn man alle aktuell gültigen Artikel (in einem eigenen FormFilter) angezeigt haben möchte. Man kann zwar die Ansicht einschränken mit einem “von” und “bis” Datum, aber es geht auch einfacher.
In der betreffenden *FromFilter.class.php setzt man ein Widget (in die Methode configure()).
[sourcecode language="php"]$formFilterCheckbox = new sfWidgetFormInputCheckbox();
$this->setWidget(‘aktiv’, $formFilterCheckbox);[/sourcecode]
Dann muss man nur noch eine Funktion mit der Syntax add*Widgetname*ColumnQuery einfügen.
Diese bekommt die Grund SQL übergeben, (es können ja auch mehrere Filteroptionen ausgewählt worden sein) das betreffende Feld und natürlich den Wert.
Sicherlich ist es sinnvoll, den Query in der ArticlesTable zu kapseln, aber für das Beispiel genügt das, denk ich.
[sourcecode language="php"]
/**
* Adds the possibility to filter only active articles
* @param string $query
* @param string $field
* @param boolean $value
* @return type string
*/
public function addAktivColumnQuery($query, $field, $value)
{
if($value == 1)
{
ArticleTable::getInstance();
$query = $query->from(‘Article a’)
->where(‘valid_till >= ?’, date(‘Y-m-d 00:00:00′));
}
return $query;
}[/sourcecode]

Cherry Cymotion Master Tastatur unter Ubuntu einrichten

Categories: Ubuntu
Tags: No Tags
Comments: Comments Off
Published on: 23.08.2011

Es ist immer wieder erstaunlich, dass die beste Hilfe für Linux immer noch in der Community zu suchen ist. Die Hardwarehersteller, in meinem Fall Cherry, schaffen es nicht, eine vernünftige Anleitung für die Einrichtung einer Tastatur bereit zu stellen. Da wird von Kernel kompilieren gesprochen und das ist eine Sache, die ich versuche zu vermeiden, da es, meiner Meinung nach, heutzutage nur noch einige Spezialfälle gibt, in denen sich das lohnt. Bei jedem Kernel Update der Distribution muss ich dann ja wieder neu kompilieren.
Es gibt (von der Community erstellt) ein sehr gutes HowTo für die Einbindung der Cherry Cymotion Master Tastatur in ein Ubuntu.
http://www.ubuntufreunde.de/forum/2987/cherry_cymotion_master_linux.html
Für mich hatte der 3. Schritt völlig ausgereicht: einfach die folgende Datei (cymotion (das .txt entfernen)) herunter laden und in das Verzeichnis
/usr/share/X11/xkb/compat/

legen. Dann noch in der Datei /usr/share/X11/xkb/compat/complete den Zusatz augment “cymotion” hinzufügen und den X-Server neu starten.

Fertig :-)

Apache lastet nach der Installation den kompletten Arbeitsspeicher aus

by Maik
Categories: Allgemein
Tags: No Tags
Comments: Comments Off
Published on: 28.04.2011

Wenn jemand das Problem haben sollte, dass der Apache-Server sich nach einer Windows-Installation allen verfügbaren RAM nimmt und die Maschine fast zu stehen bekommt, hier zwei Tipps.
1. Es wird immer empfohlen in der Datei:
Apache Software Foundation\Apache2.2\conf\extra\httpd-mpm.conf
Dein Einrtag “Win32DisableAcceptEx” zu aktivieren, bzw. hinzu zu fügen.


    ThreadsPerChild      150
    MaxRequestsPerChild    0
    <strong>Win32DisableAcceptEx</strong>

Wenn das nicht helfen sollte muss man den Winsocket-Katalog resetten:
netsh winsock reset
Genaueres zu dem Thema findet man unter:
http://support.microsoft.com/kb/299357/en-us

Welcher Radiobutton wurde denn nun gewählt?

by Maik
Categories: jQuery
Tags: No Tags
Comments: Comments Off
Published on: 20.04.2011

Um mit jQuery auf den Wert eines Radiobuttons zu zu greifen, kann man entweder durch alle Buttons einzeln durch gehen, oder folgenden Codeschnipsel verwenden:

jQuery("input[@name=Name]:checked").val();

jQuery sucht dann alle Elemente mit dem Namen(!) “Name” und gibt den Wert desjenigen zurück, welches gewählt wurde.

Die (Un)Verträglichkeit von JavaScript/CSS-Dateien und Browser-Cache

by Maik
Categories: Allgemein, Webserver
Tags: No Tags
Comments: Comments Off
Published on: 15.04.2011

Eigentlich möchte man ja, dass die CSS und Javascript-Dateien nur einmal vom User herunter geladen werden und dann lange Zeit nicht mehr. Dieser Wunsch ist verständlich, da ja inzwischen Javascript/CSS-Frameworks wie jQuery viele Kilobyte belegen.
Man kann natürlich die alte Datei löschen und eine neue Datei referenzieren. Dies ist aber sehr aufwendig und wenn man irgendwo eine Stelle vergessen hat, wo die CSS/Javascript-Datei noch verwendet wird, kann es zu unschönen Nebeneffekten, bis hin zum Totalausfall der Seite, wegen fehlender CSS-Anweisungen, kommen.
Um das zu verhindern kann man mit einer einfachen RewriteRule in der .htaccess Datei wahre Wunder bewirken.
Mit der Zeile
RewriteRule ^(css|js)/(.*)\.(v_.*)\.(js|css)$ /$1/$2.$4 [L]
wird der Webserver angewiesen nach Anforderungen zu suchen, die CSS bzw. Javascript-Dateien aus dem jeweiligen Verzeichnis laden wollen.
Das “Geheimnis” der Regel, ist der Abschnitt (.*)\.(v_.*)\.(js|css). Es wird nach einem Dateiname gesucht, die in der Mitte “v_” enthält und am Ende eine JS/CSS-Datei ist. Wenn diese gefunden wird, wird der Zweite und der Vierte Teil der Anforderung umgesetzt.
Der Mittlere Teil mit dem “v_” wird einfach ignoriert. Somit ist es möglich, ein und die selbe Datei immer wieder zu bearbeiten, z.B. die main.css, und im Quellecode der Seite steht dann bei der einen Seite main.v_1.1.css und auf einer anderen main.v_1.2.css. Im Hintergrund wird immer die gleiche Datei geladen.

Suchen und Ersetzen von Textteilen in MSSQL

by Maik
Categories: Allgemein
Tags: No Tags
Comments: Comments Off
Published on: 28.03.2011

Wenn man Textteile suchen und ersetzen möchte, dann geht das in SQL einfach mit der REPLACE-Funktion. Leider stößt die Funktion bei TEXT-Feldern an Ihre Grenzen und verweigert den Dienst.
Um aber dennoch Textteile ersetzen zu können habe ich unter http://blogs.x2line.com/al/archive/2008/05/03/3417.aspx die Lösung gefunden (und noch verfeinert :-) ).
Ich habe es noch ein wenig auf gebohrt, so dass man auch die Tabelle und die Spalte, in der die Ersetzung statt finden soll, selbst bestimmen kann.

CREATE PROCEDURE [dbo].[SearchAndReplace]
@FindString      NVARCHAR(100),
@ReplaceString NVARCHAR(100),
@ReplaceTable  NVARCHAR(100),
@column	      NVARCHAR(100),
@id_column      NVARCHAR(100)
AS
BEGIN
  DECLARE @TextPointer VARBINARY(16),
               @DeleteLength INT,
               @OffSet INT,
               @id INT,
               @sql VARCHAR(8000)

SET @DeleteLength = LEN(@FindString)
SET @FindString = '%' + @FindString + '%'

SET @sql = '
DECLARE @TextPointer VARBINARY(16), @OffSet INT, @id VARCHAR(50)

DECLARE searchReplace CURSOR FOR
SELECT TEXTPTR('+@column+'), '+@id_column+'
FROM '+@ReplaceTable+'

OPEN searchReplace
FETCH NEXT FROM searchReplace INTO
@TextPointer, @id

WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @id
SET @OffSet = 0

WHILE ((SELECT COUNT(*)
FROM '+@ReplaceTable+'
WHERE (PATINDEX('''+@FindString+''', '+@column+'  COLLATE Latin1_General_BIN)  0 AND '+@id_column+' = @id)) &gt; 0)

BEGIN

SELECT @OffSet = PATINDEX('''+@FindString+''', '+@column+'  COLLATE Latin1_General_BIN)
FROM '+@ReplaceTable+'
WHERE PATINDEX('''+@FindString+''', '+@column+') &lt;&gt; 0
AND '+@id_column+' = @id

SET @OffSet = @OffSet -1
UPDATETEXT '+@ReplaceTable+'.'+@column+'
@TextPointer
@OffSet
'+CAST(LEN(@DeleteLength) AS VARCHAR)+'
'''+@ReplaceString+'''

END

FETCH NEXT FROM searchReplace INTO
@TextPointer, @id
END
CLOSE searchReplace
DEALLOCATE searchReplace'
PRINT @sql
EXEC(@sql)
SELECT 	@FindString+' has been replaced by '+@ReplaceString+' in ReplaceTable "'+@ReplaceTable+'" within column "'+@spalte+'"'

END

Doppelte Datensätze finden und löschen

by Maik
Categories: Allgemein
Tags: No Tags
Comments: Comments Off
Published on: 03.03.2011

Wenn man doppelte Datensätze in einer Tabelle finden und eliminieren möchte, ist das manuell zwar möglich, es wird aber ab einer gewissen Anzahl von Datensätzen nicht mehr sinnvoll möglich sein. Zum Glück gibt es SQL und dabei speziell den HAVING-Operator. Dieser ermöglicht uns, eine Datenbankabfrage zu starten und danach das Ergebnis zu bearbeiten. Man kann sich also zum Beispiel Umsatzwerte aus geben lassen und mit HAVING nur die, die sehr niedrig oder sehr hoch sind.
Oder, um auf die eigentliche Aufgabe zurück zu kommen, kann man damit auch doppelte Datensätze finden und löschen. Der Einfachheit halber gehen wir trotzdem davon aus, dass es eine ID-Spalte gibt, die aber teilweise doppelt vergeben wurde.

-- Damit wir sehen, wie viel Zeilen wir eingespart haben
DECLARE @count as INT
SELECT @count = COUNT(*) FROM quell_tabelle
-- Wir nehmen alle Zeilen, die bei einem <code>GROUP BY</code> 
-- Mehr als einmal vorkommen (HAVING count(*) > 1). 
-- Diese werden in eine temporäre Tabelle gespeichert
SELECT id, spalte1, spalte2, spalte3, zaehler = count(*)
 INTO #temp_tabelle
FROM quell_tabelle
 GROUP BY  id, spalte1, spalte2, spalte3
HAVING count(*) > 1
-- Es werden alle Zeilen aus der ursprünglichen Tabelle gelöscht, die auch in
-- der temporären Tabelle, jetzt aber nur einmal, gelöscht.
DELETE FROM quell_tabelle
WHERE ID IN(
SELECT ID FROM #temp_tabelle)
-- Jetzt müssen wir noch die Daten aus der temporären 
-- in die richtige Tabelle zurück schreiben
INSERT INTO quell_tabelle
SELECT id, spalte1, spalte2, spalte3
      FROM #temp_tabelle
-- die temporäre Tabelle können wir jetzt löschen
DROP TABLE #temp_tabelle
SELECT @count - COUNT(*) as Anzahl_geloeschter_Datensaetze
FROM quell_tabelle
«page 1 of 4
Welcome , today is Dienstag, 21.10.2014