Bai, Virginia, *Bada* a Santa Claus Web esparruen arteko aldea 2023an

Programatzaile desafiatzaile batek web zerbitzariaren kode azkarra aurkitzeko bidaia, merkatuaren presioari eta zor teknikoari men egin aurretik.
2023-03-24 11:52:06
๐Ÿ‘๏ธ 763
๐Ÿ’ฌ 0

Edukiak

  1. Sarrera
  2. Proba
  3. PHP/Laravel
  4. PHP hutsa
  5. Laravel berriro bisitatzen
  6. Django
  7. Matrazea
  8. Starlette
  9. Node.js/ExpressJS
  10. Herdoila/Actix
  11. Zor Teknikoa
  12. Baliabideak

Sarrera

Nire azken lan-elkarrizketa baten ondoren, harrituta geratu nintzen konturatu nintzen eskatu nuen enpresak Laravel erabiltzen ari zela, duela hamarkada bat gutxi gorabehera probatu nuen PHP esparrua. Garai hartako duina zen, baina teknologian eta modetan konstante bat baldin badago, estilo eta kontzeptuen etengabeko aldaketa eta birsortzea da. JavaScript programatzailea bazara, ziurrenik txiste zahar hau ezagutzen duzu

1. programatzailea: "Ez zait gustatzen'JavaScript marko berri hau!"

2. programatzailea: "Ez da kezkatu beharrik. Sei hilabete itxaron besterik ez dago eta beste bat egongo da ordezkatzeko!"

Jakin-minagatik, zaharrak eta berriak proban jartzen ditugunean zer gertatzen den zehatz-mehatz ikustea erabaki nuen. Jakina, web erreferentzi eta erreklamazioz beteta dago, eta horietatik ezagunena da ziurrenik TechEmpower Web Framework Benchmarks hemen . Hala ere, gaur ez dugu ezer ia ia konplikaturik egingo. Gauza politak eta sinpleak mantenduko ditugu biak, artikulu hau bihurtu ez dadin Gerra eta Bakea , eta irakurtzen amaitzean esna egoteko aukera apur bat izango duzula' Ohiko oharrak aplikatzen dira: baliteke honek zure makinan berdin ez funtzionatzea, software-bertsio ezberdinek errendimenduan eragina izan dezakete eta Schrรถdinger';katua aldi berean erdi bizirik eta erdi hilda zegoen katu zonbi bihurtu zen.

Proba

Proba-ingurunea

Proba honetarako, nire ordenagailu eramangarria erabiliko dut Manjaro Linux exekutatzen duen i5 txiki batekin armatuta hemen erakusten den moduan.

โ•ฐโ”€โžค  uname -a
Linux jimsredmi 5.10.174-1-MANJARO #1 SMP PREEMPT Tuesday Mar 21 11:15:28 UTC 2023 x86_64 GNU/Linux

โ•ฐโ”€โžค  cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family  : 6
model   : 126
model name  : Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
stepping  : 5
microcode : 0xb6
cpu MHz   : 990.210
cache size  : 6144 KB

Eskuartean duen zeregina

Gure kodeak hiru zeregin erraz izango ditu eskaera bakoitzerako:

  1. Irakurri uneko erabiltzailearen saioaren IDa cookie batetik
  2. Kargatu informazio gehigarria datu-base batetik
  3. Itzuli informazio hori erabiltzaileari

Zer nolako proba idiota da hori, galdetuko zenuke? Bada, orrialde honen sareko eskaerak aztertzen badituzu, sessionvars.js izeneko bat ikusiko duzu gauza bera egiten duena.

sessionvars.js-en edukiak

Ikusten duzu, web-orri modernoak izaki konplikatuak dira, eta zeregin ohikoenetako bat orrialde konplexuak cachean gordetzea da datu-basearen zerbitzarian gehiegizko karga saihesteko.

Erabiltzaile batek eskatzen duen bakoitzean orrialde konplexu bat berriro errendatzen badugu, segundoko 600 erabiltzaile inguru bakarrik zerbitzatuko ditugu.

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1/system/index.en.html      
Running 10s test @ http://127.0.0.1/system/index.en.html
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   186.83ms  174.22ms   1.06s    81.16%
    Req/Sec   166.11     58.84   414.00     71.89%
  6213 requests in 10.02s, 49.35MB read
Requests/sec:    619.97
Transfer/sec:      4.92MB

Baina orrialde hau HTML fitxategi estatiko gisa gordetzen badugu eta Nginx-i erabiltzaileari leihotik azkar botatzen uzten badiogu, orduan 32.000 erabiltzaileri zerbitzatuko diegu segundoko, errendimendua 50 aldiz handituz.

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1/system/index.en.html
Running 10s test @ http://127.0.0.1/system/index.en.html
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.03ms  511.95us   6.87ms   68.10%
    Req/Sec     8.20k     1.15k   28.55k    97.26%
  327353 requests in 10.10s, 2.36GB read
Requests/sec:  32410.83
Transfer/sec:    238.99MB

Index.en.html estatikoa guztiontzat doan zatia da, eta erabiltzaileen arabera desberdinak diren zatiak soilik bidaltzen dira sessionvars.js-en. Honek datu-basearen karga murrizten du eta gure erabiltzaileentzako esperientzia hobea sortzen du, baina baita gure zerbitzariak berez lurrunduko dituen probabilitate kuantikoak murrizten ditu klingoien erasoan deformazio-nukleoaren haustura batean.

Kodearen Baldintzak

Esparru bakoitzeko itzultzen den kodeak baldintza sinple bat izango du: erakutsi erabiltzaileari orria zenbat aldiz freskatu duen "Kontua x da" esanez. Gauzak sinpleak izan daitezen, Redis ilaretatik, Kubernetes osagaietatik edo AWS Lambdatik urrun egongo gara oraingoz.

Orria zenbat aldiz bisitatu duzun erakusten

Erabiltzaile bakoitzaren saioaren datuak PostgreSQL datu-base batean gordeko dira.

Erabiltzaileen saioen taula

Eta datu-baseko taula hau proba bakoitzaren aurretik moztu egingo da.

Taula moztu ostean

Sinplea baina eraginkorra da Paferaren leloa... denbora-lerro ilunenetik kanpo, hala ere...

Benetako probaren emaitzak

PHP/Laravel

Ados, orain azkenean eskuak zikintzen has gaitezke. Laravelen konfigurazioa saltatuko dugu, konpositore eta artisau mordo bat besterik ez baita. aginduak.

Lehenik eta behin, gure datu-basearen ezarpenak .env fitxategian konfiguratuko ditugu

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=sessiontest
DB_USERNAME=sessiontest
DB_PASSWORD=sessiontest

Ondoren, eskaera guztiak gure kontrolagailura bidaltzen dituen bidegabeko bide bakarra ezarriko dugu.

Route::fallback(SessionController::class);

Eta ezarri kontrolagailua zenbaketa bistaratzeko. Laravelek, lehenespenez, datu-basean gordetzen ditu saioak. Gainera, eskaintzen du session() funtzioa gure saioko datuekin interfazea izateko, beraz, kode lerro pare bat izan ziren gure orria errendatzeko.

class SessionController extends Controller
{
  public function __invoke(Request $request)
  {
    $count  = session('count', 0);

    $count  += 1;

    session(['count' => $count]);

    return 'Count is ' . $count;
  }
}

php-fpm eta Nginx konfiguratu ondoren, gure orrialdeak nahiko itxura ona du...

โ•ฐโ”€โžค  php -v
PHP 8.2.2 (cli) (built: Feb  1 2023 08:33:04) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.2, Copyright (c) Zend Technologies
    with Xdebug v3.2.0, Copyright (c) 2002-2022, by Derick Rethans

โ•ฐโ”€โžค  sudo systemctl restart php-fpm
โ•ฐโ”€โžค  sudo systemctl restart nginx

Proben emaitzak benetan ikusi arte behintzat...

PHP/Laravel

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1
Running 10s test @ http://127.0.0.1
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.08s   546.33ms   1.96s    65.71%
    Req/Sec    12.37      7.28    40.00     56.64%
  211 requests in 10.03s, 177.21KB read
  Socket errors: connect 0, read 0, write 0, timeout 176
Requests/sec:     21.04
Transfer/sec:     17.67KB

Ez, hori ez da akatsa. Gure proba-makinak orrialde konplexu bat errendatzen duen segundoko 600 eskaera izatetik... segundoko 21 eskaera izatera pasa da "zenbaketa 1 da".

Beraz, zer gertatu zen gaizki? Zerbait gaizki al dago gure PHP instalazioarekin? Nginx nolabait moteltzen al da php-fpm-rekin konektatzean?

PHP hutsa

Egin dezagun orri hau PHP kode hutsean.

<?php

// ====================================================================
function uuid4() 
{
  return sprintf(
    '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
    mt_rand(0, 0xffff), mt_rand(0, 0xffff),
    mt_rand(0, 0xffff),
    mt_rand(0, 0x0fff) | 0x4000,
    mt_rand(0, 0x3fff) | 0x8000,
    mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
  );
}

// ====================================================================
function Query($db, $query, $params = [])
{
  $s  = $db->prepare($query);
  
  $s->setFetchMode(PDO::FETCH_ASSOC);
  $s->execute(array_values($params));
  
  return $s;
}

// ********************************************************************
session_start();

$sessionid  = 0;

if (isset($_SESSION['sessionid']))
{
  $sessionid  = $_SESSION['sessionid'];
}

if (!$sessionid)
{
  $sessionid              = uuid4();
  $_SESSION['sessionid']  = $sessionid;
}

$db   = new PDO('pgsql:host=127.0.0.1 dbname=sessiontest user=sessiontest password=sessiontest');
$data = 0;

try
{
  $result = Query(
    $db,
    'SELECT data FROM usersessions WHERE uid = ?',
    [$sessionid]
  )->fetchAll();
  
  if ($result)
  {
    $data = json_decode($result[0]['data'], 1);
  } 
} catch (Exception $e)
{
  echo $e;

  Query(
    $db,
    'CREATE TABLE usersessions(
      uid     TEXT PRIMARY KEY,
      data    TEXT
    )'
  );
}

if (!$data)
{
  $data = ['count'  => 0];
}

$data['count']++;

if ($data['count'] == 1)
{
  Query(
    $db,
    'INSERT INTO usersessions(uid, data)
    VALUES(?, ?)',
    [$sessionid, json_encode($data)]
  );
} else
{
  Query(
    $db,
    'UPDATE usersessions
      SET data = ?
      WHERE uid = ?',
    [json_encode($data), $sessionid]
  );
}

echo 'Count is ' . $data['count'];

Orain 98 kode-lerro erabili ditugu Laravel-en lau kode-lerro (eta konfigurazio-lan mordoa) egin zutena egiteko. (Noski, erroreen kudeaketa eta erabiltzaileei aurre egiteko mezu egokiak eginez gero, lerro kopuruaren bikoitza izango litzateke.) Agian segundoko 30 eskaera egin ditzakegu?

PHP/Pure PHP

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1                  
Running 10s test @ http://127.0.0.1
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   140.79ms   27.88ms 332.31ms   90.75%
    Req/Sec   178.63     58.34   252.00     61.01%
  7074 requests in 10.04s, 3.62MB read
Requests/sec:    704.46
Transfer/sec:    369.43KB

Aupa! Badirudi azken finean ez dagoela ezer okerrik gure PHP instalazioarekin. PHP bertsio hutsa segundoko 700 eskaera egiten ari da.

PHPrekin ezer okerrik ez badago, agian gaizki konfiguratu dugu Laravel?

Laravel berriro bisitatzen

Weba arakatu ondoren konfigurazio-arazoak eta errendimendu-aholkuak bilatzeko, teknikarik ezagunenetako bi konfigurazio-datuak cachean gordetzea eta bideratzea izan ziren, eskaera bakoitzerako prozesatzea ekiditeko. Hori dela eta, haien aholkuak hartuko ditugu eta aholku hauek probatuko ditugu.

โ•ฐโ”€โžค  php artisan config:cache

   INFO  Configuration cached successfully.  

โ•ฐโ”€โžค  php artisan route:cache

   INFO  Routes cached successfully.  

Dena ondo ikusten da komando lerroan. Berregin dezagun erreferentzia.

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1
Running 10s test @ http://127.0.0.1
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.13s   543.50ms   1.98s    61.90%
    Req/Sec    25.45     13.39    50.00     55.77%
  289 requests in 10.04s, 242.15KB read
  Socket errors: connect 0, read 0, write 0, timeout 247
Requests/sec:     28.80
Transfer/sec:     24.13KB

Beno, orain errendimendua handitu dugu segundoko 21.04tik 28.80 eskaerara, ia % 37ko igoera izugarria! Hau nahiko ikusgarria izango litzateke edozein software paketerentzat... izan ezik, oraindik ere PHP hutsezko bertsioaren eskaera kopuruaren 1/24 baino ez dugula egiten&#x27;

Proba honekin zerbait gaizki egon behar dela pentsatzen baduzu, Lucinda PHP frameworkaren egilearekin hitz egin beharko zenuke. Bere proben emaitzetan, du Lucinda Laravel irabazten 36x HTML eskaeretarako eta 90x JSON eskaeretarako.

Apache eta Nginx-ekin nire makinan probatu ondoren, ez dut haren zalantzan jartzeko arrazoirik. Laravel benetan justua da hori motel! PHP berez ez da hain txarra, baina Laravelek eskaera bakoitzari gehitzen dion prozesamendu gehigarri guztia gehitzen duzunean, oso zaila iruditzen zait Laravel 2023an aukera gisa gomendatzea.

Django

PHP/Wordpress kontuetarako sareko webgune guztien %40 inguru , marko nagusiena bihurtuz, alde handiz. Pertsonalki, baina, uste dut ospea ez dela zertan kalitatean itzultzen, eta ez dudan bat-batean janari gourmet aparteko gogoa kontrolaezina sentitzen dudana baino. munduko jatetxerik ezagunena ... McDonald&#x27;s. Dagoeneko PHP kode hutsa probatu dugunez, ez dugu Wordpress bera probatuko, Wordpress inplikatzen duen edozer PHP hutsarekin ikusi genituen segundoko 700 eskaerak baino txikiagoa izango litzateke dudarik gabe.

Django aspalditik dagoen beste esparru ezagun bat da. Iraganean erabili bazenuen, ziurrenik ederki gogoan izango duzu bere datu-baseen administrazio-interfaze ikusgarria eta dena nahi zenuen moduan konfiguratzea zein gogaikarria zen. Ikus dezagun Django-k 2023an nola funtzionatzen duen, batez ere 4.0 bertsiotik aurrera gehitu duen ASGI interfaze berriarekin.

Django konfiguratzea Laravel konfiguratzearen antzekoa da, biak baitziren MVC arkitektura dotoreak eta zuzenak ziren garaikoak. Konfigurazio aspergarria saltatuko dugu eta zuzenean ikuspegia konfiguratzera joango gara.

from django.shortcuts import render
from django.http import HttpResponse

# =====================================================================
def index(request):
  count = request.session.get('count', 0)
  count += 1
  request.session['count']  = count 
  return HttpResponse(f"Count is {count}")

Lau kode lerro Laravel bertsioarekin berdinak dira. Ikus dezagun nola funtzionatzen duen.

โ•ฐโ”€โžค  python --version
Python 3.10.9

Python/Django
โ•ฐโ”€โžค  gunicorn --access-logfile - -k uvicorn.workers.UvicornWorker -w 4 djangotest.asgi
[2023-03-21 15:20:38 +0800] [2886633] [INFO] Starting gunicorn 20.1.0

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1:8000/sessiontest/
Running 10s test @ http://127.0.0.1:8000/sessiontest/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   277.71ms  142.84ms 835.12ms   69.93%
    Req/Sec    91.21     57.57   230.00     61.04%
  3577 requests in 10.06s, 1.46MB read
Requests/sec:    355.44
Transfer/sec:    148.56KB

Ez dago batere gaizki segundoko 355 eskaerarekin. PHP bertsio hutsaren errendimenduaren erdia baino ez da, baina Laravel bertsioarena ere 12 aldiz handiagoa da. Django vs. Laravel badirudi ez dela inolako lehiarik.

Matrazea

Dena-sukaldea-harraska-esparru handiagoez gain, oinarrizko konfigurazio batzuk egiten dituzten marko txikiagoak ere badaude gainerakoa kudeatzen uzten dizuten bitartean. Erabiltzeko onenetako bat Flask eta bere ASGI Quart da. Nirea PaferaPy esparrua Flask-en gainean eraikita dago, beraz, ongi dakit zein erraza den gauzak egitea errendimendua mantenduz.

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Session benchmark test

import json
import psycopg
import uuid

from flask import Flask, session, redirect, url_for, request, current_app, g, abort, send_from_directory
from flask.sessions import SecureCookieSessionInterface

app = Flask('pafera')

app.secret_key  = b'secretkey'

dbconn  = 0

# =====================================================================
@app.route('/', defaults={'path': ''}, methods = ['GET', 'POST'])
@app.route('/<path:path>', methods = ['GET', 'POST'])
def index(path):
  """Handles all requests for the server. 
  
  We route all requests through here to handle the database and session
  logic in one place.
  """
  global dbconn
  
  if not dbconn:
    dbconn  = psycopg.connect('dbname=sessiontest user=sessiontest password=sessiontest')
    
    cursor  = dbconn.execute('''
      CREATE TABLE IF NOT EXISTS usersessions(
        uid     TEXT PRIMARY KEY,
        data    TEXT
      )
    ''')
    cursor.close()
    dbconn.commit()
      
  sessionid = session.get('sessionid', 0)
  
  if not sessionid:
    sessionid = uuid.uuid4().hex
    session['sessionid']  = sessionid
  
  cursor  = dbconn.execute("SELECT data FROM usersessions WHERE uid = %s", [sessionid])
  row     = cursor.fetchone()
  
  count = json.loads(row[0])['count'] if row else 0
  
  count += 1
  
  newdata = json.dumps({'count': count})
  
  if count == 1:
    cursor.execute("""
        INSERT INTO usersessions(uid, data)
        VALUES(%s, %s)
      """,
      [sessionid, newdata]
    )
  else:
    cursor.execute("""
        UPDATE usersessions
        SET data = %s
        WHERE uid = %s
      """,
      [newdata, sessionid]
    )
  
  cursor.close()
  
  dbconn.commit()
  
  return f'Count is {count}'

Ikus dezakezunez, Flask script-a PHP script hutsa baino laburragoa da. Erabili ditudan hizkuntza guztien artean, Python da, ziurrenik, tekla sakatzeei dagokienez, hizkuntza adierazgarriena. Giltza eta parentesirik ezak, zerrenda eta diktuaren ulermenak eta puntu eta koma baino koskatan oinarritutako blokeatzeak Python sinple samarra da, baina bere gaitasunetan indartsua.

Zoritxarrez, Python helburu orokorreko hizkuntza motelena ere bada, bertan zenbat software idatzi den arren. Eskuragarri dauden Python liburutegien kopurua antzeko hizkuntzak baino lau aldiz handiagoa da eta domeinu ugari hartzen ditu, baina inork ez luke esango Python azkarra edo eraginkorra denik NumPy bezalako nitxoetatik kanpo.

Ikus dezagun gure Flask bertsioa gure aurreko esparruekin alderatzen den.

Python/Flask

โ•ฐโ”€โžค  gunicorn --access-logfile - -w 4 flasksite:app
[2023-03-21 15:32:49 +0800] [2856296] [INFO] Starting gunicorn 20.1.0

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1:8000
Running 10s test @ http://127.0.0.1:8000
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    91.84ms   11.97ms 149.63ms   86.18%
    Req/Sec   272.04     39.05   380.00     74.50%
  10842 requests in 10.04s, 3.27MB read
Requests/sec:   1080.28
Transfer/sec:    333.37KB

Gure Flask script-a benetan gure PHP bertsio hutsa baino azkarragoa da!

Honekin harritzen bazaizu, konturatu beharko zenuke gure Flask aplikazioak hasierako eta konfigurazio guztia egiten duela gunicorn zerbitzaria abiarazten dugunean, eta PHP-k scripta berriro exekutatzen du eskaera berri bat sartzen den bakoitzean.&#x27; ;Flask autoa martxan jarri eta errepide ondoan zain dagoen taxi gidari gazte eta irritsu izatearen baliokidea da, PHP bere etxean dei baten zain geratzen den eta orduan bakarrik gidatzen duen gidari zaharra da. zu hartzera. Eskola zaharreko mutila izanik eta PHP HTML eta SHTML fitxategi arruntetarako aldaketa zoragarria izan zen garaietatik etorrita, triste samarra da zenbat denbora igaro den konturatzea, baina diseinu-desberdintasunek benetan zaila egiten diote PHP-ri lehiatu Python, Java eta Node.js zerbitzarien aurka, memorian geratzen diren eta eskaerak malabarista baten erraztasun arinarekin kudeatzen dituztenak.

Starlette

Flask izan daiteke orain arteko gure esparrurik azkarrena, baina benetan software zaharra da. Python komunitatea ASGI zerbitzari asinkrono berrietara aldatu zen duela pare bat urte, eta, jakina, ni neu aldatu naiz haiekin batera.

Pafera Markoaren bertsio berriena, PaferaPyAsync , Starletten oinarritzen da. Quart izeneko Flask-en ASGI bertsioa badago ere, Quart eta Starlette-ren arteko errendimendu-desberdintasunak nahikoak izan ziren nire kodea Starlette-n oinarritzeko.

Programazio asinkronoak jende askori beldurra eman diezaioke, baina ez da kontzeptu zaila duela hamarkada bat baino gehiago Node.js-eko mutilei esker.

Hari anitzeko, prozesamendu anitzeko, konputazio banatuarekin, promesa katearekin eta programatzaile beterano asko lehenago zahartu eta lehortzen zituzten garai dibertigarri haiekin batera borrokatzen genuen. Orain, idazten dugu async gure funtzioen aurrean eta await exekutatzeko denbora behar izan dezakeen edozein koderen aurrean. Kode arrunta baino zehatzagoa da, baina askoz gogaikarriagoa da erabiltzeko sinkronizazio primitiboekin, mezuen pasabideekin eta promesak ebatzi behar izatea baino.

Gure Starlette fitxategiak itxura hau du:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Session benchmark test

import json
import uuid

import psycopg

from starlette.applications import Starlette
from starlette.responses import Response, PlainTextResponse, JSONResponse, RedirectResponse, HTMLResponse
from starlette.routing import Route, Mount, WebSocketRoute
from starlette_session import SessionMiddleware

dbconn  = 0

# =====================================================================
async def index(R):
  global dbconn
  
  if not dbconn:
    dbconn  = await psycopg.AsyncConnection.connect('dbname=sessiontest user=sessiontest password=sessiontest')
    
    cursor  = await dbconn.execute('''
      CREATE TABLE IF NOT EXISTS usersessions(
        uid     TEXT PRIMARY KEY,
        data    TEXT
      )
    ''')
    await cursor.close()
    await dbconn.commit()
    
  sessionid = R.session.get('sessionid', 0)
  
  if not sessionid:
    sessionid = uuid.uuid4().hex
    R.session['sessionid']  = sessionid
  
  cursor  = await dbconn.execute("SELECT data FROM usersessions WHERE uid = %s", [sessionid])
  row     = await cursor.fetchone()
  
  count = json.loads(row[0])['count'] if row else 0
  
  count += 1
  
  newdata = json.dumps({'count': count})
  
  if count == 1:
    await cursor.execute("""
        INSERT INTO usersessions(uid, data)
        VALUES(%s, %s)
      """,
      [sessionid, newdata]
    )
  else:
    await cursor.execute("""
        UPDATE usersessions
        SET data = %s
        WHERE uid = %s
      """,
      [newdata, sessionid]
    )
  
  await cursor.close()
  await dbconn.commit()
  
  return PlainTextResponse(f'Count is {count}')

# *********************************************************************
app = Starlette(
  debug   = True, 
  routes  = [
    Route('/{path:path}', index, methods = ['GET', 'POST']),
  ],
)

app.add_middleware(
  SessionMiddleware, 
  secret_key  = 'testsecretkey', 
  cookie_name = "pafera",
)

Ikus dezakezunez, gure Flask scriptetik nahiko kopiatu eta itsatsi da bideratze-aldaketa pare batekin eta async/await gako-hitzak.

Zenbat hobekuntza ekar diezaguke benetan kopiatu eta itsatsitako kodea?

Python/Starlette

โ•ฐโ”€โžค  gunicorn --access-logfile - -k uvicorn.workers.UvicornWorker -w 4 starlettesite:app                                                                                                130 โ†ต
[2023-03-21 15:42:34 +0800] [2856220] [INFO] Starting gunicorn 20.1.0

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1:8000
Running 10s test @ http://127.0.0.1:8000
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    21.85ms   10.45ms  67.29ms   55.18%
    Req/Sec     1.15k   170.11     1.52k    66.00%
  45809 requests in 10.04s, 13.85MB read
Requests/sec:   4562.82
Transfer/sec:      1.38MB

Txapeldun berria dugu, jaun-andreok! Gure aurreko altua gure PHP bertsio hutsa izan zen segundoko 704 eskaerarekin, eta gero gure Flask bertsioa gainditu zuen segundoko 1080 eskaerarekin. Gure Starlette gidoiak aurreko lehiakide guztiak zapaltzen ditu segundoko 4562 eskaerarekin, hau da, PHP hutsaren 6 aldiz hobetzea eta Flask baino 4 aldiz hobetzea.

Oraindik ez baduzu zure WSGI Python kodea ASGIra aldatu, orain hasteko une egokia izan daiteke.

Node.js/ExpressJS

Orain arte, PHP eta Python esparruak soilik estali ditugu. Hala ere, munduko zati handi batek Java, DotNet, Node.js, Ruby on Rails eta horrelako beste teknologia batzuk erabiltzen ditu bere webguneetarako. Hau ez da inolaz ere munduko ekosistema eta bioma guztien ikuspegi orokorra, beraz, kimika organikoaren programazio baliokidea egitea saihesteko, kodea idazteko errazenak diren esparruak bakarrik aukeratuko ditugu. Java ez da zalantzarik gabe.

K&R C edo Knuth&#x27;s kopiaren azpian ezkutatuta egon ez bazara. Ordenagailuen Programazioaren Artea azken hamabost urteotan, seguruenik, Node.js-en berri izan duzu. JavaScript-en hasieratik egon garenok izugarri beldurtuta, harrituta gaude edo biak JavaScript modernoaren egoerarekin, baina ezin da ukatu JavaScript zerbitzarietan ere kontuan hartu beharreko indarra bihurtu dela. nabigatzaile gisa. Azken finean, jatorrizko 64 biteko zenbaki osoak ere baditugu hizkuntzan! Hori 64 biteko flotagailuetan gordeta dagoen guztia baino askoz hobea da!

ExpressJS da seguruenik erabiltzeko errazena Node.js zerbitzaria, beraz, Node.js/ExpressJS aplikazio azkar eta zikin bat egingo dugu gure kontagailua zerbitzatzeko.

/**********************************************************************
 * Simple session test using ExpressJS.
 **********************************************************************/
var L           = console.log;

var uuid        = require('uuid4');
var express     = require('express');
var session     = require('express-session');
var MemoryStore = require('memorystore')(session);

var { Client }  = require('pg')
var db          = 0;
var app       = express();

const PORT    = 8000;

//session middleware
app.use(
  session({
    secret:             "secretkey",
    saveUninitialized:  true,
    resave:             false,
    store:              new MemoryStore({
      checkPeriod: 1000 * 60 * 60 * 24 // prune expired entries every 24h
    })
  })
);

app.get('/',
  async function(req,res)
  {
    if (!db)
    {
      db  = new Client({
        user:     'sessiontest',
        host:     '127.0.0.1',
        database: 'sessiontest',
        password: 'sessiontest'
      });
      
      await db.connect();
      
      await db.query(`
        CREATE TABLE IF NOT EXISTS usersessions(
          uid     TEXT PRIMARY KEY,
          data    TEXT
        )`,
        []
      );
    };
    
    var session = req.session;
    
    if (!session.sessionid)
    {
      session.sessionid = uuid();
    }
    
    var row = 0;
    
    let queryresult = await db.query(`
      SELECT data::TEXT
      FROM usersessions 
      WHERE uid = $1`,
      [session.sessionid]
    );
    
    if (queryresult && queryresult.rows.length)
    {
      row = queryresult.rows[0].data;
    } 
    
    var count = 0;
    
    if (row)
    {
      var data  = JSON.parse(row);
      
      data.count  += 1;
      
      count = data.count;
      
      await db.query(`
          UPDATE usersessions
          SET data = $1
          WHERE uid = $2
        `,
        [JSON.stringify(data), session.sessionid]
      );
    } else
    {
      await db.query(`
        INSERT INTO usersessions(uid, data)
          VALUES($1, $2)`,
        [session.sessionid, JSON.stringify({count: 1})]
      );
      
      count = 1;
    }
    
    res.send(`Count is ${count}`);
  }
);

app.listen(PORT, () => console.log(`Server Running at port ${PORT}`));

Kode hau Python bertsioak baino idazteko errazagoa zen, nahiz eta jatorrizko JavaScript nahiko zaila den aplikazioak handitzen direnean, eta hau zuzentzeko saiakera guztiak, esate baterako, TypeScript, Python-ek baino arinagoak bihurtzen dira.

Ea nola funtzionatzen duen!

Node.js/ExpressJS

โ•ฐโ”€โžค  node --version                                                                                                                                                                     v19.6.0

โ•ฐโ”€โžค  NODE_ENV=production node nodejsapp.js                                                                                                                                             130 โ†ต
Server Running at port 8000

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1:8000
Running 10s test @ http://127.0.0.1:8000
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    90.41ms    7.20ms 188.29ms   85.16%
    Req/Sec   277.15     37.21   393.00     81.66%
  11018 requests in 10.02s, 3.82MB read
Requests/sec:   1100.12
Transfer/sec:    390.68KB

Baliteke Node.js&#x27; abiadura, eta istorio horiek egiazkoak dira gehienbat Google-k V8 JavaScript motorearekin egin duen lan ikusgarriari esker. Kasu honetan, baina, gure aplikazio azkarrak Flask script-a gainditzen duen arren, bere hari bakarreko izaerak garaitzen ditu "Ni!" esaten duen Starlette Knight-ek erabiltzen dituen lau prozesu asinkronikoek.

Lor dezagun laguntza gehiago!

โ•ฐโ”€โžค  pm2 start nodejsapp.js -i 4 

[PM2] Spawning PM2 daemon with pm2_home=/home/jim/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/jim/projects/paferarust/nodejsapp.js in cluster_mode (4 instances)
[PM2] Done.
โ”Œโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ id โ”‚ name         โ”‚ namespace   โ”‚ version โ”‚ mode    โ”‚ pid      โ”‚ uptime โ”‚ โ†บ    โ”‚ status    โ”‚ cpu      โ”‚ mem      โ”‚ user     โ”‚ watching โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 0  โ”‚ nodejsapp    โ”‚ default     โ”‚ N/A     โ”‚ cluster โ”‚ 37141    โ”‚ 0s     โ”‚ 0    โ”‚ online    โ”‚ 0%       โ”‚ 64.6mb   โ”‚ jim      โ”‚ disabled โ”‚
โ”‚ 1  โ”‚ nodejsapp    โ”‚ default     โ”‚ N/A     โ”‚ cluster โ”‚ 37148    โ”‚ 0s     โ”‚ 0    โ”‚ online    โ”‚ 0%       โ”‚ 64.5mb   โ”‚ jim      โ”‚ disabled โ”‚
โ”‚ 2  โ”‚ nodejsapp    โ”‚ default     โ”‚ N/A     โ”‚ cluster โ”‚ 37159    โ”‚ 0s     โ”‚ 0    โ”‚ online    โ”‚ 0%       โ”‚ 56.0mb   โ”‚ jim      โ”‚ disabled โ”‚
โ”‚ 3  โ”‚ nodejsapp    โ”‚ default     โ”‚ N/A     โ”‚ cluster โ”‚ 37171    โ”‚ 0s     โ”‚ 0    โ”‚ online    โ”‚ 0%       โ”‚ 45.3mb   โ”‚ jim      โ”‚ disabled โ”‚
โ””โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Ados! Orain lauko lau arteko borroka da! Egin dezagun erreferentzia!

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1:8000
Running 10s test @ http://127.0.0.1:8000
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    45.09ms   19.89ms 176.14ms   60.22%
    Req/Sec   558.93     97.50   770.00     66.17%
  22234 requests in 10.02s, 7.71MB read
Requests/sec:   2218.69
Transfer/sec:    787.89KB

Oraindik ez dago Starlette-ren mailan, baina ez da txarra bost minutuko JavaScript hack egiteko. Nire proben arabera, script hau apur bat atzera egiten ari da datu-baseen interfaze mailan, node-postgres ez baita inondik inora psycopg Pythonentzat bezain eraginkorra. Sqlite-ra aldatzeak datu-basearen kontrolatzaileak 3000 eskaera baino gehiago ematen ditu segundoko ExpressJS kode berarentzat.

Kontuan izan behar den gauza nagusia da Python-en exekuzio-abiadura motela izan arren, ASGI markoak benetan lehiakorrak izan daitezkeela Node.js irtenbideekin lan-karga jakin batzuetarako.

Herdoila/Actix

Beraz, orain, mendi tontorrera hurbiltzen ari gara, eta mendiz, saguek zein gizonek erregistratutako puntuazio altuenak esan nahi dut.

Sarean eskuragarri dauden esparru-erreferentzia gehienei begiratzen badiozu, nabarituko duzu goian nagusi izan ohi diren bi hizkuntza daudela: C++ eta Rust. 90eko hamarkadatik C++-rekin lan egin dut, eta nire Win32 C++ esparru propioa ere banuen MFC/ATL gauza izan baino lehen, beraz, esperientzia handia dut hizkuntzarekin. Ez da oso dibertigarria zerbaitekin lan egitea dagoeneko ezagutzen duzunean, beraz, Rust bertsio bat egingo dugu. ;)

Rust nahiko berria da programazio-lengoaiari dagokionez, baina jakin-min objektu bihurtu zitzaidan Linus Torvaldsek Rust Linux kernel programazio-lengoaia gisa onartuko zuela iragarri zuenean. Programatzaile zaharragook, hau da, AEBetako Konstituzioaren aldaketa berri bat izango zela esatea.

Orain, eskarmentu handiko programatzailea zarenean, gazteenek bezain azkar karroan ez salto egin ohi duzu, edo, bestela, baliteke hizkuntzaren edo liburutegien aldaketa azkarrek erretzea. (AngularJS-en lehen bertsioa erabili duen edonork jakingo du zertaz ari naizen.) Rust garapen esperimentalaren fase horretan dago oraindik, eta dibertigarria iruditzen zait sareko kode-adibide asko ez izatea. konpilatu gehiago paketeen egungo bertsioekin.

Hala ere, Rust aplikazioek erakusten duten errendimendua ezin da ukatu. Inoiz saiatu ez bazara ripgrep edo fd-aurkitu iturburu-kodeen zuhaitz handietan, behin betiko buelta bat eman beharko diezu. Linux banaketa gehienetarako ere eskuragarri daude paketeen kudeatzailetik. Rust-ekin hitzezkotasuna trukatzen ari zara... a asko verbositatearen batentzat asko errendimenduaren.

Rust-en kode osoa handi samarra da, beraz, hemen dagozkion kudeatzaileei begiratu bat emango diegu:

// =====================================================================
pub async fn RunQuery(
  db:       &web::Data<Pool>,
  query:    &str,
  args:     &[&(dyn ToSql + Sync)]
) -> Result<Vec<tokio_postgres::row::Row>, tokio_postgres::Error>
{  
  let client      = db.get().await.unwrap();
  let statement   = client.prepare_cached(query).await.unwrap();
  
  client.query(&statement, args).await
}

// =====================================================================
pub async fn index(
  req:      HttpRequest,
  session:  Session,
  db:       web::Data<Pool>,
) -> Result<HttpResponse, Error> 
{
  let mut count = 1;
  
  if let Some(sessionid) = session.get::<String>("sessionid")? 
  {
    let rows  = RunQuery(
      &db, 
      "SELECT data 
        FROM usersessions 
        WHERE uid = $1", 
      &[&sessionid]
    ).await.unwrap();
    
    if rows.is_empty()
    {
      let jsondata  = serde_json::json!({
        "count": 1,
      }).to_string();
      
      RunQuery(
        &db, 
        "INSERT INTO usersessions(uid, data)
          VALUES($1, $2)", 
        &[&sessionid, &jsondata]
      ).await
      .expect("Insert failed!");
    } else
    {
      let jsonstring:&str  = rows[0].get(0);
      let countdata: CountData = serde_json::from_str(jsonstring)?;
      
      count = countdata.count;
      
      count += 1;
      
      let jsondata  = serde_json::json!({
        "count": count,
      }).to_string();
      
      RunQuery(
        &db, 
        "UPDATE usersessions
        SET data = $1
        WHERE uid = $2
        ",
        &[&jsondata, &sessionid]
      ).await
      .expect("Update failed!");
    }
  } else 
  {
    let sessionid = Uuid::new_v4().to_string();
    
    let jsondata  = serde_json::json!({
      "count": 1,
    }).to_string();
    
    RunQuery(
      &db, 
      "INSERT INTO usersessions(uid, data)
        VALUES($1, $2)", 
      &[&sessionid, &jsondata]
    ).await
    .expect("Insert failed!");
    
    session.insert("sessionid", sessionid)?;    
  }  
  
  Ok(HttpResponse::Ok().body(format!(
    "Count is {:?}",
    count
  )))
}

Hau Python/Node.js bertsioak baino askoz konplexuagoa da...

Rust/Actix

โ•ฐโ”€โžค  cargo run --release
[2023-03-21T23:37:25Z INFO  actix_server::builder] starting 4 workers
Server running at http://127.0.0.1:8888/

โ•ฐโ”€โžค  wrk -d 10s -t 4 -c 100 http://127.0.0.1:8888
Running 10s test @ http://127.0.0.1:8888
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     9.93ms    3.90ms  77.18ms   94.87%
    Req/Sec     2.59k   226.41     2.83k    89.25%
  102951 requests in 10.03s, 24.59MB read
Requests/sec:  10267.39
Transfer/sec:      2.45MB

Eta askoz errendimendu handiagoa!

Gure Rust zerbitzariak Actix/deadpool_postgres erabiliz modu errazean gainditzen du gure aurreko txapeldun Starlette % 125, ExpressJS % 362 eta PHP hutsa % 1366. (Emanaldiko delta Laravel bertsioarekin utziko dut irakurlearentzat ariketa gisa.)

Rust hizkuntza bera ikastea beste hizkuntzak baino zailagoa izan dela ikusi dut, 6502 Assembly-tik kanpo ikusi dudan ezer baino askoz ere jokabide gehiago dituelako, baina zure Rust zerbitzariak 14 aldiz gehiago har ditzake. erabiltzaileak zure PHP zerbitzari gisa, orduan agian zerbait irabazi behar da teknologiak aldatzearekin. Horregatik, Pafera Markoaren hurrengo bertsioa Rust-en oinarrituko da. Ikasketa-kurba scripting lengoaiak baino askoz handiagoa da, baina errendimenduak mereziko du. Rust ikasteko denborarik eman ezin baduzu, zure teknologia-pila Starlette edo Node.js-en oinarritzea ere ez da erabaki txarra.

Zor Teknikoa

Azken hogei urteotan, ostalaritza-gune estatiko merkeetatik LAMP pilarekin ostalaritza partekatuetara pasa gara VPSak AWS, Azure eta hodeiko beste zerbitzu batzuetara alokatzera. Gaur egun, enpresa asko pozik daude diseinu-erabakiak hartzerakoan, eskuragarri edo merkeena den edonoren arabera, hodeiko zerbitzu erosoen agerpenetik zerbitzari eta aplikazio moteletara hardware gehiago botatzea erraztu baitute. Horrek epe laburreko irabazi handiak eman dizkie epe luzeko zor teknikoaren kostuan.

Kaliforniako Surgeon General&#x27;s Abisua: Hau ez da benetako espazio-txakur bat.

Duela 70 urte, Sobietar Batasunaren eta Estatu Batuen arteko espazio-lasterketa handia izan zen. Sobietarrek hasierako mugarri gehienak irabazi zituzten. Sputniken lehen satelitea izan zuten, Laikan espazioko lehen txakurra, Luna 2ko lehen ilargi-ontzia, espazioko lehen gizon-emakumea Yuri Gagarin eta Valentina Tereshkova eta abar...

Baina poliki-poliki zor teknikoa pilatzen ari ziren.

Sobietarrak lorpen horietako bakoitzean lehenak izan baziren ere, haien ingeniaritza-prozesuek eta helburuek epe laburreko erronketan zentratu zuten epe luzeko bideragarritasunean baino. Jauzi egiten zuten bakoitzean irabazi zuten, baina gero eta nekatuago eta motelagoak ziren aurkariek helmugan urrats sendoak ematen jarraitzen zuten bitartean.

Behin Neil Armstrong-ek zuzeneko telebistan ilargian bere urrats historikoak eman zituenean, estatubatuarrek hartu zuten aurrea, eta gero han geratu ziren sobietar programa kolokan zegoen bitartean. Hau ez da gaur egun hurrengo gauza handian, hurrengo ordain handian edo hurrengo teknologia handian zentratu diren enpresen aldean, epe luzerako ohitura eta estrategia egokiak garatzen ez dituzten bitartean.

Merkatuan lehena izateak ez du esan nahi merkatu horretan nagusi izango zarenik. Bestela, gauzak ondo egiteko denbora hartzeak ez du arrakasta bermatzen, baina, zalantzarik gabe, epe luzerako lorpenak lortzeko aukerak areagotzen ditu. Zure enpresaren teknologia-burua bazara, aukeratu norabide eta tresna egokiak zure lan-kargarako. Ez utzi ospeak errendimendua eta eraginkortasuna ordezkatzen.

Baliabideak

Rust, ExpressJS, Flask, Starlette eta Pure PHP scriptak dituen 7z fitxategi bat deskargatu nahi duzu?

Egileari buruz

Jim 90eko hamarkadan IBM PS/2 bat lortu zuenetik programatzen ari da. Gaur egun, oraindik nahiago du HTML eta SQL eskuz idaztea, eta eraginkortasuna eta zuzentasuna du arreta bere lanean.