KenntWas.de – Technische Tipps

Technische Informationen zu Linux, (Oracle-) Datenbanken und mehr

Owncloud mit nginx auf dem Raspberry PI: (4): Speedup für statische Dateien

| 4 Comments

owcloud: .js und .css Dateien schneller ausliefern

Für statische Dateien (.css und .js) wird bei Owncloud 4.0.7 immer php bemüht. Das ist gerade bei dem ‘langsamen’ Raspberry Pi sehr unvorteilhaft. Leider gilt dies auch, wenn die Dateien sich nicht geändert haben (not modified). Über einen kleinen Trick kann Owncloud auf dem Raspberry PI wesentlich beschleunigen.

Zugriffszeiten ohne Cache

.css - Datei ohne Optimierung (über php)

.css – Datei ohne Optimierung (über php)

Obwohl sich die Datei nicht geändert hat, dauert der Zugriff auf dem langsamen Raspberry PI mehrere Sekunden, weil der Aufruf über owncloud/?app=files_sharing&getfile=css/sharing.css erfolgt. Der Webserver (nginx oder Apache) kann die Datei nicht direkt ausliefern. Anders ist es z.B. bei den jquery.js-Dateien: diese werden direkt ausgeliefert.

Obwohl die Dateien ‘not modified’ sind, dauert der Zugriff viel zu lange!

Mit Optimierung

Wenn der Webserver (nginx) sich direkt um die Dateien ‘kümmern’ kann, werden sie auch rasend schnell geliefert (139ms).

Mit Optimierung (modified), also noch nicht im Browser-Cache

Mit Optimierung (modified), also noch nicht im Browser-Cache

Wenn die Dateien sich bereits im Browser-Cache befinden, geht es sogar noch schneller:

Mit Optimierung (not modified), Dateien bereits im Browser-Cache

Mit Optimierung (not modified), Dateien bereits im Browser-Cache

Die Funktion loadfile()

Die Funktion loadfile() verwaltet .css und .js – Dateien der Plugins unter apps/. Vor der Auslieferung an den Web-Client werden zwei Variablen (%webroot% und %appswebroot% mit str_replace in den Dateien ersetzt. Dann berechnet die Funktion einem md5() – Hash und liefert an den Web-Client. Wenn sich die Datei nicht geändert hat, ist das eigentlich unnötig und kostet nur Prozessorzeit.

# http://localhost/owncloud/?app=files_imageviewer&getfile=css%2Fjquery.fancybox-1.3.4.css
# => /var/www/owncloud/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css
#
# But lookout:
#owncloud/lib/base.php:
public static function loadfile(){
    if(file_exists(OC::$APPSROOT . '/apps/' . OC::$REQUESTEDAPP . '/' . OC::$REQUESTEDFILE)){
        if(substr(OC::$REQUESTEDFILE, -3) == 'css'){
            $appswebroot = (string) OC::$APPSWEBROOT;
            $webroot = (string) OC::$WEBROOT;
            $filepath = OC::$APPSROOT . '/apps/' . OC::$REQUESTEDAPP . '/' . OC::$REQUESTEDFILE;
            header('Content-Type: text/css');
            OC_Response::enableCaching();
            OC_Response::setLastModifiedHeader(filemtime($filepath));
            $cssfile = file_get_contents($filepath);
    ---> $cssfile = str_replace('%appswebroot%', $appswebroot, $cssfile);
    ---> $cssfile = str_replace('%webroot%', $webroot, $cssfile);
            OC_Response::setETagHeader(md5($cssfile));
            header('Content-Length: '.strlen($cssfile));
            echo $cssfile;
            exit;
      }elseif
            ...
}

 

Optimierung

Ersatz für loadfile()

Über ein Shell-Script (update_cache.sh) können wir die Ersetzung der Variablen einfach durchführen. Damit die Dateien von nginx nicht mehr komprimiert werden müssen, werden sie zusätzlich auch noch mit gzip komprimiert (nginx: gzip_static on;).

Das Script sollte zur Sicherheit einfach täglich (/etc/cron.daily) aufgerufen werden. Nach jeder Änderung an Owncloud (z.B. Installation von Add-Ons) sollte es erneut gestartet werden.

#!/bin/sh
#update_cache.sh
# call ist daily (/etc/cron.daily) and after modification
# of your owncloud-installation (new apps).
me=`basename $0`
fatal() {
	msg="$1"
	echo "$me:fatal:$msg"
	exit 1
}

webroot="/owncloud"
appswebroot="$webroot"
webdir="/var/www/$webroot"
wwwusr="www-data:www-data"

mycache="mycache"
cachedir="$webdir/$mycache"

[ -d "$webdir" ] || fatal "webdir not found: $webdir"
[ -d "$cachedir" ] || mkdir $cachedir || fatal "cannot create: $cachedir"

cd "$cachedir" || fatal "cannot cd to $cachedir"

( cd $webdir && find . -name \*.css ) | grep -v "\./$mycache" |
while read f
do
	d=`dirname "$f"`
	[ -d "$d" ] || mkdir -p "$d" || fatal "cannot create $d"
	echo "$f"
	sed -e"s#%webroot%#$webroot#g" -e "s#%appswebroot%#$appswebroot#g" < "$webdir/$f" > $f
	# Precompress for Mod_gzip
	gzip <$f > $f.gz
done

chown -R $wwwusr $cachedir

Beispiel

nachher: .fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_n.png', sizingMethod='scale'); }
---
vorher: .fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/owncloud/apps/files_imageviewer/img/fancy_shadow_nw.png', sizingMethod='scale'); }

 

nginx-Konfiguration

Das Shell-Script kopiert die Dateien nach /var/www/owncloud/mycache. Jetzt müssen wir nginx nur noch mitteilen, was er mit diesen Dateien machen soll: Möglichst schnell ausliefern!

Normale Dateien, die keine Modifikation genötigen, werden direkt ausgeliefert.

	# This block will catch static file requests, such as images, css, js
	# The ?: prefix is a 'non-capturing' mark, meaning we do not require
	# the pattern to be captured into $1 which should help improve performance
	#
	# MN: Added svg here
	#location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|js)$ {
	location ~* /owncloud/(?:apps/[^/]+|core)/(?:css|js|img|img/[^/]+)/[^/]+\.(?:ico|css|js|gif|jpe?g|png|svg|js)$ {
               # Some basic cache-control for static files to be sent to the browser 
               #expires max; 
               #add_header Pragma public; 
               #add_header Cache-Control "public, must-revalidate, proxy-revalidate"; 
               expires 7d; 
               add_header Pragma public; 
               add_header Cache-Control "public"; 
         }

 

location ~* /owncloud/mycache/ {
        # Some basic cache-control for static files to be sent to the browser
        expires 7d;
        add_header Pragma public;
        #add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        add_header Cache-Control "public";
}

Die Zugriffe auf getfile() biegen wir einfach um. Dazu sind ein paar Tricks mit Regular Expressions nötig. Hier kann man mit $1 auf den Wert in er ersten Klammer (), mit $2 auf den Wert in der 2. Klammer, usw. zugreifen.
Das Statement ist etwas ‘tricky’.

location = /owncloud/ {
	# hier nur requests auf /owncloud/ oder index.php
        # http://localhost/owncloud/?app=files_imageviewer&getfile=css%2Fjquery.fancybox-1.3.4.css
	if ($args ~ "app=([^&]+)&getfile=(css)%2F(.*\.css)$") {
		set $app   $1;
		set $file  $2/$3;
		# redict to ownclod/mycache
		#  .css (and .css.gz) must be copied there!
		# try_files not allowed here 
		rewrite ^/owncloud/.*$  /owncloud/mycache/apps/$app/$file last;
	}
}

Nebenwirkungen

Es gibt ein paar Nebenwirkungen:

  • Das Shell-Script update_cache.sh muss aufgerufen werden

Sicherheit

.css – Dateien sind nicht sicherheitskritisch. Man sollte sich aber davor hüten, andere Dateien. oder gar die User-Files von Owncloud über den Webserver zu cachen!

Das wäre dann ein riesiges Sicherheitsproblem, weil nicht (mehr) authentifizierte Benutzer Zugriff bekommen.

Also: Vorsicht;

Fazit

Wie man sehen kann, wird owncloud auf dem Raspberry Pi rapide beschleunigt. Meiner Meinung nach sollten sich die Entwickler von Owncloud noch einmal überlegen, ob man mit den .css und .js-Dateien nicht etwas schlauer umgehen kann.

4 Comments

  1. Hi,
    hast du diesen Tip schon den OwnCloud entwicklern mitgeteilt?
    Ich möchte mir einen RaspberryPi aufsetzten mit OwnCloud und das wäre es natürlich cool, wenn ich diese Änderung nicht selbst in die aktuelle Version neu implementieren müßte…

    Gruß
    Eugen

    • Richtig offiziell mitgeteilt habe ich denen das noch nicht.
      Ich will erst die aktuelle Version von Owncloud ausprobieren, vielleicht hat sich da ja etwas geändert.
      Aber: “Never change a running system” – es funktioniert bei mir gerade so gut.

  2. Hi, hab jetzt auch einen owncloud-server mit dem Raspi und nginx aufgesetzt.

    Ich bin zwar kein Linux-Newbie mehr aber bei einigen deiner Ounkte steh ich ganz schön auf dem Schlauch.

    wie kann ich den nginx anpassen (z.B. ssl, gzip, caches timeouts…) und wie bzw. wo muss ich die Änderungen die du in diesem Part zur Optimierung vorschlägst denn vornehmen?

    Da blick ich noch nicht ganz durch.. wäre schön, wenn du mir weiterhelfen könntest.

  3. hallo
    an welcher stelle sind denn die obigen scripts einzufügen?
    hast du schon owncloud 5 getestet?
    bei mir läuft owncloud 5 mit lighttpd mit php-apc aber ohne deine optimierung, da ich noch recht neu bei linux bin :-) und nichts verstehe ^^

Leave a Reply

Required fields are marked *.