Website-Entwicklung in Pascal (Backend)

In diesem Artikel werde ich darüber sprechen, warum, warum und wie ich angefangen habe, Websites in Pascal zu erstellen: Delphi / FPC.
Wahrscheinlich ist die "Pascal-Site" mit etwas verbunden wie:

writeln('Content-type: text/html');

Aber nein, alles ist viel interessanter! Der Quellcode einer realen Site (fast alle) ist jedoch auf GitHub verfügbar.

Wozu?


Im Allgemeinen bin ich nie ein professioneller Webentwickler - ich mache Spiele. Ein Spiel, insbesondere ein Online-Spiel, benötigt eine Website. Daher kam es vor, dass ich anfing, mehr Websites für meine Spiele zu erstellen. Verwendung von CGI auf Perl - Anfang / Mitte der 2000er Jahre war es beliebt. Alles war in Ordnung, bis ein Problem auftrat.

Seit 2013 veranstalten wir Online-Turniere für das Spectromancer- Spiel. Zu diesem Zweck habe ich auf der Website des Spiels eine Turnierseite erstellt, auf der angegeben ist, mit wem gespielt werden soll, aktuelle Ergebnisse usw. Zu Beginn des Turniers wurde die Spielerseite aktualisiert und ... nicht geladen. Die Leute drückten F5, was das Problem weiter verschärfte. Es stellte sich heraus, dass selbst 4-5 Anforderungen pro Sekunde an ein CGI-Skript, das als separater Perl-Prozess gestartet wurde, den Server erheblich verlangsamen und> 10 Anforderungen pro Sekunde den Zugriff auf ihn vollständig erschweren.

Es ist gut, dass dieser Stresstest während des Probenturniers stattgefunden hat: Später habe ich die aktualisierte statische Seite bereits für Turniere verwendet.

Warum?


Als sich die Notwendigkeit ergab, diese Seite für ein neues Spiel zu erstellen , stellte sich die Frage - worauf? Brems-CGI auf Perl ist keine Option. FastCGI in Perl? Ich kann mir nicht vorstellen, wie man ein Multithread-Programm in Perl schreibt und debuggt. Ich hatte genug Probleme mit normalen Skripten. Node.js? Wahrscheinlich wäre es die beste Wahl, wenn nicht eine Feindseligkeit gegenüber JS. Und da das Spiel selbst und sein Server in Pascal geschrieben sind (eigentlich Delphi, aber FPC ist auch gut), entstand die Idee - sollten wir die Site in derselben Sprache erstellen? Dies vereinfacht die Integration mit dem Spieleserver. "Versuchen ist keine Folter!" Dachte ich und beschloss, es zu versuchen.

Wie?


Ich habe SimpleCGI (SCGI) als Schnittstelle gewählt: Es ist etwas einfacher als FastCGI, und die Vorteile des letzteren sind für mich irrelevant - das Backend muss nicht auf verschiedene Server verteilt werden, alles läuft auf demselben Server. Die Aufgabe bestand also in der Entwicklung eines bestimmten SCGI-Frameworks, das Anforderungen vom Server verarbeitet und HTML-Seiten als Antwort auf bestimmte vorgefertigte Vorlagen generiert. Das Ergebnis ist ein solches Modul-Framework . Es besteht aus folgenden Teilen:

  • Hauptschleife : Akzeptiert eingehende Verbindungen, liest Anforderungen und stellt sie zur Verarbeitung in eine Warteschlange. Schreibt fertige Antworten auf verarbeitete Anforderungen in Verbindungssockets und schließt sie.
  • (N ): , . worker' — .
  • : HTML- ( ) . .
  • : ( CGI.pm Perl). , ..


Eine sehr praktische Funktion von Perl-Skripten ist, dass es sehr einfach ist, kleine Änderungen an der Site vorzunehmen: Bearbeiten Sie einfach den Skriptcode und fertig. Keine Notwendigkeit zum Kompilieren, Bereitstellen. Natürlich ist Pascal eine kompilierte Sprache, es wird nicht so funktionieren, aber ich wollte trotzdem in der Lage sein, Änderungen vorzunehmen, wann immer dies möglich ist, ohne den Prozess neu zu starten. Daher habe ich versucht, das Vorlagensystem flexibel genug zu gestalten.

Sie arbeitet so. Die Vorlagendateien befinden sich im Ordner „Vorlagen“: Sie werden beim Start des Prozesses geladen und beim Ändern neu geladen. Auf diese Weise können Sie dynamischen Inhalt ändern, ohne den Prozess neu zu starten. Jede Datei kann eine oder mehrere Vorlagen enthalten. Zusammen bilden sie ein Wörterbuch (oder einen Hash) von Vorlagen: {"Name" -> "Wert"}. Dies ist ein statisches Wörterbuch mit Vorlagen - es ist allen Anforderungen gemeinsam und sein Inhalt bleibt unverändert (bis sich der Inhalt der Dateien ändert). Es gibt ein anderes - ein dynamisches Wörterbuch, das für jede Anforderung leer erstellt und mit einem dynamischen Datenhandler gefüllt wird - beispielsweise aus der Datenbank. Durch die Kombination von statischen und dynamischen Daten wird das Endergebnis gebildet.

Beispiel für eine Vorlagendeklaration:

#NEWSFEED_ITEM:
<div class=NewsHeader>
 <a href='/$LANG_LC/forum/thread/$NEWS_ID'><IF_NEWS_PINNED>[TOP]  </IF_NEWS_PINNED>$NEWS_DATE   $NEWS_TITLE</a>
</div>
<div class=NewsText>$NEWS_TEXT
 <div align=right>
  <a href='/$LANG_LC/forum/thread/$NEWS_ID'>$COMMENTS</a>
 </div>
</div>

Dies ist eine statische Vorlage für einen Newsfeed mit dem Namen NEWSFEED_ITEM. Sie enthält mehrere andere Vorlagen, z. B. NEWS_TEXT - eine dynamische Vorlage mit aus der Datenbank heruntergeladenem Nachrichtentext. Die Übersetzung besteht darin, dass alle Teilzeichenfolgen der Form $ TEMPLATE_NAME rekursiv durch den Wert dieser Vorlage ersetzt werden.

Hier können Sie auch das Pseudotag für die bedingte Übersetzung bemerken: <IF_TEMPLATE_NAME> - Während der Übersetzung werden solche Tags gelöscht und ihr Inhalt bleibt erhalten oder wird auch gelöscht, abhängig vom Wert der angegebenen Vorlage. Ich habe speziell dieses Format von Bedingungen gewählt - in Form von HTML-Tags, damit beim Bearbeiten in einem Texteditor die Syntaxhervorhebung funktioniert und ein gepaartes Tag leicht zu erkennen ist.

Der Code zur Feed-Generierung mit dieser Vorlage sieht ungefähr so ​​aus:


    result:='';
    //       NEWSFEED_ITEM      result
    for i:=0 to n-1 do begin
      id:=StrToIntDef(sa[i*c],0);
      title:=sa[i*c+1];
      cnt:=StrToIntDef(sa[i*c+2],1)-1;
      flags:=StrToIntDef(sa[i*c+3],0);
      //     
      db.Query('SELECT msg,created FROM messages WHERE topic=%d ORDER BY id LIMIT 1', 
        [id]);
      if db.lastErrorCode<>0 then continue;
      text:=db.Next;
      date:=db.NextDate;
      //    ( temp)
      temp.Put('NEWS_ID',id,true);
      temp.Put('NEWS_DATE',FormatDate(date,true),true);
      temp.Put('NEWS_TITLE',title,true);
      temp.Put('NEWS_PINNED',flags and 4>0,true);
      comLink:='$LNK_READ_MORE | ';
      if cnt>0 then comLink:=comLink+inttostr(cnt)+' $LNK_COMMENTS'
        else comLink:=comLink+'$LNK_LEAVE_COMMENT';
      temp.Put('NEWS_TEXT',text,true);
      temp.Put('COMMENTS',comLink,true);
      //   
      result:=result+BuildTemplate('#NEWSFEED_ITEM');
    end;

Lokalisierung


Vorlagen können auch bequem zur Lokalisierung verwendet werden. Verwenden Sie dazu die globale Variable (im Kontext der Anforderung) clientLang. Das funktioniert folgendermaßen: Wenn der Anforderungshandler feststellt, dass der Client eine Seite auf Russisch benötigt, schreibt er den Wert "RU" in clientLang. Danach versucht der Vorlagenübersetzer, nachdem er $ TEMPLATE_NAME im Text gefunden hat, immer zuerst $ TEMPLATE_NAME anzuwenden. Für die Lokalisierung ist es daher nur erforderlich, dass jede Vorlage mit Text ihre Version für eine andere Sprache erstellt:

#TITLE_NEWS:News
#TITLE_NEWS_RU:

Ein Beispiel für die Verwendung eines Frameworks


Einfaches Site-Code-Beispiel:

program website;
uses SysUtils, SCGI;

//    
function IndexPage:AnsiString; stdcall;
 begin
   result:=FormatHeaders('text/html')+BuildTemplate('#INDEX.HTM');
 end;

begin
 SetCurrentDir(ExtractFileDir(ParamStr(0)));
 SCGI.Initialize; //  
 AddHandler('/',IndexPage); //     '/'
 SCGI.RunServer; //      
end.

Gesamt


Ich habe das beschriebene Framework bei der Erstellung der realen Website astralheroes.com Ende 2015 geschrieben. Wie es normalerweise passiert, kam der erste Pfannkuchen etwas klumpig heraus - der Code erwies sich als etwas chaotisch und verwirrend, die nächste Seite wird besser. Trotzdem bin ich mit dem Prozess und dem Ergebnis zufrieden: Die Seite funktioniert gut, ist leicht zu debuggen und zu aktualisieren.

Ergebnisse:

  • Ich habe erwartet, dass der Site-Code im Vergleich zu kompaktem Perl sehr aufgebläht ist, aber nein - dieselbe in Pascal geschriebene Funktionalität benötigt nur etwa doppelt so viel wie in Perl. Aber es sieht klarer aus.
  • ! Perl — , - 100 , , . - - — . Delphi .
  • Perl. -, , , . -, Perl, , .
  • : , , . .
  • . , , ( ), , . , , — . .

    , . — . , :



    , — . , .

?


Quellen auf GitHub: github.com/Cooler2/ApusEngineExamples

Beachten Sie, dass das Repository ein Submodul enthält. Es ist daher besser, mit dem Parameter " --recursive " zu klonen .

Das Site-Projekt befindet sich in der Datei: "AH-Website \ Backend \ src \ website.dpr"

Dies ist keine vollständige Kopie der aktuellen Site: Es ist klar, dass ich den Inhalt der Datenbank nicht mit den Daten der Player veröffentlichen kann, ich veröffentliche auch keine CGI-Skripte, da diese nicht mit dem Thema verbunden. Trotzdem wird das Projekt zusammengestellt, gestartet und funktioniert, um die Arbeit des Frameworks vollständig zu demonstrieren.

Die Veröffentlichung des Site-Codes sowie des verwendeten Engine-Codes wurde dank der Unterstützung ermöglicht, die ich auf Patreon erhalten habe. Ich bedanke mich bei allen, die mich unterstützt haben und fordere Sie auf, sich anzuschließen - es liegen noch viele interessante Dinge vor

Ihnen :) Vielen Dank für Ihre Aufmerksamkeit!

All Articles