Discussion:
Perl cgi avec Apache2 (Wheezy), le script garde un état persistent
(trop ancien pour répondre)
Francois Lafont
2014-04-06 01:01:18 UTC
Permalink
Bonjour à tous,

À vrai dire, je ne sais pas trop dire si mon problème est lié à Perl
ou bien à Apache2. Du coup, je poste sur 2 groupes en même temps. Sur
une Debian Wheezy, j'ai installé Apache2 et Perl CGI avec :

apt-get install apache2 libapache2-mod-perl2

Ensuite, j'ai configuré le site "default" comme ceci :

----------------------------------------------------------
<VirtualHost *:80>
ServerAdmin ***@localhost

DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions -ParseHeaders
Options +ExecCGI -MultiViews -SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn

CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
----------------------------------------------------------

Après, j'ai mis le script test.pl ci-dessous dans
/usr/lib/cgi-bin/ :

----------------------------------------------------------
#!/usr/bin/perl

use strict;
use warnings;
use share::module;
use 5.010;
use CGI;

my $q = CGI->new;
print $q->header();

foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}

# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;

foreach my $key (keys %share::module::hash) {
say "AFTER: $key --> [$share::module::hash{$key}]";
}
----------------------------------------------------------

et j'ai mis le fichier module.pm dans /etc/apache2/share/.
Voici son contenu :

----------------------------------------------------------
package share::module;

use strict;
use warnings;
use 5.010;

our %hash = ( a => 1,
b => 2,
c => 3,
);

1
----------------------------------------------------------

Puis j'ai redémarré apache2. Ensuite, je teste mon script Perl
avec curl :

$ curl http://localhost/cgi-bin/test.pl
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]

Ça correspond donc à ce que j'attendais (j'appelle cette
sortie la sortie 1). Mais si je relance plusieurs fois la
commande, je finis (au bout de 3 ou 4 exécutions de curl)
par avoir ça :

$ curl http://localhost/cgi-bin/test.pl
BEFORE: e --> [5]
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
BEFORE: d --> [4]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]

Et une fois que j'ai ça (j'appelle cette sortie la sortie 2),
j'ai l'impression que ça ne bouge plus j'ai toujours cette sortie
2. Si je redémarre apache2, je me retrouve avec la sortie 1 à
nouveau mais au bout de 3 ou 4 curl, je retombe sur la sortie 2
etc. etc.

Pourquoi je finis par avoir cette sortie 2 ? Elle signifie
qu'au moment même où le script perl s'exécute, j'ai déjà
%share::module::hash qui est mis à jour alors qu'en principe
la première boucle "for" du script test.pl arrive *avant*
la mise à jour de %share::module::hash. Du coup je ne comprends
pas trop.

Si jamais je me débarrasse du module "share::module" et que
je mets directement le hash dans le script test.pl, je ne
constate pas ce phénomène.

Est-ce possible de faire en sorte que l'état du script test.pl
« reparte à zéro » à chaque requête (via curl dans mon cas).

Merci d'avance pour votre aide.
--
François Lafont
Jean-Louis Morel
2014-04-08 18:33:11 UTC
Permalink
Post by Francois Lafont
Bonjour à tous,
À vrai dire, je ne sais pas trop dire si mon problème est lié à Perl
ou bien à Apache2. Du coup, je poste sur 2 groupes en même temps. Sur
apt-get install apache2 libapache2-mod-perl2
----------------------------------------------------------
<VirtualHost *:80>
DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions -ParseHeaders
Options +ExecCGI -MultiViews -SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
----------------------------------------------------------
Après, j'ai mis le script test.pl ci-dessous dans
----------------------------------------------------------
#!/usr/bin/perl
use strict;
use warnings;
use share::module;
use 5.010;
use CGI;
my $q = CGI->new;
print $q->header();
foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}
# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;
foreach my $key (keys %share::module::hash) {
say "AFTER: $key --> [$share::module::hash{$key}]";
}
----------------------------------------------------------
et j'ai mis le fichier module.pm dans /etc/apache2/share/.
----------------------------------------------------------
package share::module;
use strict;
use warnings;
use 5.010;
our %hash = ( a => 1,
b => 2,
c => 3,
);
1
----------------------------------------------------------
Puis j'ai redémarré apache2. Ensuite, je teste mon script Perl
$ curl http://localhost/cgi-bin/test.pl
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]
Ça correspond donc à ce que j'attendais (j'appelle cette
sortie la sortie 1). Mais si je relance plusieurs fois la
commande, je finis (au bout de 3 ou 4 exécutions de curl)
$ curl http://localhost/cgi-bin/test.pl
BEFORE: e --> [5]
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
BEFORE: d --> [4]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]
Et une fois que j'ai ça (j'appelle cette sortie la sortie 2),
j'ai l'impression que ça ne bouge plus j'ai toujours cette sortie
2. Si je redémarre apache2, je me retrouve avec la sortie 1 à
nouveau mais au bout de 3 ou 4 curl, je retombe sur la sortie 2
etc. etc.
Pourquoi je finis par avoir cette sortie 2 ? Elle signifie
qu'au moment même où le script perl s'exécute, j'ai déjà
%share::module::hash qui est mis à jour alors qu'en principe
la première boucle "for" du script test.pl arrive *avant*
la mise à jour de %share::module::hash. Du coup je ne comprends
pas trop.
Si jamais je me débarrasse du module "share::module" et que
je mets directement le hash dans le script test.pl, je ne
constate pas ce phénomène.
Est-ce possible de faire en sorte que l'état du script test.pl
« reparte à zéro » à chaque requête (via curl dans mon cas).
Merci d'avance pour votre aide.
Le comportement que vous décrivez est tout à fait normal !

Si vous utilisez mod_perl avec Apache::Registry, un script perl
est compilé puis mis en cache mémoire. Il y a gain de rapidité,
car Apache va réutiliser le script déjà compilé. Il peut y avoir
plusieurs fois le même script dans le cache s'il y a plusieurs
requêtes simultanées.

Les variables globales sont initialisées à la première invocation,
puis elles gardent leurs valeurs. Il faut donc absolument initialiser
les variables globales en début de script. Si une variable globale
est définie dans un module, il faut une fonction d'initialisation :

package share::module;

use strict;
use warnings;
use 5.010;

our %hash;

sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);
}

1;

On initialise à chaque exécution. Si votre hash est de taille
importante, il est recommandé de le vider à la fin du script
pour épargner de la mémoire.

#!/usr/bin/perl

use strict;
use warnings;
use share::module;
use 5.010;
use CGI;

my $q = CGI->new;
print $q->header();

share::module::init(); # initialisation obligatoire

foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}

# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;

foreach my $key (keys %share::module::hash) {
say "AFTER: $key -|-> [$share::module::hash{$key}]";
}

%share::module::hash = (); # vidage facultatif

Attention : si vous modifiez le script, le changement est pris en
compte, car le cache est plus vieux que le script et Apache le
recompile et le remet en cache. Par contre, si vous modifiez
share::module, Apache ne s'aperçoit de rien et votre modification
n'est pas prise en compte. Il faut relancer Apache pour vider le
cache ce qui force une recompilation. (ou alors il faut utiliser
le module Apache::StatINC).

HTH

--
J-L
http://www.bribes.org/perl
Francois Lafont
2014-04-09 03:23:18 UTC
Permalink
Bonjour,
Post by Jean-Louis Morel
Si vous utilisez mod_perl avec Apache::Registry, un script perl
est compilé puis mis en cache mémoire. Il y a gain de rapidité,
car Apache va réutiliser le script déjà compilé.
En effet, c'est justement cette rapidité d'exécution qui
m'a orienté vers Apache2 + Perl via CGI.
Post by Jean-Louis Morel
Il peut y avoir
plusieurs fois le même script dans le cache s'il y a plusieurs
requêtes simultanées.
Les variables globales sont initialisées à la première invocation,
puis elles gardent leurs valeurs.
J'imagine qu'une variable globale est une variable déclarée
avec le mot-clé our (je suis vraiment débutant en Perl). J'avoue
qu'après avoir cherché des infos sur le web, les choses ne restent
pas super claires pour moi au niveau de la différence entre my et our.
Déjà, pour moi, une variable globale peut être utilisée directement
par son nom (par exemple %hash). Or quand j'avais testé dans mon cas,
la variable %hash n'était accessible dans le script que via son nom
pleinement qualifié (ie %share::module::hash) mais pas par son nom
directement. Si je tentais « %hash » directement, j'obtenais :

Global symbol "%hash" requires explicit package name

Donc ça serait une variable globale mais dont le nom est
disponible uniquement dans l'espace de noms du package "module".
Pour moi, ça ne correspond pas tout à fait à la notion que
j'ai d'une variable globale mais encore une fois je ne
connais pas très bien Perl pour l'instant.
Post by Jean-Louis Morel
Il faut donc absolument initialiser
les variables globales en début de script. Si une variable globale
package share::module;
use strict;
use warnings;
use 5.010;
our %hash;
sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);
}
1;
On initialise à chaque exécution. Si votre hash est de taille
importante, il est recommandé de le vider à la fin du script
pour épargner de la mémoire.
#!/usr/bin/perl
use strict;
use warnings;
use share::module;
use 5.010;
use CGI;
my $q = CGI->new;
print $q->header();
share::module::init(); # initialisation obligatoire
foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}
# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;
foreach my $key (keys %share::module::hash) {
say "AFTER: $key -|-> [$share::module::hash{$key}]";
}
%share::module::hash = (); # vidage facultatif
Ok, je vois l'idée.
Mais en fait, ce que je comprends de tout ça, c'est que j'aurais
surtout tout intérêt à me débarrasser complètement des variables
globales. Faire un truc du genre :

my %hash = share::module::get_initial_hash();

Autrement dit le package "module" contient une fonction qui
va me générer le hash de départ dont j'ai besoin (mais le
package lui-même ne contient pas un tel hash).
Post by Jean-Louis Morel
Attention : si vous modifiez le script, le changement est pris en
compte, car le cache est plus vieux que le script et Apache le
recompile et le remet en cache. Par contre, si vous modifiez
share::module, Apache ne s'aperçoit de rien et votre modification
n'est pas prise en compte. Il faut relancer Apache pour vider le
cache ce qui force une recompilation. (ou alors il faut utiliser
le module Apache::StatINC).
Ok, si je modifie un script appelé directement, pas besoin de
redémarrer apache2 mais si je change un package qui est utilisé
(via use) par un des scripts, là je dois redémarrer apache2. C'est
noté.

Merci beaucoup pour votre aide.
--
François Lafont
Paul Gaborit
2014-04-09 07:30:29 UTC
Permalink
À (at) Wed, 09 Apr 2014 05:23:18 +0200,
Post by Francois Lafont
Post by Jean-Louis Morel
Si vous utilisez mod_perl avec Apache::Registry, un script perl
est compilé puis mis en cache mémoire. Il y a gain de rapidité,
car Apache va réutiliser le script déjà compilé.
En effet, c'est justement cette rapidité d'exécution qui
m'a orienté vers Apache2 + Perl via CGI.
Si vous utilisez mod_perl, vous n'utilisez pas CGI !!!

En CGI, le script est un processus externe lancé par Apache à chaque
requête (les échanges entre Apache et ce processus externe se font
selon le protocole CGI).

En FastCGI, le script est un processus externe auquel Apache se
connecte au démarrage et auquel il transmet les requêtes une par une
(les échanges entre Apache et ce processus externe se font selon le
protocole FastCGI).

En mod_perl, Apache intègre un interpréteur Perl et les scripts Perl
tournent dans le processus Apache.
--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Francois Lafont
2014-04-09 09:36:23 UTC
Permalink
Bonjour,
Post by Paul Gaborit
Si vous utilisez mod_perl, vous n'utilisez pas CGI !!!
En CGI, le script est un processus externe lancé par Apache à chaque
requête (les échanges entre Apache et ce processus externe se font
selon le protocole CGI).
En FastCGI, le script est un processus externe auquel Apache se
connecte au démarrage et auquel il transmet les requêtes une par une
(les échanges entre Apache et ce processus externe se font selon le
protocole FastCGI).
En mod_perl, Apache intègre un interpréteur Perl et les scripts Perl
tournent dans le processus Apache.
Ok, merci pour ces précisions. Mais alors avec la configuration que
j'ai décrite au début de mon premier message, je suis dans quel cas
de figure ? Dans l'utilisation de mod_perl, c'est ça ? (vu que je
suis passé par l'installation du paquet libapache2-mod-perl2).

Mais d'un autre côté, le fait de mettre les scripts dans /usr/lib/cgi-bin/,
de les lancer via des urls comme http://localhost/cgi-bin/test.pl
et d'utiliser le module Perl CGI me met un peu dans la confusion.
--
François Lafont
Jean-Louis Morel
2014-04-09 16:42:55 UTC
Permalink
Post by Francois Lafont
J'imagine qu'une variable globale est une variable déclarée
avec le mot-clé our (je suis vraiment débutant en Perl). J'avoue
qu'après avoir cherché des infos sur le web, les choses ne restent
pas super claires pour moi au niveau de la différence entre my et our.
Déjà, pour moi, une variable globale peut être utilisée directement
par son nom (par exemple %hash). Or quand j'avais testé dans mon cas,
la variable %hash n'était accessible dans le script que via son nom
pleinement qualifié (ie %share::module::hash) mais pas par son nom
Global symbol "%hash" requires explicit package name
Donc ça serait une variable globale mais dont le nom est
disponible uniquement dans l'espace de noms du package "module".
Pour moi, ça ne correspond pas tout à fait à la notion que
j'ai d'une variable globale mais encore une fois je ne
connais pas très bien Perl pour l'instant.
Pour la différence entre my et our voir perlfunc :
my :
http://www.bribes.org/perl/docfr/perlfunc.html#LA5DF3A2B

our :
http://www.bribes.org/perl/docfr/perlfunc.html#LB9C68853

Si vous voulez pouvoir utiliser directement la variable %hash dans
votre script sans utiliser son nom pleinement qualifié, il faut
l'exporter - deux lignes à ajouter :

package share::module;

use strict;
use warnings;
use 5.010;

use Exporter qw( import );
our @EXPORT = ('%hash');

our %hash;

sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);
}

1;

Ensuite, si dans votre script vous faites
use share::module;
la variable %hash est importée automatiquement et vous pouvez
l'utiliser sous son nom.
Post by Francois Lafont
Mais en fait, ce que je comprends de tout ça, c'est que j'aurais
surtout tout intérêt à me débarrasser complètement des variables
my %hash = share::module::get_initial_hash();
Autrement dit le package "module" contient une fonction qui
va me générer le hash de départ dont j'ai besoin (mais le
package lui-même ne contient pas un tel hash).
Oui.
Si vous pouvez vous débarrasser d'une variable globale, n'hésitez
pas à vous en débarrasser ! c'est des soucis en moins.

Je pensais que votre exemple de code était simplifié et que
vous aviez plusieurs variables globales à initialiser. C'est
facile avec une seule fonction :

sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);

@liste = (...);

$foo = 'bar';
}

--
J-L
http://www.bribes.org/perl

Loading...