Sequa: Variablenverwaltungsverhalten von Perl/CGI

Hallo Zusammen,

ich bin momentan dabei in relativ umfangreiches System zu programmieren das viele Userabhänige Variablen über eine primäre SessionID steuert. Diese SessionID wird jeweils bei einer Aktion des Users an ein Verteilerscript übertragen. Zudem wird die gewünschte Aktion mit übergeben. Diese werden nun im Verteiler ausgewertet und von dort aus wird dann der eigentliche Service aufgerufen. Die Variablen hierfür sind unter anderem: $sessionid, $service, $action, $reload, $offset etc... das sind aber alles globale Variablen, keine my-Variablen. Der Grund hierfür ist das diese Variablen zwar im Verteiler erzeugt werden, aber später in den Unterroutinen noch gebraucht werden... z.b. die SessionID ($sessionid) um den User zu identifizieren oder $reload um festzustellen ob das System sich neu aufgebaut hat (spielt hier jetzt aber keine Rolle). Nun, wie ihr euch sicher denken könnt soll das nicht nur für einen User funktionieren, sondern für mehrere (was der Server verkraftet spielt nicht so die Rolle, wenn das Ganze fertig ist ist eh ein eigener Server dafür fällig...). Soweit sogut... aber was passiert jetzt wenn z.b. User1 einen Service aufruft, die Routinen werden abgearbeitet und die aufgerufene Seite dargestellt... direkt nach User1 hat aber User2 auch eine Abfrage geschickt, die Variablen von User1 wie z.b. die $sessionid wird also mit der von User2 belegt. Eine Routine die noch User1 bearbeitet braucht die Variable aber noch, was passiert dann? Hat Perl da eine Temporäre Verwaltung das mehrere gleichnamige Variablen existieren können und dann richtig zugeteilt werden oder block Perl User2 solange bis User1 abgearbeitet wurde? Wie genau verhält sich das? Es wäre nämlich nicht in meinem Sinne wenn User1 plötzlich die SessionID von User2 zugewiesen bekommt und damit möglicherweise eine ganz andere Userklasse bekommt.
My-Variablen die z.b. über routine($sessionid) durchgeschleift werden will ich nicht unbedingt machen da ich das etwas aufwendig finde... zudem wüsste ich nicht ob dann nicht der gleiche Effekt wie oben beschrieben auftreten könnte.

Ich hoffe das sich dazu jemand auskennt.

Schöne Grüße,

Sequa

  1. Hallo,

    das sind aber alles globale Variablen, keine my-Variablen. Der Grund hierfür ist das diese Variablen zwar im Verteiler erzeugt werden, aber später in den Unterroutinen noch gebraucht werden...

    Also, wenn sie nur für _Unter_routinen gebraucht werden, dann sind my-deklarierte Variablen auch dort noch sichtbar. Anders wäre es, wenn sie in Eltern-Funktionen benötigt werden. Bsp:

    my v;

    sub f1 {
     my v1;
     sub f2 {
      my v2;
     }
    }

    v1 existiert für f1 und f1, v2 allerdings nur für f2, v existiert sowohl in f1 als auch in f2 und im "Hauptprogramm" !

    aber was passiert jetzt wenn z.b. User1 einen Service aufruft, die Routinen werden abgearbeitet und die aufgerufene Seite dargestellt... direkt nach User1 hat aber User2 auch eine Abfrage geschickt, die Variablen von User1 wie z.b. die $sessionid wird also mit der von User2 belegt. Eine Routine die noch User1 bearbeitet braucht die Variable aber noch, was passiert dann?

    Nix, denn für jeden User der dein Perl Script aufruft werden alle Variablen/Funktionen neu angelegt, eine ganz neue Instanz deines Scripts erzeugt. Das funktioniert so ähnlich wie ein Funktionsaufruf: In der Unterfunktion werden mit jedem Aufruf auch wieder die Variablen neu übergeben, Werte verändert, ...  Da ein Perl-Script immer neu aufgerufen wird kann sowas nicht passieren.

    Wenn du z.b. mit Servlets arbeitest, musst du genau dies beachten. Ein Servlet wird einmal gestartet und läuft bis man es (händisch) wieder abschaltet. Dazwischen werden die Requests der User abgearbeitet. Dazu werden für jeden Aufruf "Threads" erzeugt, die, wie oben bei Perl, eine neue Instanz des Servlets erzeugen.

    Das kann natürlich alles parallel/gleichzeitig passieren.

    Ich hoffe ich habe dein Problem richtig gedeutet, und dir halbwegs verständlichmachen können wie das funktioniert ;-)

    lg bernhard

  2. Hallo,

    Fangen wir mal anders an. Was passiert, wenn Anfragen von zwei Benutzern quasi gleichzeitig zum Webserver geschickt werden? Im Normalfall wird das Script zwei mal gestartet, von zwei unterschiedlichen Instanzen des Webservers aus.
    Somit ist eins mal sicher, solange das Script läuft sind die Daten der beiden Benutzer von einander unabhängig.
    Wenn das Script beendet ist sind auch alle Daten wieder vergessen. Was wiederum auch bedeutet, daß bei jedem Aufruf die Daten dieses Benutzers wieder hergestellt werden müssen.

    Schwieriger ist es bei Systemen, welche beispielsweise mod_perl verwenden.
    Hier wird das Script nur einmal pro Serverinstanz gestartet und dann nicht mehr beendet. Das heißt, daß das Script bei einer bestimmten Instanz seine globalen Variablen nicht mehr 'vergisst'.
    Wenn man das und den Umstand, daß die Instanzen im Rotationsprinzip die Anfragen der Benutzer zugeordnet bekommen, berücksichtigt, ergeben sich mit globalen Variablen ein wirkliches Problem.
    Es kann dadurch nämlich passieren, daß ein Benutzer plötzlich mit den Daten eines andern Benutzers arbeitet.
    Um dies zu verhindern wird empfohlen, ständig 'my' zu verwenden.
    Wie das ganze funktioniert, kannst du ja der Perldokumentation entnehmen (oder einem schlauen Perlbuch), bzw. hat ja im wesentloichen Bernhard schon erklärt.

    Für das Problem mit den Benutzerdaten könntest Du Dir auch die Geschichte mit Objekten in Perl ansehen. Dies ist m.E. ein recht effizienter Weg, um Daten während der Laufzeit zu sammeln und an verschiedenen Stellen im Script zu verwenden.

    Beispiel:

    <code_of_main>
    #!perl

    use lib '/pfad/zu/meinen/moduln';
    use Mymodule;

    my $app_object = new Mymodule;

    $app_object->run();

    </code_of_main>

    <code_of_mymodule.pm>
    #!perl
    package Mymodule;
    use strict;
    use CGI;
    use Was::auch::immer;

    sub new
    {
    my($class) = shift;
    my(%params) = @_;
    my($self) = {};
    bless $self,$class;

    $self->{CGI} = $query; #als verbindung zum CGI;
    $self->init();
    return $self;
    }

    sub init  #dient zum bestimmen der Benutzerdaten aufgrund einer SessionID;
    {
    my $self = shift;
    #hier nur als Beispiel
    $self->{SESSION_ID} = $self->{CGI}->param('SESSIONID'); #oder was auch immer

    hier kaeme dann der Rest der Initialisierung hin, wie zB. Sicherheitscheck usw.

    }

    hier nur noch einige Beispiele, nix vollständiges

    sub run
    {
    my $self = shift;
    return $self->main() unless $self->{SESSION_ID};
    return $self->error('Du sicher nicht') unless $self->{IS_ALLOWED};

    }

    sub error
    {
    my $self = shift;
    my $errortext = shift;
    $self->_printHeader();
    print <<EOT;
    $errortext
    EOT
    exit;
    }

    sub _printHeader
    {
    my($self) = shift;

    if ($self->{CGI} && !($self->{IS_HEADER}))
     {
     print $self->{CGI}->header();
     print $self->{CGI}->start_html( -title => 'Ultimaives Webangebot');
     $self->{IS_HEADER} = 1;
     }

    }

    1;

    </code_of_mymodule.pm>

    Der Sinn der Sache ist eigentlich dieses ominöse '$self'. Da sämtliche Funktionen als Funktionen des Objekts aufgerufen werden, bekommen sie als ersten Parameter eine Referenz auf das Objekt. Über diese Referenz sind alle im Objekt gespeicherten Daten in der Funktion verfügbar. Auch Zustände, wie der, ob der HTTP-Header schon ausgegeben ist oder nicht, sind dadurch einfach zu behandeln.

    Ich hoffe, halbwegs Deine Fragen beantwortet zu haben, ohne allzu langatmig zu sein

    Grüße
      Klaus

    1. Hi,

      yo.. danke... das war eigentlich das was ich wissen wollte... das Sessionsystem das ich entwickelt habe ist an sich schon recht gut... läuft alles über MySQL Datenbanken und selbstständig prüfenden timestamps... aber das mit den Variablen war mir halt noch nicht ganz klar... vielen Dank euch beiden.

      CU Sequa