Neste artigo, falarei sobre por que, por que e como comecei a criar sites em Pascal: Delphi / FPC.Provavelmente, o "site Pascal" está associado a algo como:writeln('Content-type: text/html');
Mas não, tudo é muito mais interessante! No entanto, o código fonte de um site real (quase todos) está disponível no GitHub.Pelo que?
Em geral, nunca sou desenvolvedor web profissional - faço jogos. Um jogo, especialmente online, precisa de um site. Por isso, aconteceu que comecei a criar mais sites para meus jogos. Usando CGI no Perl - no início / meados dos anos 2000, era popular. Tudo estava bem até que um problema surgiu.Em 2013, começamos a realizar torneios on-line para o jogo Spectromancer . Para isso, criei uma página de torneio no site do jogo, que mostra quem jogar com quem, resultados atuais etc. No início do torneio, a página dos jogadores foi atualizada e ... não foi carregada. As pessoas pressionaram F5, exacerbando ainda mais o problema. Acontece que mesmo 4-5 solicitações por segundo para um script CGI, executam como um processo Perl separado, diminuem significativamente o servidor e mais de 10 solicitações por segundo o tornam completamente inacessível.É bom que esse teste de estresse tenha ocorrido durante o torneio de ensaio: mais tarde, eu já usei a página estática atualizada para os torneios.Por quê?
Assim, quando surgiu a necessidade de tornar este site para um novo jogo , surgiu a questão - em quê? O CGI do freio no Perl não é uma opção. FastCGI em Perl? Não consigo imaginar como escrever e depurar um programa multithread no Perl, já tive problemas suficientes com scripts comuns. Node.js? Provavelmente seria a melhor escolha, se não fosse por alguma hostilidade ao JS. E como o jogo em si e seu servidor são escritos em Pascal (na verdade, Delphi, mas o FPC também é bom), surgiu a idéia - devemos criar o site no mesmo idioma? Isso simplificará a integração com o servidor do jogo. "Tentar não é tortura!" Eu pensei e decidi tentar.Quão?
Eu escolhi o SimpleCGI (SCGI) como interface: é um pouco mais simples que o FastCGI, e as vantagens deste último são irrelevantes para mim - não há necessidade de distribuir o back-end para diferentes servidores, tudo roda no mesmo servidor. Portanto, a tarefa se resumiu ao desenvolvimento de uma certa estrutura SCGI que processa solicitações do servidor e gera páginas HTML em resposta a determinados modelos pré-fabricados. O resultado é uma estrutura de módulo . Consiste nas seguintes partes:- Loop principal : aceita conexões de entrada, lê solicitações e as coloca em uma fila para processamento. Grava respostas prontas para solicitações processadas em soquetes de conexão e as fecha.
- (N ): , . worker' — .
- : HTML- ( ) . .
- : ( CGI.pm Perl). , ..
Um recurso muito conveniente dos scripts Perl é que é muito fácil fazer pequenas alterações no site: basta editar o código do script e pronto. Não há necessidade de compilar, implantar. Certamente, pascal é uma linguagem compilada, não funcionará dessa maneira, mas ainda assim eu queria poder fazer alterações sempre que possível, sem reiniciar o processo. Portanto, tentei tornar o sistema de modelos suficientemente flexível.Ela trabalha assim. Os arquivos de modelos estão na pasta "modelos": eles são carregados quando o processo é iniciado e também recarregados quando alterados - dessa forma, você pode alterar o conteúdo dinâmico sem reiniciar o processo. Cada arquivo pode ter um ou mais modelos. Juntos, eles formam um dicionário (ou hash) de modelos: {"nome" -> "valor"}. Este é um dicionário estático de modelos - é comum a todas as solicitações e seu conteúdo permanece inalterado (até que o conteúdo dos arquivos seja alterado). Há outro - um dicionário dinâmico, criado vazio para cada solicitação e preenchido com manipulador de dados dinâmicos - por exemplo, do banco de dados. Combinando dados estáticos e dinâmicos, o resultado final é formado.Exemplo de declaração de modelo:#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>
Este é um modelo estático para um feed de notícias com o nome NEWSFEED_ITEM, que contém vários outros modelos, por exemplo, NEWS_TEXT - um modelo dinâmico que contém o texto de notícias baixado do banco de dados. A tradução é que todas as substrings do formulário $ TEMPLATE_NAME são recursivamente substituídas pelo valor desse modelo.Aqui você também pode observar o pseudotag para tradução condicional: <IF_TEMPLATE_NAME> - durante a tradução, essas tags são excluídas e seu conteúdo é deixado ou também excluído, dependendo do valor do modelo especificado. Escolhi especificamente esse formato de condições - na forma de tags HTML, para que, ao editar em um editor de texto, a realce da sintaxe funcione e seja fácil ver uma tag emparelhada.O código de geração de feeds usando este modelo é mais ou menos assim:
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;
Localização
Também é conveniente usar modelos para localização. Para fazer isso, use a variável global clientLang (no contexto da solicitação). Funciona assim: se o manipulador de solicitações descobrir que o cliente precisa de uma página em russo, ele escreverá o valor "RU" em clientLang, após o qual o tradutor de modelos, tendo encontrado $ TEMPLATE_NAME no texto, sempre tenta aplicar primeiro $ TEMPLATE_NAME. Assim, para a localização, é necessário apenas para cada modelo com texto criar sua versão para outro idioma:#TITLE_NEWS:News
#TITLE_NEWS_RU:
Um exemplo de uso de uma estrutura
Exemplo simples de código do site: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
Eu escrevi a estrutura descrita no processo de criação do site real astralheroes.com no final de 2015. Como geralmente acontece, a primeira panqueca saiu um pouco irregular - o código acabou sendo um pouco confuso e confuso, o próximo site está melhorando. No entanto, estou satisfeito com o processo e o resultado: o site funciona bem, é facilmente depurado e atualizado.Constatações:- Eu esperava que, comparado ao Perl compacto, o código do site esteja muito inchado, mas não - a mesma funcionalidade escrita em Pascal leva apenas o dobro do que em Perl. Mas parece mais claro.
- ! Perl — , - 100 , , . - - — . Delphi .
- Perl. -, , , . -, Perl, , .
- : , , . .
- . , , ( ), , . , , — . .
, . — . , :

, — . , .
?
Fontes no GitHub: github.com/Cooler2/ApusEngineExamplesObserve que há um submódulo no repositório; portanto, é melhor clonar com o parâmetro " --recursive ".O projeto do site está no arquivo: "AH-Website \ Backend \ src \ website.dpr"Esta não é uma cópia completa do site atual: é claro que não posso publicar o conteúdo do banco de dados com os dados dos players, também não publico scripts CGI, pois eles não relacionado ao tópico. No entanto, o projeto está sendo montado, lançado e funcionando, demonstrando plenamente o trabalho da estrutura.A publicação do código do site, bem como do código do mecanismo que ele usa, foi possível graças ao suporte que recebi no Patreon. Expresso minha gratidão a todos aqueles que me apoiaram e exorto você a participar - ainda há muitas coisas interessantes pela frente :)Obrigado pela atenção!