Nginx + PHP5-fpm auf Debian/Ubuntu

Zurück zur „Webseiten beschleunigen“ – Übersicht

5.3) Nginx als Webserver


nginx_logo

Wer komplett auf nginx als Webserver umsteigen will kann dies auch sehr einfach bewerkstelligen. Ich persönlich nutzte diese Methode und es laufen mehrere WordPress-Webseiten, e107, DokuWiki u.s.w. tadellos mit diesem System.

Als erstes müssen wir unsere sources.list ein wenig erweitern… um php5-fpm direkt mit dem Projektmanager installieren zu können, wie dies Funktioniert habe ich bereits in einem anderen Beitrag beschreiben: php-5-FPM

Danach installieren wir nginx (Webserver) + PHP, wenn du noch apache2 oder einen anderen Webserver, welcher auf Port 80 lauscht installiert hast, musst du diesen nun deinstallieren bzw. erst einmal stoppen und ggf. einige libraries nachinstallieren, falls du Ubuntu und nicht Debian nutzt.


ggf. nur bei Ubuntu

wget http://us.archive.ubuntu.com/ubuntu/pool/main/k/krb5/libkrb53_1.6.dfsg.4~beta1-5ubuntu2_i386.deb
wget http://us.archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu38_3.8-6ubuntu0.2_i386.deb
sudo dpkg -i *.deb

ggf. root-Rechte

sudo bash

ggf. apache stoppen

/etc/init.d/apache2 stop
aptitude install nginx php5-fpm

Nun kommen wir zur Konfiguration, ich poste hier einfach mal meine komplette Konfig, daher sollte diese ohne Probleme lauffähig sein, wenn du die aktuelle Version installiert hast.

vim /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
worker_rlimit_nofile 3000;
pid /var/run/nginx.pid;

# [ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log;

#google_perftools_profiles /tmp/profile/;

events {
        worker_connections 2048;
        # use [ kqueue | rtsig | epoll | /dev/poll | select | poll ] ;
        use epoll;
        multi_accept on;
}

http {
        server_names_hash_bucket_size 64;

        ## Size Limits
        client_max_body_size        10M;
        client_header_buffer_size   32k;
        client_body_buffer_size     128k;
        large_client_header_buffers 64 8k;

        ## Timeouts
        client_body_timeout   3m;
        client_header_timeout 3m;
        send_timeout          3m;
        #expires              24h;
        keepalive_timeout     120 120;

        ## General Options
        ignore_invalid_headers   on;
        keepalive_requests       100;
        limit_zone normal $binary_remote_addr 5m;
        recursive_error_pages    on;
        sendfile                 on;
        server_name_in_redirect  off;
        server_names_hash_max_size 512;
        server_tokens            off;
        set_real_ip_from 127.0.0.1;
        real_ip_header X-Forwarded-For;

        ## TCP options
        #tcp_nopush on;
        tcp_nodelay off;
        #send_lowat 12000;

        include mime.types;
        default_type application/octet-stream;

        access_log /var/log/nginx/access.log;

        log_format main '$remote_addr - $remote_user [$time_local] $status '
        '\"$request\" $body_bytes_sent \"$http_referer\" '
        '\"$http_user_agent\" \"http_x_forwarded_for\"';

        ## Cache
        open_file_cache max=1000 inactive=24h;
        open_file_cache_valid    24h;
        open_file_cache_min_uses 2;
        open_file_cache_errors   on;

        ## Compression
        gzip              on;
        gzip_static       on;
        gzip_disable      "MSIE [1-6]\.";
        gzip_buffers      32 8k;
        gzip_comp_level   5;
        gzip_http_version 1.0;
        gzip_min_length   0;
        gzip_proxied      any;
        gzip_types text/plain text/css text/xml text/javascript application/json application/xml application/xml+rss application/x-javascript image/x-icon application/x-perl application/x-httpd-cgi;
        gzip_vary         on;

        output_buffers   10 128k;
        postpone_output  1500;

        # Beispiel: Loadbalance
        #upstream webbackend  {
                #ip_hash;
                #server web1.domain.lan weight=10 max_fails=3 fail_timeout=30s;
                #server web2.domain.lan weight=1 backup;
        #}

        # Beispiel: Reverse-Proxy
        #server {
                #access_log  /var/log/nginx/access.proxy.log main;
                #error_log   /var/log/nginx/error.proxy.log;
                #listen      80;
                #server_name _;

                ## PROXY - Web
                #location ~ \.php$ {
                        #proxy_cache            cache;
                        #proxy_pass  http://127.0.0.1:8080;
                #}
        #}

        # Beispiel: HTTP Server (sollte jedoch besser unter /etc/nginx/sites-available/ angelegt werden)
        #server {
                #listen       80;
                #listen       somename:80;
                #server_name  somename  alias  another.alias;

                #location / {
                        #root   html;
                        #index  index.html index.htm;
                        #}
                #}

        # Beispiel: HTTPS Server (sollte jedoch besser unter /etc/nginx/sites-available/ angelegt werden)
        #server {
                #listen       443;
                #server_name  localhost;

                #ssl                  on;
                #ssl_certificate      cert.pem;
                #ssl_certificate_key  cert.key;

                #ssl_session_timeout  5m;

                #ssl_protocols  SSLv2 SSLv3 TLSv1;
                #ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
                #ssl_prefer_server_ciphers   on;

                #location / {
                #root   html;
                #index  index.html index.htm;
                #}
        #}

#include /etc/nginx/proxy.conf;
include /etc/nginx/sites-enabled/*.conf;
}
vim /etc/nginx/proxy.conf
proxy_buffering            on;
proxy_cache_min_uses       3;
proxy_cache_path           /usr/local/nginx/proxy_cache/ levels=1:2 keys_zone=cache:10m inactive=24h max_size=1000M;
proxy_cache                cache;
proxy_cache_valid          200 24h;
proxy_cache_use_stale      error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_ignore_client_abort  off;
proxy_intercept_errors     on;
proxy_next_upstream        error timeout invalid_header;
proxy_redirect             off;
proxy_set_header           Host            $host;
proxy_set_header           X-Real-IP       $remote_addr;
proxy_set_header           X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffers              32 1M;
proxy_busy_buffers_size    31M;
proxy_temp_file_write_size 30M;
proxy_buffer_size          30M;
proxy_connect_timeout      90;
proxy_send_timeout         90;
proxy_read_timeout         90;
proxy_ignore_headers       Expires Cache-Control;

so nun richten wir unsere erste Webseite ein… (domain.de muss in den nachfolgenden Howto durch deine eigene Domain ersetzt werden und natürlich für jede neue Werbepräsenz eine neue Datei angelegt werden.

cd /etc/nginx/sites-available/
vim domain.de.conf
server {
        server_name domain.de www.domain.de *.domain.de;
        root /var/www/www.domian.de/web/;
        listen 80;
        index index.php;
        access_log /var/log/nginx/www.domian.de.access.log main;
        error_log /var/log/nginx/www.domian.de.error.log;

        if (-f $request_filename) {
                break;
        }

        location / {
                try_files $uri $uri/ @domian;
        }

        location ~* ^.+.(htm|html|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js)$ {
                access_log off;
                expires max;
                root /var/www/www.domian.de/web/;
        }

        location @domian {
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_param SCRIPT_FILENAME $document_root/index.php;
                include /etc/nginx/fastcgi_params;
                fastcgi_param SCRIPT_NAME /index.php;
        }

        location ~ \.php$ {
                try_files $uri @domian;
                fastcgi_index index.php;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include /etc/nginx/fastcgi_params;
        }

        location ~ /\.ht {
                deny  all;
        }
}
vim /etc/nginx/fastcgi_params
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

fastcgi_param  REDIRECT_STATUS    200;

nun müssen wir die Webseite nur noch aktivieren, indem wir die soeben erstellte Datei verlinken.

cd /etc/nginx/sites-enabled/
ln -s ../sites-available/domain.de.conf .
nginx -t
/etc/init.d/nginx restart

Und schon läuft unser neuer Webserver. :-) Um die neuste Version von Nginx auf Debian/Ubuntu zu installieren kann man ggf. noch folgende Quellen in die „sources.list“ eintragen.

echo "deb  http://ppa.launchpad.net/nginx/stable/ubuntu lucid main" >> /etc/apt/sources.list
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C
apt-get update
apt-get install nginx

… oder du verwendest die letzte Development-Version:

echo "deb  http://ppa.launchpad.net/nginx/development/ubuntu lucid main" >> /etc/apt/sources.list
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C
apt-get update
apt-get install nginx

Nun müssen wir PHP5-FPM noch ein wenig anpassen, so dass php-fpm mit den Rechten des jeweilligen Webs ausgeführt wird:

mkdir /etc/php5/fpm/pools/
vim /etc/php5/fpm/php5-fpm.conf
[...]
[global]
pid = /var/run/php5-fpm.pid
error_log = /var/log/php-fpm.log
log_level = notice
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 5s
daemonize = yes

;  pools defined in virtual hosts
include=/etc/php5/fpm/pools/*.conf
[..]

Nun leben wir für eine Webseite eine PHP-Konfiguration an:

vim /etc/php5/fpm/pools/domain.conf
[domain]

; one Port for one Website
listen = 127.0.0.1:11000

; uid/gid
user = domain_user
group = domain_group

; logging
request_slowlog_timeout = 5s
slowlog = /var/log/slowlog-domain.log

; Choose how the process manager will control the number of child processes.
pm.max_children = 10
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 500

; Pass environment variables
env[TMP] = /var/www/www.domain.de/phptmp
env[TMPDIR] = /var/www/www.domain.de/phptmp
env[TEMP] = /var/www/www.domain.de/phptmp

; inculde defaults
include = /etc/php5/fpm/common.conf

; host-specific php ini settings here
;php_admin_value[open_basedir] =
vim /etc/php5/fpm/common.conf
listen.backlog = -1
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.status_path = /status
ping.path = /ping
ping.response = pong
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[PATH] = /bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin
Jetzt läuft der PHP-fpm Prozess mit den Rechten des jeweiligen Users und mit eigenem Tmp-Verzeichnis, wer will kann php-fpm sogar in einer chroot-Umgebung ausführen lassen :-)
Links:

PHP 5.3.3

Auf Dotdeb.org gibt es PHP in Version 5.3.3 im .deb-Format. Das nachfolgende kleine HowTo zeigt wie du dies auf Debian installieren kannst. Es gibt Pakete für Debian (Lenny), sowohl für amd64 als auch i386 Architekturen.

Install:

1.) als erstes benötigen wir eine Root-Shell

sudo bash

2.) nun können wir die zusätzlichen Quellen einfügen

echo "deb http://php53.dotdeb.org stable all" >> /etc/apt/sources.list
echo "deb-src http://php53.dotdeb.org stable all" >> /etc/apt/sources.list

3.) diese einmal aktualisieren

apt-get update

4.) und schon können wir PHP 5.3 installieren

aptitude safe-upgrade

Migration von PHP 5.2.x zu PHP 5.3.x:
de.php.net/migration53

Falls man bereits mit Debian (Squeeze) arbeitet oder die Lenny-Backports in den Quellen eingefügt hat und daher das Paket libtool > 2.2 installiert hat, muss man eine ältere Version per dpkg installieren, da man ansonsten php5-dev (5.3.1) nicht installieren kann, damit man selber Erweiterungen für PHP kompilieren kann (z.B. eaccelerator). Es folgt ein kleines HowTo:

wget http://ftp.de.debian.org/debian/pool/main/libt/libtool/libtool_1.5.26-4_i386.deb
dpkg --force-all -i libtool_1.5.26-4_i386.deb
aptitude install build-essential php5-dev

APC + eAccelerator + XCache

PHP besitzt selbst keinen Cache, was dazu führt, dass ein Skript bei jedem Aufruf neu übersetzt werden muss. Um dem entgegenzuwirken, gibt es einige Erweiterungen, die diese Funktionalität nachrüsten, wie beispielsweise den eAccelerator den alternativen PHP Cache.

Da PHP selber noch keinen Cache besitzt, was zur Folge hat das die Skript immer und immer wieder neu übersetzt werden müssen, gibt es ein paar Erweiterungen, welche diese Funktion nachrüsten. Und schon sind wir beim Thema.

 

1.) APC (Alternative PHP Cache)

APC ist ein Open-Source-Zusatzmodul für PHP, das eine beschleunigte Ausführung von PHP-Programmen ermöglicht. Die Beschleunigung wird dadurch erreicht, dass der kompilierte PHP-Quelltext zwischengespeichert wird und bei wiederholter Ausführung das zeitaufwändige Kompilieren nahezu vollständig vermieden werden kann. – Wiki

Als erstes schauen wir uns unsere aktuelle php-Config an…

vim /var/www/info.php
< ?php
phpinfo();
?>
w3m deine_webseite.abc/info.php

Zudem prüfen wir eben noch die momentane Geschwindigkeit unserer Webseite… z.B. auf www.linkvendor.com und internetsupervision.com

Nun installieren wir APC:

aptitude install php-pear
aptitude install php5-dev apache2-prefork-dev build-essential
pecl install apc
vim /etc/php5/conf.d/apc.ini
extension="apc.so"
apc.enabled=1
apc.file_update_protection=2
apc.optimization=0
apc.shm_size=32
apc.shm_segments=1
apc.gc_ttl=7200
apc.ttl=7200
apc.num_files_hint=1024
apc.enable_cli=0

Die Größe an Dateien welche im Speicher (apc.shm_size) gehalten wird, kann man ganz nach Auslastung des Servers anpassen. Um sich anzeigen zu lassen, wie viel der momentane maximal Wert ist, kannst du folgenden Befehl ausführen. (max seg size (kbytes) = 32768 -> 32MB) Wer einen Root-Server hat kann unter (/etc/sysctl.conf) die folgenden Werte entsprechend anpassen: kernel.shmall, kernel.shmmax

ipcs -lm 
/etc/init.d/apache2 restart
Die komplette Dokumentation kann im PHP Manual gefunden werden: http://de2.php.net/manual/de/ref.apc.php

 

2.) eAccelerator

eAccelerator ist eine Open Source Software zum Einsatz auf Webservern, die als Beschleuniger, Optimierer und Cache für PHP-Seiten dient. – Wiki

Wir schauen uns wieder unsere aktuelle php-Config an…

vim /var/www/info.php
<?php phpinfo(); ?>
w3m deine_webseite.abc/info.php

Geschwindigkeit unserer Webseite prüfen… z.B. auf www.linkvendor.com und internetsupervision.com

Nun installieren wir eAccelerator:

aptitude install build-essential php5-dev git
cd /usr/src/
git clone https://github.com/eaccelerator/eaccelerator
cd eaccelerator/
phpize
./configure
make
make install
vim /etc/php5/conf.d/eaccelerator.ini
;[eaccelerator]
extension = "eaccelerator.so"

eaccelerator.enable = "1"
eaccelerator.shm_size = "0"
eaccelerator.optimizer = "1"
eaccelerator.debug = "0"
eaccelerator.cache_dir = "/tmp/eaccelerator"
eaccelerator.log_file = "/var/log/httpd/eaccelerator_log"
eaccelerator.check_mtime = "1"
eaccelerator.filter = ""
eaccelerator.shm_max = "0"
eaccelerator.shm_ttl = "0"
eaccelerator.shm_prune_period = "0"
eaccelerator.shm_only = "0"
;eaccelerator.compress = "1"
;eaccelerator.compress_level = "6"
;eaccelerator.keys = "shm"
;eaccelerator.sessions = "shm"
;eaccelerator.content = "shm"
;eaccelerator.allowed_admin_path = ""
mkdir -p /var/cache/eaccelerator
chmod 0777 /var/cache/eaccelerator
/etc/init.d/apache2  restart

Und noch einmal die Geschwindigkeit deiner Webseite prüfen… z.B. auf www.octagate.com und internetsupervision.com

Konfiguration von eAccelerator:
—————————————

eaccelerator.shm_size
Die Menge an Arbeitsspeicher (in Megabytes) welche eAccelerator verwenden darf.
Der Standard Wert ist „0“.

eaccelerator.cache_dir
Gibt das Verzeichnis an, wo eAccelerator die vorkompilierten Daten auf der Festplatte speichern darf. Die gleichen Daten können auch im Speicher oder auch nur im Speicher untergebracht werden, was einen schnelleren Zugriff zu folge hätte.
Der Standard Wert ist „/tmp/eaccelerator“.

eaccelerator.enable
Ein- / Ausschalten von eAccelerator. Wobei „1“ gleich „an“ bedeutet.
Der Standard Wert ist „1“.

eaccelerator.optimizer
Hier kannst du Ein- / Ausschalten ob der PHP-Code noch optimiert werden soll. Wobei „1“ gleich „an“ bedeutet.
Der Standard Wert ist „1“.

eaccelerator.debug
Hier kannst du das Debug-Logging Ein- / Ausschalten.
Der Standard Wert ist „0“.

eaccelerator.check_mtime
Ein- / Ausschalten für die Prüfung, ob der PHP-Code geändert wurde und somit doch noch mal neu übersetzt werden sollte. Wobei „1“ gleich „an“ bedeutet.
Der Standard Wert ist „1“.

eaccelerator.filter
Hier kannst du angeben welche Daten gecacht werden sollen. (z.B. „*.php *.phtml“) Wenn der Eintrag mit einem „!“ beginnt, bedeutet dies, das diese Daten nicht gecacht werden. Der Standard Wert ist „“. -> was bedeutet, dass alles gecacht wird.

eaccelerator.shm_max
Wenn sehr große Daten nicht in den Arbeitsspeicher wandern sollen, dann mann man diesen Wert setzten. (z.B. 10240, 10K, 1M).
Der Standard Wert ist „0“.

eaccelerator.shm_ttl
Wenn eaccelerator keinen Arbeitsspeicher mehr zur Verfügung hat, werden alle Daten gelöscht, welche älter sind als z.B. „9000“ Sekunden (Webseiten welche die letzten 2 1/2 Stunden nicht besucht wurden)
Der Standard Wert ist „0“.

eaccelerator.shm_prune_period
Wenn eaccelerator keinen Arbeitsspeicher mehr zur Verfügung hat, werden die Daten welche vor z.B. 9000 Sekunden gecacht wurden gelöscht.
Der Standard Wert ist „0“.

eaccelerator.shm_only
Ein- / Ausschalten vom Cache auf der Festplatte.
Der Standard Wert ist „0“. -> Was bedeutet das sowohl im Arbeitsspeicher als auch auf der Festplatte gecacht wird.

eaccelerator.compress
Ein- / Ausschalten der Komprimierung der Daten.
Der Standard Wert ist „1“.

eaccelerator.compress_level
Stellt den Grad der Komprimierung ein.
Der Standard Wert ist „9“. -> Max. Komprimierung

 

3.) XCache

Opcode-Cacher zum PHP-Beschleunigen auf Servern, beschleunigt den Prozess des Kompilierens von PHP-Skripten, indem er den kompilierten Zustand von PHP-Skripten im RAM zwischenspeichert und die kompilierte Version direkt aus dem Hauptspeicher nutzt.

aptitude install php5-xcache

Hier noch meine Konfiguration für das WordPress Plugin „W3 Total Cache“ …

vim /etc/php5/conf.d/xcache.ini
; configuration for php xcache module
[xcache-common]
extension=xcache.so

[xcache.admin]
xcache.admin.enable_auth = Off
;xcache.admin.user = "admin"
;xcache.admin.pass = ""

[xcache]
; select low level shm/allocator scheme implemenation
xcache.shm_scheme = "mmap"
; to disable: xcache.size=0 (Standard)
; to enable : xcache.size=64M etc (any size > 0) and your system mmap allows
xcache.size = 64M
; set to cpu count (cat /proc/cpuinfo |grep -c processor)
xcache.count = 1
; just a hash hints, you can always store count(items) > slots
xcache.slots = 8K
; ttl of the cache item, 0=forever
xcache.ttl = 0
; interval of gc scanning expired items, 0=no scan, other values is in seconds
xcache.gc_interval = 0

; same as aboves but for variable cache
xcache.var_size  = 64M
xcache.var_count = 1
xcache.var_slots = 8K
; default ttl
xcache.var_ttl = 0
xcache.var_maxttl = 0
xcache.var_gc_interval = 900

xcache.test = Off
; N/A for /dev/zero
xcache.readonly_protection = Off
; for *nix, xcache.mmap_path is a file path, not directory.
; Use something like "/tmp/xcache" if you want to turn on ReadonlyProtection
; 2 group of php won't share the same /tmp/xcache
; for win32, xcache.mmap_path=anonymous map name, not file path
;
; xcache.mmap_path = "/dev/zero"

; leave it blank(disabled) or "/tmp/phpcore/"
; make sure it's writable by php (without checking open_basedir)
;
; xcache.coredump_directory = ""

; per request settings
xcache.cacher = On
xcache.stat = Off
xcache.optimizer = On

[xcache.coverager]
; per request settings
; enable coverage data collecting for xcache.coveragedump_directory and xcache_coverager_start/stop/get/clean() functions (will hurt executing performance)
;
;xcache.coverager = Off

; ini only settings
; make sure it's readable (care open_basedir) by coverage viewer script
; requires xcache.coverager=On
;
;xcache.coveragedump_directory = ""

weiter Infos zu xCache: xcache.lighttpd.net/wiki/XcacheIni

 

Test-Ergebnisse:

Mit folgendem Befehl habe ich die Geschwindigkeit meiner Webseite getestet, wobei ich in der Zwischenzeit, vom apache2 als Webserver Abstand genommen habe und voll auf „nginx“ setzte. :-)

ab -n500 -c20 -dS "http://suckup.de/"
Somit wurden 500 Seitenaufrufe, davon 20 parallel ausgeführt, um einen Durchschnittswert zu erhalten habe ich den Befehl 3x ausgeführt und xCache war ein wenig schneller!!! :-)

PHP optimieren

Zurück zur „Webseiten beschleunigen“ – Übersicht

6.) PHP optimieren/caching

 

6.1) PHP-Daten zwischenspeichern

Im folgenden Blog-Eintag habe ich bereits den Vorteil dargestellt, wenn man php-Daten zwischenspeichert.

-> APC + eAccelerator + XCache <-

 

6.2) SQL-Abfrage mittels PHP zwischenspeichern

Man kann auch verschiedene Caching-Methoden miteinander kombinieren, so habe ich z.B. Zeitweise auf dieser Webseite sowohl eAccelerator als auch „memcached + php5-memcache“ eingesetzt, jedoch war der Speicherverbrauch nicht für einen kleinen V-Server ausgelegt.

Distributed memory caching system, was bedeutet, dass als Speichersystem auch mehrere Rechner verwendet werden können, ursprünglich wurde memcache für Livejournal entwickelt.

Um mittels memcachd Daten direkt in den Arbeitsspeicher auszulagern, muss man als ersteres folgende Pakete installieren. – Debian/Ubuntu HowTo

sudo bash
aptitude install memcached
aptitude install php5-memcache

hier meine Konfigurationsdatei vom MemCache-Daemon

cat /etc/memcached.conf

## Daemon
-d

## das Logfile
logfile /var/log/memcached.log

## Ausgaben ins Log schreiben
# -v

## mehr Ausgaben ins Log schreiben
# -vv

## zur Verfügung stehende Arbeitsspeicher
-m 50

# Port
-p 11211

## User-Rechte
-u www-data

## Interface/IP
-l 127.0.0.1

/etc/init.d/apache2  restart

Nun können wir bereits Daten direkt von PHP in den Speicher schieben.

 

Beispiel:

Im folgenden Beispiel werden die Ergebnisse einer SQL-Abfrage direkt in den Speicher geladen

<?php
$mc = memcache_pconnect('127.0.0.1', 11211);
$query = "SELECT n.*, u.user_id, u.user_name,[...]
$ergebnis = memcache_get($mc, $id);
if(!$ergebnis) {
$sql->db_Select_gen($query);
$newsList = $sql->db_getList();
$ttl = 3600;
memcache_set($mc, $id, $newsList, MEMCACHE_COMPRESSED, $ttl);
}
foreach($newsList as $row)
{ [.........]
?>

 

Links:
memcached.org
php.net/memcache

 

6.3) PHP-Module deaktivieren

Ggf. kann man auch noch einige PHP-Erweiterungen abschalten, um den Speicherverbrauch von PHP zu verringern und dies somit zu beschleunigen.

ls /etc/php5/conf.d/

meine aktuell aktivierten Erweiterungen:

  • curl.ini
  • gd.ini
  • mcrypt.ini
  • mysql.ini
  • mysqli.ini
  • suhosin.ini
  • xcache.ini
Man sollte natürlich beachten, welche PHP-Anwendungen man auf seiner Webseite installiert hat bzw. möchte und dessen Abhängigkeiten.

 

6.4) php.ini (Konfiguration) optimieren

Als erstes müssen wir klären, welche Datei geändert werden muss, um die Konfiguration von PHP wirklich zu ändern, dafür nutzen wir am einfachsten eine php_info.php – Datei, welche im Webverzeichnis abgelegt wird und direkt über den Browser aufgerufen wird.

vim php_info.php
<?php
phpinfo();
?>

Bei mir wird angezeigt, dass die Konfigurationsdatei an folgender Stelle editiert werden muss.

vim /etc/php5/apache2/php.ini

 

  • register_argc_argv = Off

 

Diese Einstellung ist nur nützlich, wenn man PHP über die Kommandozeile aufruft und dort Parameter mitgeben möchte, ansonsten aus.

 

  • always_populate_raw_post_data = Off

 

Da ich diese Funktion nicht nutze hab ich diese deaktiviert, sollte man einfach mal ausprobieren…

 

… die meisten Einstellung an PHP muss man jedoch, genauso wie die meisten Einstellungen an eine bestimmte Situation anpassen!

Browser-Cache nutzen

Zurück zur „Webseiten beschleunigen“ – Übersicht

4.) Browser-Cache nutzen

4.1) Browser-Cache manuell einstellen

Falls du keinen Zugriff auf die Konfiguration von Apache hast, kannst du den Browser-Cache folgendermaßen beeinflussen. Nun wollen wir unseren JS/CSS Dateien noch einen Header mitgeben, welcher wiederum bewirkt, dass der entsprechende Browser (Client) die Datei für eine gewisse Zeit speichert und für „neu“ hält, dies spart wieder Traffic, welchen wir nicht bezahlen müssen und außerdem beschleunigt es den Seitenaufruf.


CSS in PHP umbenennen, um den php-Code in der Datei ausführen zu können.

mv test.css test.css.php

Anschließend werden wir am Anfang der Datei folgendes mit einfügen.

<?php
require_once("gzipCSS.php");
?>

Im folgenden Beispiel wird die Datei für 3 Tage (60 * 60 * 24 * 3) gespeichert.

vim gzipCSS.php
<?php
ob_start ("ob_gzhandler");
header("Content-type: text/css; charset: UTF-8");
header("Cache-Control: must-revalidate");
$offset = 60 * 60 * 24 * 3;
$ExpStr = "Expires: " .
gmdate("D, d M Y H:i:s",
time() + $offset) . " GMT";
header($ExpStr);
?>


JS in PHP umbenennen, um den php-Code in der Datei ausführen zu können.

mv test.js test.js.php

Anschließend werden wir am Anfang der Datei folgendes mit einfügen.

<?php
ob_start ("ob_gzhandler");
header("Content-type: text/javascript; charset: UTF-8");
header("Cache-Control: must-revalidate");
$offset = 60 * 60 * 24 * 3;
$ExpStr = "Expires: " .
gmdate("D, d M Y H:i:s",
time() + $offset) . " GMT";
header($ExpStr);
?>


Falls man die Komprimierung jedoch bereits serverseitig aktiviert hat, hatte ich das Problem, dass die Webseite nicht zu-ende geladen werden konnte, daher würde ich empfehlen auf diese Methode zu verzichten, sofern man Zugriff auf das System hat und die Einstellungen am Webserver selber einstellen kann, zumal es relativ lange dauern dürfte, bis man alle Dateien auf diese weiße optimiert hat!


Link:
www.php.net/ob_gzhandler
www.php.net/zlib


4.2) Browser-Cache serverseitig einstellen


Expires – in diesen Header-Eintrag wird, für jede Server-Antwort festgelegt, wie lange diese gültig ist. Um jedoch erst-einmal zu verstehen, was in diesem Header steht folgt ein kleines Beispiel in

PHP:

function setExpires($expires) {
header(
'Expires: '.gmdate('D, d M Y H:i:s', time()+$expires).'GMT');
}
setExpires(10);
echo ( 'This page will self destruct in 10 seconds<br />' );
echo ( 'The GMT is now '.gmdate('H:i:s').'<br />' );
echo ( '<a href="'.$_SERVER['PHP_SELF'].'">View Again</a><br />' );

nach 10 Sekunden ist diese Webseite alt und muss vom Browser erneut geladen werden.


weitere Beispiele:
www.sitepoint.com


Kommen wir nun zurück zur Konfiguration von Apache, um „expires“ zu aktivieren führen wir unter Linux Debian/Ubuntu folgendes Kommando aus.

a2enmod expires
a2enmod headers


Nun passen wir die entsprechende Konfiguration an, um bestimmte Dateien für eine gewisse Zeit clientseitig zu speichern.

vim /etc/apache2/mods-available/expires.conf
<IfModule mod_expires.c>

AddType image/vnd.microsoft.icon .ico
AddType application/javascript .js
AddType application/x-tar .tgz
AddType text/plain .c
AddType text/plain .h

ExpiresActive On
ExpiresDefault "access plus 4 houres"

ExpiresByType image/vnd.microsoft.icon "access plus 3 months"
ExpiresByType image/ico "access plus 3 month"
ExpiresByType image/x-icon "access plus 3 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/js "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
ExpiresByType video/x-flv "access plus 1 month"
ExpiresByType video/quicktime "access plus 1 months"
ExpiresByType video/mp4 "access plus 1 months"
ExpiresByType video/mpeg "access plus 1 months"
ExpiresByType video/x-ms-wmv "access plus 1 months"
ExpiresByType audio/mpeg "access plus 1 months"
ExpiresByType audio/y-wav "access plus 1 months"
ExpiresByType application/pdf "access plus 2 weeks"
ExpiresByType application/zip "access plus 2 weeks"
ExpiresByType application/x-tar "access plus 2 weeks"
ExpiresByType application/x-download "access plus 2 weeks"
ExpiresByType application/ps "access plus 2 weeks"
ExpiresByType application/msword "access plus 2 weeks"
ExpiresByType text/css "access plus 2 weeks"
ExpiresByType application/xml "access plus 24 houres"
ExpiresByType application/xhtml+xml "access plus 24 houres"
ExpiresByType text/html "access plus 12 houres"
ExpiresByType text/htm "access plus 12 houres"
ExpiresByType text/plain "access plus 12 hours"
ExpiresByType text/xml "access plus 12 hours"

<IfModule mod_headers.c>

<FilesMatch "\\.(ico)$">
Header set Cache-Control "max-age=29030400, public"
</FilesMatch>

<FilesMatch "\\.(css|pdf|flv|jpg|jpeg|png|gif|swf)$">
Header set Cache-Control "max-age=2419200, public"
</FilesMatch>

<FilesMatch "\\.(js)$">
Header set Cache-Control "max-age=216000, private"
</FilesMatch>

<FilesMatch "\\.(xml|txt)$">
Header set Cache-Control "max-age=216000, public, must-revalidate"
</FilesMatch>

<FilesMatch "\\.(html|htm)$">
Header set Cache-Control "max-age=300, private, must-revalidate"
</FilesMatch>

<FilesMatch "\.(pl|php|cgi|spl|scgi|fcgi)$">
ExpiresActive Off
Header unset Expires
Header unset Last-Modified
Header set Pragma "no-cache"
Header set Cache-Control "private, no-cache, no-store, proxy-revalidate, no-transform"
</FilesMatch>
</IfModule>

FileETag none

</IfModule>


… und den Apache-Webserver neu-starten, um die neue Konfiguration zu übernehmen.

/etc/init.d/apache2 restart


Nun werden bestimmte Daten z.B. Bilder erst nach einem Monat wieder von der Webseite geholt, solange diese noch im Cache des Browsers sind und nicht manuell neu angefragt werden (F5).


ohne Expires:

no_cache_expires


mit Expires:

cache_expires

Die Grafik sind ebenfalls von YSlow, die Erweiterung, welche ich am Anfang des Artikels bereits beschrieben habe. Im ersten Bild siehst du die Ausgabe, wenn ein neuer Besucher (leerer Browsercache) meine Seite besucht und im zweiten, wenn z. B. ein Stammbesucher die Website aufruft, somit lässt sich gut erkennen wie viel Traffic it dieser Methode bereits bei einem Seitenaufruf eingespart werden kann, rechnet das mal für einen Monat hoch. ^^


Syntax:

ExpiresByType <TYPE> „<BASIS> [ plus ] <ANZAHL> <TYP>“

  • <TYPE> hier wird der Datei-Type definiert
  • <BASIS>

– modification ist das Änderungsdatum

– access oder now stehen für den Zugriffszeitpunkt

 

Das Schlüsselwort „plus“ ist optional. Es verdeutlicht die Tatsache, dass die nachfolgende Zeitangabe zum Basiszeitpunkt addiert wird


  • <ANZAHL> ist eine beliebige positive ganze Zahl
  • <TYP> können folgende Angaben sein

– year/years (Jahre)
– month/months (Monate)
– week/weeks (Wochen)
– day/days (Tage)
– hour/hours (Stunden)
– minute/minutes (Minuten)
– second/seconds (Sekunden)

 

Link:
www.askapache.com

Webseiten beschleunigen – Übersicht

Der Artikel beschreibt, wie man seine Webseite bzw. seinen Server analysiert und optimiert um Performance zu gewinnen, Ladezeit zu reduzierten bzw. Traffic einzusparen. Man kann einiges an Performance gewinnen, indem man z.B. Bilder im richtigen Format abspeichert bzw. komprimiert, CSS- / JS-Dateien kombiniert und ebenfalls komprimiert oder auch bestimmt Daten vorkomprimiert zur Verfügung stellt.

1.) Webseiten Analyse
1.1) Webseiten Benchmark

2.) Komprimierung
2.1) Bilder komprimieren
2.1.1) Bilder Sprites
2.1.2) PNG-Bilder komprimieren
2.1.3) JPEG-Bilder komprimieren
2.1.4) Bilder komprimieren – Online
2.2) JavaScript/CSS komprimieren
2.2.1) JavaScript/CSS komprimieren – serverseitig
2.2.2) JavaScript/CSS komprimieren – Online
2.3) Apache gzip-Kompression
2.3.1) Aktiviere mod_deflate (gzip) – serverseitig
2.3.2) Aktiviere mod_gzip – serverseitig
2.3.3) Aktiviere gzip – manuell

3.) JavaScript richtig im HTML-Code platzieren

4.) Browser-Cache nutzen
4.1) Browser-Cache manuell einstellen
4.2) Browser-Cache serverseitig einstellen

5.) Webserver beschleunigen
5.1) Apache-Module deaktivieren
5.2) Reverse Proxy (Nginx)
5.3) Nginx als Webserver
5.4) Nginx mit Varnish (Proxy)

6.) PHP optimieren/caching
6.1) PHP-Daten zwischenspeichern
6.2) SQL-Abfrage mittels PHP zwischenspeichern
6.3) PHP-Module deaktivieren
6.4) php.ini (Konfiguration) optimieren

7.) MySQL optimieren
7.1) MariaDB (MySQL-Fork)


Zusammenfassung

Abschließend möchte ich diesen Thema noch einmal kurz zusammenfassen, so dass man ggf. selber weitere Möglichkeiten erkennt, die Ladegeschwindigkeit seiner Webseite zu verbessern, jedoch nicht auf die Technische Umsetzung eingehen.

Kenne deinen Feind !!!

80 bis 90% der schlechten Performance liegt an der Webseite selber und nicht auf der Serverseite, solange nur wenig Besucher auf deiner Webseite sind, braucht man sich eigentlich um einen Revere-Proxy oder eine alternative zu Apache keine oder zumindest nur wenig Gedanken machen, da die Performance erst mit steigender Besucherzahl einbricht. Jedoch sollte man in jedem Fall die gzip-Komprimierung und den Browser-Cache nutzen.

Externe Dateien (Bilder, JS, CSS, Videos…)

Jeder Aufruf einer Datei kostet Performance und Bandbreite, daher sollte man im allgemeinen weniger Requests produzieren, indem man z.B. JS- bzw. CSS-Dateien kombiniert. Dieser negative Einwirkung steigt dramatisch bei externen Dateien von andern Servern und besonders dann, wenn dieser gerade mal nicht erreichbar ist.

Scripte

Immer wenn eine Browser eine Webseite abruft und im HTML-Code ein Script finde, wird das weitere laden der Webseite eingestellt und erst-einmal die JavaScript Engine damit beansprucht, diesen zu interpretieren. Daher sollte man diese Dateien erst am Ende der Webseite laden lassen, so dass der User die Webseite früher sehen kann, auch wenn z.B. die Animation des Menüs erst nach nachgeladen werden muss. Mit diesem Ansatz, erscheint es nur Sinnvoll CSS-Daten am Anfang laden zu lassen, so dass sich die Webseite dem User direkt im korrektem Layout präsentiert.

Bilder

Ein weites großer Bereich sind Bilder, dabei muss man jedoch ein gutes Mittelmaß an Text und Bildern finden, da die User zwar Bilder immer schön finden.. Logo, Hintergrundbild u.s.w. jedoch macht es nur wenig Sinn seine selbst jeder kleine Datei zu optimieren, wenn man oben über der Webseite ein 300 KB großes Bild eingefügt hat. Wie bereit im Artikel beschrieben gibt es jedoch zahlreiche Methoden (PNG- und JPEG-Dateien) diese zu optimieren.

Yahoo hat zudem eine sehr umfassende Liste von weiteren Regeln zusammengestellt, welche man beachten sollte.

Twitter automatisieren!

Auf folgender Webseite (www.catswhocode.com) habe ich ein paar sehr interessante PHP-Skript gefunden, eines davon stelle ich hier kurz vor.

Das Skript läuft „out of the box“, dass heißt es ist nicht notwendig, dass man irgendwas von dem folgenden PHP-Code versteht ^^ alles was du tun musst ist, die Variablen einzusetzen -> $user, $pass und $term.

$user = "Benutzernamen";
$pass = "Passwort";
$term = "Suchbegriff";

Hinter $term steht der Begriff für die Suche in der User-Timeline, wer diesen Begriff erwähnt, wird automatisch von dir verfolgt…

twitter_neu

< ?php
// Twitter Auto-follow Script by Dave Stevens - http://davestevens.co.uk

$user = "enter_your_twitter_username_here";
$pass = "enter_your_twitter_password_here";

$term = "enter_the_search_term_to_follow_here";

$userApiUrl = "http://twitter.com/statuses/friends.json";

$ch = curl_init($userApiUrl);
curl_setopt($ch, CURLOPT_USERPWD, $user.":".$pass);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$apiresponse = curl_exec($ch);
curl_close($ch);
$followed = array();

if ($apiresponse) {
 $json = json_decode($apiresponse);
 if ($json != null) {
  foreach ($json as $u) {
   $followed[] = $u->name;
  }
 }
}

$userApiUrl = "http://search.twitter.com/search.json?q=" . $term . "&rpp=100";
$ch = curl_init($userApiUrl);
curl_setopt($ch, CURLOPT_USERPWD, $user.":".$pass);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$apiresponse = curl_exec($ch);
curl_close($ch);

if ($apiresponse) {
 $results = json_decode($apiresponse);
 $count = 20;
 if ($results != null) {
  $resultsArr = $results->results;
  if (is_array($resultsArr)) {
   foreach ($resultsArr as $result) {
    $from_user = $result->from_user;
    if (!in_array($from_user,$followed)) {
     $ch = curl_init("http://twitter.com/friendships/create/" . $from_user . ".json");
     curl_setopt($ch, CURLOPT_USERPWD, $user.":".$pass);
     curl_setopt($ch, CURLOPT_POST, 1);
     curl_setopt($ch, CURLOPT_POSTFIELDS,"follow=true");
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     $apiresponse = curl_exec($ch);

     if ($apiresponse) {
      $response = json_decode($apiresponse);
      if ($response != null) {
       if (property_exists($response,"following")) {
        if ($response->following === true) {
         echo "Now following " . $response->screen_name . "n";
        } else {
         echo "Couldn't follow " . $response->screen_name . "n";
        }
       } else {
        echo "Follow limit exceeded, skipped " . $from_user . "n";
       }
      }
     }
     curl_close($ch);
    } else {
     echo "Already following " . $from_user . "n";
    }
   }
  }
 }
}
?>

ggf. kann man sich auch noch ein crontab einrichten, welcher dieses Skript ausführt

gravatar – dein global verfügbarer Avatar

Bei einem Gravatar handelt es sich um einen global verfügbaren Avatar (Global Recognized Avatar), welcher mit der E-Mail-Adresse des Benutzers verknüpft ist. Verschiedene Seiten bieten den Nutzern an, Avatare auf ihre Server zu laden und mit ihren E-Mail-Adressen zu verknüpfen. So kann ein Benutzer des Internets in jedem Blog oder anderen System seinen globalen Avatar hinterlassen, ohne sich extra bei jedem Blog zu registrieren und dann ein Bild hochzuladen. – Wiki

Das einbinden auf deine Webseite ist sehr einfach:

en.gravatar.com/site/implement

z.B.: für PHP

$email = "someone@somewhere.com";
$default = "http://www.somewhere.com/homestar.jpg";
$size = 40;

$grav_url = "http://www.gravatar.com/avatar.php?
gravatar_id=".md5( strtolower($email) ).
"&default=".urlencode($default).
"&size=".$size;

für WordPress gibt es eine Reihe von Plugins welche gravatar mehr oder weniger in deinen Blog integrieren…