Pengembangan situs web dalam pascal (backend)

Pada artikel ini saya akan berbicara tentang mengapa, mengapa, dan bagaimana saya mulai membuat situs di Pascal: Delphi / FPC.
Mungkin "situs Pascal" dikaitkan dengan sesuatu seperti:

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

Tapi tidak, semuanya jauh lebih menarik! Namun, kode sumber situs nyata (hampir semua) tersedia di GitHub.

Untuk apa?


Secara umum, saya tidak pernah menjadi pengembang web profesional - saya membuat game. Gim, terutama gim daring, membutuhkan situs web. Oleh karena itu, kebetulan saya mulai membuat lebih banyak situs untuk permainan saya. Menggunakan CGI pada Perl - pada awal / pertengahan 2000-an itu populer. Semuanya baik-baik saja sampai muncul masalah.

Pada tahun 2013, kami mulai mengadakan turnamen online untuk game Spectromancer . Untuk melakukan ini, saya membuat halaman turnamen di situs web permainan, yang menunjukkan siapa yang akan bermain dengan siapa, hasil saat ini, dll. Di awal turnamen, halaman pemain telah diperbarui dan ... tidak dimuat. Orang-orang menekan F5, semakin memperburuk masalah. Ternyata bahkan 4-5 permintaan per detik ke skrip CGI, berjalan sebagai proses Perl terpisah, secara signifikan memperlambat server, dan> 10 permintaan per detik membuatnya benar-benar tidak dapat diakses.

Sangat bagus bahwa stress test ini terjadi selama turnamen latihan: nanti saya sudah menggunakan halaman statis yang diperbarui untuk turnamen.

Mengapa?


Jadi, ketika perlu muncul untuk membuat situs ini untuk permainan baru , pertanyaan muncul - pada apa? Rem CGI pada Perl bukan opsi. FastCGI dalam Perl? Saya tidak bisa membayangkan bagaimana menulis dan men-debug program multithreaded di Perl, saya punya cukup banyak masalah dengan skrip biasa. Node.js? Mungkin itu akan menjadi pilihan terbaik, jika bukan karena permusuhan dengan JS. Dan karena permainan itu sendiri dan servernya ditulis dalam Pascal (sebenarnya Delphi, tetapi FPC juga bagus), muncul ide - haruskah kita membuat situs dalam bahasa yang sama? Ini akan menyederhanakan integrasi dengan server game. "Berusaha bukanlah penyiksaan!" Saya berpikir, dan memutuskan untuk mencobanya.

Bagaimana?


Saya memilih SimpleCGI (SCGI) sebagai antarmuka: ini lebih sederhana daripada FastCGI, dan kelebihan yang terakhir tidak relevan bagi saya - tidak perlu mendistribusikan backend ke server yang berbeda, semuanya berjalan di server yang sama. Jadi tugas bermuara pada pengembangan kerangka kerja SCGI tertentu yang memproses permintaan dari server dan menghasilkan halaman HTML sebagai respons dari beberapa templat kosong. Hasilnya adalah kerangka kerja modul . Ini terdiri dari bagian-bagian berikut:

  • Loop utama : menerima koneksi yang masuk, membaca permintaan dan menempatkannya dalam antrian untuk diproses. Menulis tanggapan siap pakai untuk permintaan yang diproses ke soket koneksi dan menutupnya.
  • (N ): , . worker' — .
  • : HTML- ( ) . .
  • : ( CGI.pm Perl). , ..


Fitur yang sangat nyaman dari skrip Perl adalah sangat mudah untuk membuat perubahan kecil ke situs: cukup mengedit kode skrip dan hanya itu. Tidak perlu mengkompilasi, menyebarkan. Tentu saja, pascal adalah bahasa yang dikompilasi, itu tidak akan berhasil seperti itu, tapi tetap saya ingin dapat membuat perubahan bila memungkinkan tanpa memulai kembali proses. Karena itu, saya mencoba membuat sistem template cukup fleksibel.

Dia bekerja seperti itu. File templat ada di folder "templat": mereka dimuat saat proses dimulai dan juga dimuat kembali saat diubah - dengan cara ini Anda dapat mengubah konten dinamis tanpa memulai ulang proses. Setiap file dapat memiliki satu atau beberapa templat. Bersama-sama, mereka membentuk kamus (atau hash) templat: {"name" -> "value"}. Ini adalah kamus templat statis - umum untuk semua permintaan dan kontennya tidak berubah (hingga konten file berubah). Ada satu lagi - kamus dinamis, dibuat kosong untuk setiap permintaan dan diisi dengan penangan data dinamis - misalnya, dari database. Menggabungkan data statis dan dinamis, hasil akhir terbentuk.

Contoh pernyataan template:

#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>

Ini adalah templat statis untuk umpan berita dengan nama NEWSFEED_ITEM, di dalamnya berisi beberapa templat lainnya, misalnya NEWS_TEXT - templat dinamis yang berisi teks berita yang diunduh dari database. Terjemahan adalah bahwa semua substring dari bentuk $ TEMPLATE_NAME diganti secara rekursif dengan nilai templat ini.

Di sini Anda juga dapat melihat pseudotag untuk terjemahan bersyarat: <IF_TEMPLATE_NAME> - selama terjemahan, tag tersebut dihapus dan isinya dibiarkan atau juga dihapus, tergantung pada nilai templat yang ditentukan. Saya secara khusus memilih format kondisi ini - dalam bentuk tag HTML, sehingga ketika mengedit dalam editor teks, penyorotan sintaks berfungsi dan mudah untuk melihat tag berpasangan.

Kode pembuatan umpan menggunakan templat ini terlihat seperti ini:


    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;

Lokalisasi


Template juga nyaman digunakan untuk pelokalan. Untuk melakukan ini, gunakan variabel clientLang global (dalam konteks permintaan). Kerjanya seperti ini: jika penangan permintaan mengetahui bahwa klien membutuhkan halaman dalam bahasa Rusia, ia menulis nilai "RU" di clientLang, setelah penerjemah templat, setelah menemukan $ TEMPLATE_NAME dalam teks, selalu mencoba menerapkan pertama $ TEMPLATE_NAME. Jadi, untuk pelokalan, Anda hanya perlu membuat opsi untuk bahasa lain untuk setiap templat dengan teks:

#TITLE_NEWS:News
#TITLE_NEWS_RU:

Contoh menggunakan framework


Contoh kode situs sederhana:

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.

Total


Saya menulis kerangka yang dijelaskan dalam proses pembuatan situs nyata astralheroes.com pada akhir 2015. Seperti yang biasanya terjadi, pancake pertama keluar sedikit kental - kode itu ternyata agak berantakan dan membingungkan, situs berikutnya semakin baik. Namun demikian, saya puas dengan proses dan hasilnya: situs berfungsi dengan baik, mudah didebug dan diperbarui.

Temuan:

  • Saya berharap dibandingkan dengan Perl yang ringkas, kode situs sangat membengkak, tetapi tidak - fungsionalitas yang sama yang ditulis dalam Pascal hanya membutuhkan sekitar dua kali lipat dari pada Perl. Tapi itu terlihat lebih jelas.
  • ! Perl — , - 100 , , . - - — . Delphi .
  • Perl. -, , , . -, Perl, , .
  • : , , . .
  • . , , ( ), , . , , — . .

    , . — . , :



    , — . , .

?


Sumber di GitHub: github.com/Cooler2/ApusEngineExamples

Perhatikan bahwa ada submodule dalam repositori, jadi lebih baik untuk mengkloning dengan parameter "- recursive ".

Proyek situs ada dalam file: "AH-Website \ Backend \ src \ website.dpr"

Ini bukan salinan lengkap dari situs saat ini: jelas bahwa saya tidak dapat mempublikasikan konten database dengan data para pemain, saya juga tidak mempublikasikan skrip CGI, karena mereka tidak terkait dengan topik. Namun demikian, proyek ini sedang dirakit, diluncurkan dan bekerja, sepenuhnya menunjukkan kerja kerangka kerja.

Publikasi kode situs, serta kode mesin yang digunakannya, dimungkinkan berkat dukungan yang saya terima di Patreon. Saya mengucapkan terima kasih kepada semua yang mendukung saya dan mendorong Anda untuk bergabung - masih ada banyak hal menarik di depan :)

Terima kasih atas perhatian Anda!

All Articles