FastCGI Server + Epoll for high performance CDN

Hi, to improve the service into Confiared I’m rewritting the CDN software.

We was with Nginx + Nginx FastCGI cache + PHP (to just proxy the reply). This solution lack fine cache control tuning, some bug due to Nginx cache.

Then I have rewritten the CDN as standalone FastCGI server, the cache is directly controlled by the server. If same url is already downloading, then the content is send from the partial downloaded content. I choose monothread to have great performance without thread coherency code (more simple to dev, more eficience if the code is very fast because if the code is very fast most of the time is consumed into thread management and data migration from a CPU to another CPU).

The code is specific, not flexible and generalist. I parse the protocols (DNS, FastCGI, …) on fly. That’s greatly improve the performance, reduce the memory. An internal page is served 3x more faster than a simple « Hello world » into PHP 7.4.

The future improvement are: better cache, cache some stuff where needed (DNS, …), use io_uring to improve file access and be 3x more fast than Nginx with static file, do profile to optimize the code. (And maybe do my own http server)

Apache vs lighttpd vs nginx

Bonjour, j’ai fait le teste d’apache, lighttpd et nginx sur un applicatif maison car la charge du dédié utilisant cet applicatif est importante et Apache commence à donner des signes de fatigue.

J’ai essayé de rester logique et de me rapprocher de la configuration que j’avais en production.

Les informations:

Server: AMD Phenom(tm) II X4 920 Processor, 12Go de ram ddr2 800MHz, gouvenor cpu performance, raid6 avec 8hdd, kernel 2.6.31-10
Client: Laptop Dell inspirons 1555, intel core2duo P8600, 4GB
Rewrite rules activé, gzip compression pour les fichiers js et css, mod expire, php fastcgi, redirection, alias, auth, status
Via le réseau j’ai vérifié les entêtes, la compression, le cache.. via l’extension webdevelopper de firefox
Les autres réglages sont par défaut sauf:

Nginx:

worker_processes 20;
worker_connections 1200;
partial log
use epoll;
output_buffers  16 32k;
gzip_buffers    16 16k;
access log off for: jpg|jpeg|gif|css|png|ico|js

Lighttpd:

server.max-fds = 5000
server.max-open-files = 1000
server.event-handler = « linux-sysepoll »
no log
« mod_rewrite »,
« mod_redirect »,
« mod_alias »,
« mod_auth »,
« mod_status »,
« mod_setenv »,
« mod_simple_vhost »,
« mod_compress »,
« mod_expire »
For cgi:
« max-procs » => 60,
« PHP_FCGI_CHILDREN » => « 10 »
« PHP_FCGI_MAX_REQUESTS » => « 10 »

Apache:

Worker

Pour nginx j’ai du utiliser php-fpm pour le background fast-cgi de php.

Voilà l’environnement gentoo utilisé:

Gentoo use flags:

www-servers/nginx-0.8.33  USE= »aio fastcgi ipv6 pcre ssl status zlib -addition -debug -flv -imap -perl -pop -random-index -realip -securelink -smtp -static-gzip -sub -webdav »
www-servers/lighttpd-1.4.26  USE= »fastcgi gdbm ipv6 pcre php ssl -bzip2 -doc -fam -ldap -lua -memcache -minimal -mysql -rrdtool -test -webdav -xattr »
www-servers/apache-2.2.14-r1  USE= »ssl static threads -debug -doc -ldap (-selinux) -suexec » APACHE2_MODULES= »actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias -asis -auth_digest -authn_dbd -cern_meta -charset_lite -dbd -dumpio -ident -imagemap -log_forensic -proxy -proxy_ajp -proxy_balancer -proxy_connect -proxy_ftp -proxy_http -substitute -version » APACHE2_MPMS= »-event -itk -peruser -prefork worker »

Et voilà les résultats:

Un fichier js avec plusieurs variante d’options, pour voir les fichiers compresser et statique, sauf dans le cas d’un proxy (quoi que ces serveurs n’ont pas été testé comme ça) il faut plus tenir compte de la partie réseau, surtout que le teste est fait via en réseau gigabyte, la bande passante de l’internaute et de votre serveur sera en général inférieure:

En requetes/secondes

Ensuite différente taille de fichier statique non compresser tel que des images:

En requêtes secondes

Et très important le php, interpréter via des rewrites rules ou en 404 not found, les temps de requêtes (les meilleurs sont les plus petits):

temps moyens des requêtes en ms

Le nombre de requêtes qui n’ont pas fonctionné (ayant affiché une page d’erreur au lieu de la page normale):

nombre de requêtes totales n’ayant pas fonctionné

Puis enfin le nombre que requêtes secondes, incluant celle n’ayant pas fonctionné (voir + haut):

nombre de requêtes secondes

Vu que nginx ayant dans ce cas énormément de requêtes n’ayant pas fonctionné, la vitesse ne lui sert à rien.

Conclusion

Apache est à la traîne sur tous les terrains.

Lighttpd: Il est très bon pour la compression de fichiers statiques, car il les met en cache. Le fam permet de ne pas aller voir la date de modification à chaque accès, mais il ne semble pas y avoir de gain. Pour les fichiers normaux il est un peu moins bon de nginx mais largement meilleur qu’apache. Il intègre tout ce qu’il faut pour la gestion et l’intégration de php via fast-cgi.

NginX: Il est très bon pour les fichiers statiques, semble avoir des problèmes à dé-servir les fichiers php avec php-fpm pour fast-cgi server (ou php-fpm qui ne suit pas), il ne profite pas d’un cache pour les fichiers statiques envoyés en compressé ce qui le rend un peu plus lent, par contre lors des benchmarks il a été le seul à tirer partie à 100% de mon cpu multi-coeur, et cela ce ressent dans les fichiers traditionnels et non compresser, tout est prévu en natif, donc pour les serveurs utilisant plein de fonctionnalité c’est très performant.

Attention, dans un cas optimale tout les images sont mis en cache côté navigateur via le mod expire, idem pour les css et php, donc si vos visiteurs reste longtemps et charge plein de pages d’un site, ou si vous avez plein de visites à une page la partie statique ou dynamique sera plus ou moins utilisé en fonction des cas.