A “like” gomb működése és a cookie-k

Nézzük a két részletet, amelyek az előző részből kimaradtak. Az első rész a számláló növeléséért felelős. Ezt egy sima adatbázis UPDATE végzi a DBI interfészen keresztül. Fontos, hogy a finish() metódussal felszabadítsuk az utasítást, különben “lógna” a levegőben azaz a memóriában. A cookie-zás (sütizés) az egy kliensből történő többszöri like-olás megakadályozását szolgálja és az üzenetfejlécben állítjuk illetve kérdezzük le, mégis teszt célokból jelen kódban ki van “ütve”.

 my $cookie = $r->headers_in->get('Cookie');
 my $uri = URI::Encode->new( { encode_reserved => 1 } );
 my $uri_encoded_referer = $uri->encode($http_referer);
 #print $uri_encoded_referer;
 my $wasliked = $cookie ne '' && $cookie =~ /$uri_encoded_referer/;
 if ($like && ! $wasliked ) {
  # increment counter
  my $sthupd = $dbh->prepare('UPDATE mylike SET counter = counter + 1 WHERE approved = 1 and referer = ?');
  $sthupd->execute($http_referer);
  $sthupd->finish();

  my $cookie = CGI::Cookie->new(-name  => 'mylike'.$http_referer,
                                -value => 'SET');
  $r->headers_out->set('Set-Cookie' => $cookie);
  $wasliked = 1;

  # !! TEST ONLY !! temporary turn off cookie-mechanism !! COMMMENT OUT FOLLOWING TWO LINES FOR USE !!
  $r->headers_out->set('Set-Cookie' => '');
  $wasliked = 0;
 }

A like változó értékadása az előző részben szerepel, a GET metódus révén átadott paraméterként kerül kiértékelésre. Az alábbi gomb onclick javascript eseményéből kerül meghívásra like=1 URI változóként.

my $req = CGI->new($r);
my $like = $req->param('like');

A második kimaradt részecske maga a “like” gomb megjelenítéséért felelős. Ha már like-oltuk az oldalt, akkor disabled a gomb, azaz nem lehet többször megnyomni. 🙂

 print "<input type=\"button\" onclick=\"\$('#mylike').load('http://localhost/perl/mylike-dbi.cgi?like=1');\" value=\"like\" ".($wasliked?'disabled ':'')."/>\n";

És akkor nézzük meg a kimenetet!

Ennyi! That’s all! 🙂

Megjegyzés: azért a felhasznált javascript miatt ügyelni kell arra, hogy a site-unk include-olja script forrásként a jquery lib-eket.

Az utolsó részben megnézzük, hogyan lehet letesztelni illetve a teherbírását megmérni a like modulunknak.

Mérés – CGI (bash/perl/C), FastCGI vagy mod_perl? Melyik a gyorsabb? :)

És akkor jöjjön az igazság órája! Nézzük meg, hogy az előző két fejezet kis webalkalmazásai közül melyek a leggyorsabbak. Természetesen egy komplex webalkalmazás mérése beszédesebb volna, de reményeim szerint 1000 mérésből a curl programcsomag segítségével egy egyszerű script segítségével is ki tudjuk deríteni, hogy mi a helyzet.

Kiindulás. A rendszer load-ja (leterheltsége) az utolsó 5 percben 1,19. Alacsony. Ez ahogy később kiderült, nem is válltozik. 🙂 És igen, a notebook 161 napja nem volt újraindítva. 🙂

gvamosi@gergo1:/var/www/perl$ uptime
 04:07:48 up 161 days,  5:02,  1 user,  load average: 1.68, 1.19, 1.02

Nézzük a mérést végző script-ünket!

#!/bin/bash

START=`date +%s.%N`
echo $START
for ((i=1;i<=1000;i++)); 
do 
	curl -s $1 2>&1> /dev/null
done
END=`date +%s.%N`
RUNTIME=$(echo "$END - $START" | /usr/bin/bc)
echo $END
echo $RUNTIME

Ez egy egyszerű script. Az időt méri, és a parancssorban kapott első paraméterre mint URL-re meghívja a curl parancs segítségével a webalkalmazásunkat.

MÉRÉS I. CGI, bash script. http://localhost/cgi-bin/counter.sh13,76 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.sh
1597372454.016087696
1597372467.780319906
13.764232210

MÉRÉS II. CGI, perl. http://localhost/cgi-bin/counter.pl16,75 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.pl
1597372699.883363458
1597372716.637874845
16.754511387

MÉRÉS III. CGI, natív C. http://localhost/cgi-bin/counter.cgi12,37 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.cgi
1597372832.246672103
1597372844.623081288
12.376409185

MÉRÉS IV. FastCGI, perl. http://localhost/cgi-bin/counter.pl.fcgi10,19 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/cgi-bin/counter.pl.fcgi
1597372942.444961716
1597372952.635298535
10.190336819

MÉRÉS V. mod_perl. http://localhost/perl/counter-mod_perl.cgi10,51 másodperc.

gvamosi@gergo1:~/Documents/http_meres$ ./1000times_curl.sh http://localhost/perl/counter-mod_perl.cgi
1597373028.237804238
1597373038.753424598
10.515620360

Látható, hogy bár igen egyszerű a webalkalmazás, csupán pár sor, a várható mérési eredményeket hozta. A leglassabb a sima perl CGI, gyorsabb a bash script illetve a natív C, míg a FastCGI illetve a mod_perl a leggyorsabbak. 🙂

És akkor egy (viszonylag régi) összehasonlító mérés a net-ről (https://stackoverflow.com/questions/382798/mod-perl-vs-mod-fastcgi). 🙂

cgi - 1200+ requests per minute
mod_perl - 6000+ requests per minute (ModPerl::PerlRun only)
fast_cgi - 6000+ requests per minute
mod_perl - 6000+ requests per minute (ModPerl::Registry)
servlets - 2438 requests per minute.

Megjegyzés: ez egy egyszerű tesztelési eljárás alapbeállításokkal (Apache) szerveroldalon, helyi gépen, 8GB memóriával, 4 magos processzorral és SSD háttértárral. Lehetne több szerverpéldányt indítani, párhuzamosítani, azaz egyszerre több kliensről tesztelni. Alapvetően idő és tárkorlátos minden számítástechnikai eljárás, szoftver, illetve program. Tehát CPU és memória/háttértár. A webes tesztekbe ezen kívül a hálózati sebesség is beleszámít(hat).

Egyszerű számláló FastCGI perl és mod_perl implementációban

A FastCGI abban különbözik a CGI-től, hogy amíg a CGI egy kérésre egy folyamatot hoz létre a szerveren, a FastCGI újbóli futásra képes, újbóli betöltődés nélkül, ezáltal több kérést tud kiszolgálni egységnyi idő alatt, tehát nagyobb teljesítményt lehet vele elérni. Részletek itt https://en.wikipedia.org/wiki/FastCGI.

Ha ilyet szeretnénk fejleszteni, először is installánunk kell az alapból nem telepített FastCGI Apache modul-t. Modul leírás itt https://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html. Ez az alábbi módon lehetséges.

root@gergo1:~# apt-get install libapache2-mod-fcgid

Majd nézzük a /etc/apache2/mods-enabled/fcgid.conf configurációs file tartlamát.

<IfModule mod_fcgid.c>
  FcgidConnectTimeout 20

  <IfModule mod_mime.c>
    AddHandler fcgid-script .fcgi
  </IfModule>
</IfModule>

Ezután hozzuk létre az első, perl nyelvű counter.pl.fcgi programocskát a cgi-bin könyvtárunkban.

#!/usr/bin/perl

use FCGI;

my $count = 0;
my $request = FCGI::Request();

while($request->Accept() >= 0) {
    print("Content-type: text/html\r\n\r\n", ++$count);
}

Látható, hogy az FCGI kérés többször lefuthat. Ha ezt az oldalt pl. hétszer frissítjük böngészőben a http://localhost/cgi-bin/counter.pl.fcgi cím alatt, akkor egy 7-es számot fog kiírni. 🙂

Megjegyzem, ha nincs az FCGI modul telepítve a perl-en belül, akkor a cpan paranccsal tudjuk ezt megtenni. A CPAN a perl csomagkezelője https://www.cpan.org/.

Most nézzük a mod_perl implementációt is! Először is szükségünk lesz az Apache modulra.

root@gergo1:~# apt-get -y install libapache2-mod-perl

Ezután nézzük meg a /etc/apache2/conf-available/mod_perl.conf konfigurációs állományt.

# create new
# for example, set PerlRun mode under the "/var/www/perl"
PerlSwitches -w
PerlSwitches -T

Alias /perl /var/www/perl
<Directory /var/www/perl>
    AddHandler perl-script .cgi .pl
    PerlResponseHandler ModPerl::PerlRun
    PerlOptions +ParseHeaders
    Options +ExecCGI
</Directory>

<Location /perl-status>
    SetHandler perl-script
    PerlResponseHandler Apache2::Status
    Require ip 127.0.0.1 10.0.0.0/24
</Location>

Majd az “élesítő” parancsokat.

root@gergo1:~# a2enconf mod_perl
Enabling conf mod_perl.
To activate the new configuration, you need to run:
  systemctl reload apache2
root@gergo1:~# systemctl reload apache2

Végül írjuk meg a perl programunkat counter-mod_perl.cgi néven.

#!/usr/bin/perl

use strict;
use warnings;

print "Content-type: text/html\n\n";

my $a = 0;
&number($a);

sub number {
    my($a) = @_;
    $a++;
    print "number \$a = $a";
}

Ez a kis programocska a http://localhost/perl/counter-mod_perl.cgi cím alatt annyit ír ki, hogy number $a = 1. 🙂

A dokumentáció szerint (https://perl.apache.org/start/index.html) egy pici változtatással 100x-os sebességnövekedést érhetünk el a script-nél (https://perl.apache.org/start/tips/registry.html). Ezt meg is fogjuk mérni a következő fejezetben! 🙂

Alias /perl /var/www/perl
<Directory /var/www/perl>
    AddHandler perl-script .cgi .pl
    # comment out PerlRun mode and add Registry mode like follows
    #PerlResponseHandler ModPerl::PerlRun
    PerlResponseHandler ModPerl::Registry
    PerlOptions +ParseHeaders
    Options +ExecCGI
</Directory>

A mod_perl implementáció esetén az Apache szerverünk a kódot a memóriában tartja, és ott “fordítja”, ezért gyorsabb.

Egyszerű CGI számláló bash, perl és C nyelveken

Először is engedélyezzük a cgi modul-t az Apache webszerverünkben. Ez pár sor Debian 10.5 alatt root-ként (rendszergazdaként).

root@gergo1:/etc/apache2/mods-enabled# ln -s ../mods-available/cgi.load
root@gergo1:/etc/apache2/mods-enabled# ls -l
..
lrwxrwxrwx 1 root root 26 Aug 12 02:24 cgi.load -> ../mods-available/cgi.load
root@gergo1:/etc/apache2/mods-enabled# service apache2 reload

root@gergo1:/etc/apache2/conf-enabled# ls -l
..
lrwxrwxrwx 1 root root 36 Aug 28  2017 serve-cgi-bin.conf -> ../conf-available/serve-cgi-bin.conf

Nézzük a serve-cgi-bin.conf konfigurációs file tartalmát!

<IfModule mod_alias.c>
        <IfModule mod_cgi.c>
                Define ENABLE_USR_LIB_CGI_BIN
        </IfModule>

        <IfModule mod_cgid.c>
                Define ENABLE_USR_LIB_CGI_BIN
        </IfModule>

        <IfDefine ENABLE_USR_LIB_CGI_BIN>
                ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
                <Directory "/usr/lib/cgi-bin">
                        AllowOverride None
                        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                        Require all granted
                </Directory>
        </IfDefine>
</IfModule>

Látható, hogy a /usr/lib/cgi-bin könyvtárban kell létrehozzuk a számlálóinkat bash, perl és C nyelveken. Nézzük az elsőt bash shell script nyelven!

#!/bin/bash

printf "Content-type: text/html\n\n"
let COUNTER++
printf "$COUNTER\n"

És akkor teszteljük le parancssorból, majd web-en!

gvamosi@gergo1:/usr/lib/cgi-bin$ chmod +x counter.sh 
gvamosi@gergo1:/usr/lib/cgi-bin$ ./counter.sh 
Content-type: text/html

1

Látható, hogy 1-et ír ki, ha parancssorban futtatjuk.

Ha megnézzük weben is a http://localhost/cgi-bin/counter.sh cím alatt, ugyanez az eredmény. Látható, hogy elég a Content-type beállítása a válasz fejlécében, majd kettő soremelés után bármit kiírathatunk. 🙂

A Content-type egyébként az ún. media type (régebben MIME type) értéket tartalmazza. Részletek: https://en.wikipedia.org/wiki/Media_type.

És akkor nézzük ugyanezt perl nyelven!

#!/usr/bin/perl

use strict;
use warnings;

print "Content-type: text/html\n\n";

my $a = 0;
&number();

sub number {
  $a++;
  print "number \$a = $a\n";
}

A parancs kimenete hasonlatos mind parancssorban, mind a http://localhost/cgi-bin/counter.pl webcím alatt.

gvamosi@gergo1:/usr/lib/cgi-bin$ chmod +x counter.pl 
gvamosi@gergo1:/usr/lib/cgi-bin$ ./counter.pl 
Content-type: text/html

number $a = 1

Végül nézzük meg C nyelven. Megjegyzem: a C nyelvű scriptet a gcc programcsomag segítségével le kell fordítanunk. 🙂

#include <stdio.h>

int i=0;

int incrementcount()
{

  i++;
  return i;
}

int main()
{
  printf("Content-type: text/html\n\n");
  printf("The current count is: ");
  printf("%d\n", incrementcount());
  return 0;
}

És akkor nézzük meg a fordítást majd a kimenetet. Ugyanez lesz látható a http://localhost/cgi-bin/counter.cgi webcím alatt is.

gvamosi@gergo1:/usr/lib/cgi-bin$ gcc counter.c -o counter.cgi
gvamosi@gergo1:/usr/lib/cgi-bin$ ls -l
total 32
-rw-r--r-- 1 gvamosi gvamosi   216 Aug 12 03:05 counter.c
-rwxr-xr-x 1 gvamosi gvamosi 16720 Aug 12 03:05 counter.cgi
-rwxr-xr-x 1 gvamosi gvamosi   158 Aug 12 02:47 counter.pl
-rwxr-xr-x 1 gvamosi gvamosi    84 Aug 12 02:37 counter.sh
gvamosi@gergo1:/usr/lib/cgi-bin$ ./counter.cgi 
Content-type: text/html

The current count is: 1

Bámulatos, nem? 🙂 Hogy mire lesz jó ez az egész három különböző nyelvű, méghozzá benne egy natív C implementáció is? A következő fejezetekben kiderül!

Megjegyzés: natívan, bár gyorsabb a végeredmény, azért nem fejlesztünk többnyire, mert rendkívül bonyoluttá válik összetett oldalak esetén a programkód. Továbbá elég egy pici hiba, és már is “vulnerable”, azaz sérülékeny lehet a weboldalunk, amit a hacker-ek ún. exploit-tal ki tudnak használni, azaz adott esetben fel is törthetik az oldalainkon keresztül a szervereinket.