Wara waħda mill-intervisti tax-xogħol l-aktar riċenti tiegħi, kont sorpriż meta rrealizzajt li l-kumpanija li applikajt għaliha kienet għadha tuża Laravel, qafas PHP li ppruvajt madwar għaxar snin ilu. Kien deċenti għal dak iż-żmien, imma jekk hemm kostanti waħda fit-teknoloġija u l-moda bl-istess mod, hija bidla kontinwa u wiċċ mill-ġdid ta 'stili u kunċetti. Jekk inti programmatur JavaScript, inti probabilment familjari ma 'din iċ-ċajta antika
Programmatur 1: "Ma nħobbx dan il-qafas JavaScript ġdid!"
Programmatur 2: "M'hemmx għalfejn tinkwieta. Just stenna sitt xhur u se jkun hemm ieħor biex tissostitwiha!"
B’kurżità, iddeċidejt li nara eżattament x’jiġri meta npoġġu l-qodma u l-ġdida għat-test. Naturalment, il-web hija mimlija b'punti ta 'referenza u talbiet, li l-aktar popolari huwa probabbilment l- TechEmpower Web Framework Benchmarks hawn . Iżda aħna'mhux se nagħmlu xejn daqshekk ikkumplikat daqshom illum. Aħna nżommu l-affarijiet sbieħ u sempliċi t-tnejn biex dan l-artiklu ma jinbidilx Gwerra u Paċi , u li jkollok ċans żgħir li tibqa' mqajjem saż-żmien li tkun lest taqra. Japplikaw it-twissijiet tas-soltu: dan jista 'ma jaħdimx l-istess fuq il-magna tiegħek, verżjonijiet ta' softwer differenti jistgħu jaffettwaw il-prestazzjoni, u l-qattus ta 'Schrödinger's fil-fatt sar qattus zombie li kien nofs ħaj u nofs mejjet fl-istess ħin eżatt.
Għal dan it-test, se nkun qed nuża l-laptop tiegħi armat b'i5 puny li jħaddem Manjaro Linux kif muri hawn.
╰─➤ 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
Il-kodiċi tagħna se jkollu tliet kompiti sempliċi għal kull talba:
X'tip ta 'test idjotiku huwa dak, tista' tistaqsi? Ukoll, jekk tħares lejn it-talbiet tan-netwerk għal din il-paġna, tinnota wieħed imsejjaħ sessionvars.js li jagħmel eżattament l-istess ħaġa.
Tara, paġni tal-web moderni huma kreaturi kkumplikati, u waħda mill-kompiti l-aktar komuni hija l-caching ta 'paġni kumplessi biex tiġi evitata tagħbija żejda fuq is-server tad-database.
Jekk nerġgħu nirrendu paġna kumplessa kull darba li utent jitlobha, allura nistgħu naqdu biss madwar 600 utent kull sekonda.
╰─➤ 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
Imma jekk nilqgħu din il-paġna bħala fajl HTML statiku u nħallu lil Nginx jitfgħuha malajr mit-tieqa lill-utent, allura nistgħu naqdu 32,000 utent kull sekonda, u nżidu l-prestazzjoni b'fattur ta '50x.
╰─➤ 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
L-index.en.html statiku huwa l-parti li tmur lil kulħadd, u l-partijiet biss li jvarjaw skont l-utent jintbagħtu f'sessionvars.js. Dan mhux biss inaqqas it-tagħbija tad-database u joħloq esperjenza aħjar għall-utenti tagħna, iżda jnaqqas ukoll il-probabbiltajiet quantum li s-server tagħna jifvaporizza spontanjament fi ksur tal-qalba tal-medd meta l-attakk tal-Klingons.
Il-kodiċi mibgħuta lura għal kull qafas se jkollu rekwiżit wieħed sempliċi: uri lill-utent kemm-il darba'aġġorna l-paġna billi jgħid "Għadd huwa x". Biex inżommu l-affarijiet sempliċi, aħna se nibqgħu 'l bogħod mill-kjuwijiet Redis, komponenti Kubernetes, jew AWS Lambdas għalissa.
Id-dejta tas-sessjoni ta' kull utent se tiġi ssejvjata f'database PostgreSQL.
U din it-tabella tad-database se tkun maqtugħa qabel kull test.
Sempliċi iżda effettiv huwa l-motto ta' Pafera... barra mill-iktar skeda ta' żmien mudlama xorta waħda...
Tajjeb, allura issa nistgħu fl-aħħar nibdew inħammġu jdejna. Aħna se naqbżu s-setup għal Laravel peress li huwa biss mazz ta 'kompożitur u artiġjan jikkmanda.
L-ewwel, aħna'se nissettjaw is-settings tad-database tagħna fil-fajl .env
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=sessiontest
DB_USERNAME=sessiontest
DB_PASSWORD=sessiontest
Imbagħad se nissettjaw rotta waħda ta' riżerva li tibgħat kull talba lill-kontrollur tagħna.
Route::fallback(SessionController::class);
U ssettja l-kontrollur biex juri l-għadd. Laravel, awtomatikament, jaħżen is-sessjonijiet fid-database. Jipprovdi wkoll il- session()
funzjoni biex interface mad-dejta tas-sessjoni tagħna, għalhekk kulma ħadet kien ftit linji ta 'kodiċi biex tirrendi l-paġna tagħna.
class SessionController extends Controller
{
public function __invoke(Request $request)
{
$count = session('count', 0);
$count += 1;
session(['count' => $count]);
return 'Count is ' . $count;
}
}
Wara li waqqaf php-fpm u Nginx, il-paġna tagħna tidher pjuttost tajba...
╰─➤ 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
Mill-inqas sakemm fil-fatt naraw ir-riżultati tat-test...
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
Le, dak mhuwiex typo. Il-magna tat-test tagħna marret minn 600 talba kull sekonda li tirrendi paġna kumplessa... għal 21 talba kull sekonda li tirrendi "Count is 1".
Allura x'mar ħażin? Hija xi ħaġa ħażina fl-installazzjoni PHP tagħna? Nginx qed jonqos b'xi mod meta jiffaċċa php-fpm?
Ejja nerġgħu nagħmlu din il-paġna f'kodiċi PHP pur.
<?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'];
Issa użajna 98 linja ta 'kodiċi biex nagħmlu dak li għamlu erba' linji ta 'kodiċi (u mazz sħiħ ta' xogħol ta 'konfigurazzjoni) f'Laravel. (Naturalment, jekk għamilna tqandil xieraq tal-iżbalji u messaġġi li jiffaċċjaw l-utent, dan ikun madwar id-doppju tan-numru ta 'linji.) Forsi nistgħu nagħmluha għal 30 talba kull sekonda?
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
Whoa! Jidher li m'hemm xejn ħażin fl-installazzjoni PHP tagħna wara kollox. Il-verżjoni PHP pura qed tagħmel 700 talba kull sekonda.
Jekk m'hemm xejn ħażin mal-PHP, forsi kkonfigurajna ħażin lil Laravel?
Wara li togħrok il-web għal kwistjonijiet ta 'konfigurazzjoni u pariri dwar il-prestazzjoni, tnejn mit-tekniki l-aktar popolari kienu li jqiegħdu fil-cache l-konfigurazzjoni u d-data tar-rotta biex jevitaw li jipproċessawhom għal kull talba. Għalhekk, aħna se nieħdu l-parir tagħhom u nippruvaw dawn il-pariri.
╰─➤ php artisan config:cache
INFO Configuration cached successfully.
╰─➤ php artisan route:cache
INFO Routes cached successfully.
Kollox jidher tajjeb fuq il-linja tal-kmand. Ejja nerġgħu nagħmlu l-benchmark.
╰─➤ 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
Ukoll, issa żidna l-prestazzjoni minn 21.04 għal 28.80 talba kull sekonda, żieda drammatika ta 'kważi 37%! Dan ikun pjuttost impressjonanti għal kwalunkwe pakkett ta 'softwer... ħlief għall-fatt li aħna'għadna nagħmlu biss 1/24th tan-numru ta 'talbiet tal-verżjoni PHP pura.
Jekk qed taħseb li xi ħaġa għandha tkun ħażina f'dan it-test, għandek titkellem mal-awtur tal-qafas Lucinda PHP. Fir-riżultati tat-test tiegħu, huwa għandu Lucinda tegħleb lil Laravel b'36x għal talbiet HTML u 90x għal talbiet JSON.
Wara li ttestja fuq il-magna tiegħi stess kemm b'Apache kif ukoll b'Nginx, m'għandi l-ebda raġuni biex niddubitah. Laravel huwa verament ġust dak bil-mod! PHP waħdu mhuwiex daqshekk ħażin, iżda ladarba żżid l-ipproċessar żejjed kollu li Laravel iżid ma 'kull talba, allura nsibha diffiċli ħafna li nirrakkomanda lil Laravel bħala għażla fl-2023.
PHP/Wordpress jammonta għal madwar 40% tal-websajts kollha fuq il-web , li jagħmilha bil-bosta l-aktar qafas dominanti. Iżda personalment, insib li l-popolarità mhux bilfors tissarraf fi kwalità aktar milli nsib lili nnifsi ħeġġa f'daqqa waħda bla kontroll għal dak l-ikel gourmet straordinarju minn l-aktar restorant popolari fid-dinja ... McDonald's. Peress li diġà ttestjajna kodiċi PHP pur, aħna mhux se nittestjaw Wordpress innifsu, peress li kull ħaġa li tinvolvi Wordpress bla dubju tkun inqas mis-700 talba kull sekonda li osservajna b'PHP pur.
Django huwa qafas popolari ieħor li ilu jeżisti għal żmien twil. Jekk inti użajt fil-passat, inti probabilment qed tiftakar bil-qalb l-interface spettakolari ta 'amministrazzjoni tad-database tagħha flimkien ma' kemm kien tedjanti li kkonfigurat kollox eżatt kif ridt. Ejja naraw kemm Django jaħdem tajjeb fl-2023, speċjalment bl-interface ASGI l-ġdida li żiedet mill-verżjoni 4.0.
It-twaqqif ta 'Django huwa simili ħafna għat-twaqqif ta' Laravel, peress li t-tnejn kienu mill-età fejn l-arkitetturi MVC kienu stylish u korretti. Aħna se naqbżu l-konfigurazzjoni boring u mmorru direttament biex inwaqqfu l-veduta.
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}")
Erba 'linji ta' kodiċi huma l-istess bħall-verżjoni Laravel. Ejja naraw kif taħdem.
╰─➤ 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
Mhux ħażin xejn għal 355 talba kull sekonda. Huwa biss nofs il-prestazzjoni tal-verżjoni PHP pura, iżda huwa wkoll 12x dak tal-verżjoni Laravel. Django vs Laravel jidher li m'hemm l-ebda kompetizzjoni.
Minbarra l-oqfsa akbar ta 'kollox-inkluż-il-sink tal-kċina, hemm ukoll oqfsa iżgħar li jagħmlu biss xi setup bażiku filwaqt li jħalluk timmaniġġja l-bqija. Waħda mill-aħjar li tuża hija Flask u l-kontroparti tiegħu tal-ASGI Quart. Tiegħi stess Qafas PaferaPy huwa mibni fuq il-Flask, għalhekk jiena naf sew kemm hu faċli li l-affarijiet isiru filwaqt li żżomm il-prestazzjoni.
#!/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}'
Kif tistgħu taraw, l-iskrittura tal-Flask hija iqsar mill-iskrittura PHP pura. Insib li mil-lingwi kollha li użajt, Python huwa probabbilment l-aktar lingwa espressiva f'termini ta 'keystrokes ittajpjati. Nuqqas ta' ċineg u parentesi, komprensjonijiet tal-lista u tad-ditta, u l-imblukkar ibbażat fuq l-indentazzjoni aktar milli fuq il-punti u virgola jagħmlu lil Python pjuttost sempliċi iżda b'saħħtu fil-kapaċitajiet tiegħu.
Sfortunatament, Python huwa wkoll l-aktar lingwa għal skopijiet ġenerali bil-mod hemmhekk, minkejja kemm inkiteb softwer fiha. In-numru ta 'libreriji Python disponibbli huwa madwar erba' darbiet aktar minn lingwi simili u jkopri ammont kbir ta 'dominji, iżda ħadd ma jgħid li Python huwa veloċi u lanqas performant barra minn niċeċ bħal NumPy.
Ejja naraw kif il-verżjoni tal-Flask tagħna tqabbel mal-oqfsa preċedenti tagħna.
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
L-iskript tal-Flask tagħna huwa fil-fatt aktar mgħaġġel mill-verżjoni PHP pura tagħna!
Jekk int sorpriż b'dan, għandek tirrealizza li l-app Flask tagħna tagħmel l-inizjalizzazzjoni u l-konfigurazzjoni kollha tagħha meta nibdew is-server tal-gunicorn, filwaqt li PHP jerġa' jesegwixxi l-iskript kull darba li tidħol talba ġdida. Huwa' ;huwa ekwivalenti għal Flask li hu x-xufier tat-taxi żagħżugħ u ħerqan li';diġà beda l-karozza u qed jistenna ħdejn it-triq, filwaqt li PHP huwa s-sewwieq antik li joqgħod id-dar tiegħu jistenna li tidħol sejħa u mbagħad isuq biss. fuq biex jiġbdek. Li tkun raġel ta 'skola antika u ġej mill-jiem fejn PHP kien bidla mill-isbaħ għal fajls HTML u SHTML sempliċi, huwa daqsxejn imdejjaq li tirrealizza kemm għadda żmien, iżda d-differenzi fid-disinn verament jagħmluha diffiċli għall-PHP biex jikkompetu kontra servers Python, Java, u Node.js li jibqgħu biss fil-memorja u jimmaniġġjaw it-talba bil-faċilità ħafifa ta 'juggler.
Flixkun jista 'jkun il-qafas l-aktar mgħaġġel tagħna s'issa, iżda fil-fatt huwa softwer pjuttost antik. Il-komunità Python qalbet għas-servers ASGI asinkroniċi l-aktar ġodda ftit ta 'snin lura, u ovvjament, jien stess qlibt magħhom.
L-aktar verżjoni ġdida tal-Qafas Pafera, PaferaPyAsync , hija bbażata fuq Starlette. Għalkemm hemm verżjoni ASGI ta 'Flask imsejħa Quart, id-differenzi fil-prestazzjoni bejn Quart u Starlette kienu biżżejjed għalija biex nibża l-kodiċi tiegħi fuq Starlette minflok.
L-ipprogrammar asinkroniku jista 'jkun tal-biża' għal ħafna nies, iżda fil-fatt mhuwiex kunċett diffiċli grazzi għall-guys Node.js popolarizzar il-kunċett aktar minn għaxar snin ilu.
Aħna konna niġġieldu l-konkorrenza ma 'multithreading, multiprocessing, kompjuters distribwiti, wegħda katina, u dawk il-ħinijiet kollha divertenti li età qabel iż-żmien u ssikkati ħafna programmaturi veteran. Issa, aħna biss ittajpja async
quddiem il-funzjonijiet tagħna u await
quddiem kwalunkwe kodiċi li jista' jieħu ftit biex jiġi eżegwit. Huwa tabilħaqq aktar verbose minn kodiċi regolari, iżda ħafna inqas tedjanti għall-użu milli jkollhom jittrattaw primitives tas-sinkronizzazzjoni, il-passaġġ tal-messaġġi, u r-riżoluzzjoni tal-wegħdiet.
Il-fajl Starlette tagħna jidher bħal dan:
#!/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",
)
Kif tistgħu taraw, huwa pjuttost ikkupjat u pasted mill-iskript tal-Flask tagħna biss bi ftit bidliet fir-rotot u l- async/await
keywords.
Kemm titjib jista 'kopja u pasted kodiċi verament tagħtina?
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
Għandna champion ġdid, onorevoli u rġulija! L-għoli preċedenti tagħna kien il-verżjoni PHP pura tagħna b'704 talba kull sekonda, li mbagħad inqabeż mill-verżjoni tal-Flask tagħna b'1080 talba kull sekonda. L-iskrittura Starlette tagħna tfarrak lill-kontendenti preċedenti kollha b'4562 talba kull sekonda, li jfisser titjib 6x fuq PHP pur u titjib 4x fuq Flask.
Jekk għadek ma biddiltx il-kodiċi WSGI Python tiegħek għal ASGI, issa jista' jkun żmien tajjeb biex tibda.
S'issa, koprejna biss oqfsa PHP u Python. Madankollu, parti kbira tad-dinja fil-fatt tuża Java, DotNet, Node.js, Ruby on Rails, u teknoloġiji oħra bħal dawn għall-websajts tagħhom. Din bl-ebda mod mhi ħarsa ġenerali komprensiva tal-ekosistemi u l-bijomi kollha tad-dinja, għalhekk biex nevitaw li nagħmlu l-ekwivalenti tal-ipprogrammar tal-kimika organika, aħna se nagħżlu biss l-oqfsa li huma l-aktar faċli biex tittajpja kodiċi għalihom.. . li Java żgur mhux.
Sakemm ma kontx qed taħbi taħt il-kopja tiegħek tal-K&R C jew Knuth's L-Arti tal-Programmazzjoni tal-Kompjuter għal dawn l-aħħar ħmistax-il sena, inti'probabbilment smajt b'Node.js. Dawk minna li ilhom madwar mill-bidu ta 'JavaScript jew huma oerhört mbeżżgħin, étonné, jew it-tnejn bl-istat tal-JavaScript modern, iżda m'hemm l-ebda rifjut li JavaScript sar forza li għandha titqies fuq is-servers ukoll bħala browsers. Wara kollox, għandna saħansitra interi indiġeni ta' 64 bit issa fil-lingwa! Dak huwa ferm aħjar minn dak kollu li jkun maħżun f'sufruni 64 bit bil-bosta!
ExpressJS huwa probabbilment l-eħfef server Node.js biex jintuża, għalhekk aħna'na nagħmlu app Node.js/ExpressJS malajr u maħmuġin biex iservu l-counter tagħna.
/**********************************************************************
* 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}`));
Dan il-kodiċi kien fil-fatt aktar faċli biex tikteb mill-verżjonijiet Python, għalkemm JavaScript nattiv isir pjuttost diffiċli meta l-applikazzjonijiet isiru akbar, u t-tentattivi kollha biex jikkoreġu dan bħal TypeScript malajr isiru aktar verbose minn Python.
Ejja naraw kif dan iwettaq!
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
Jista' jkun li smajt rakkonti tal-qedem (antik skont l-istandards tal-Internet xorta...) dwar Node.js' veloċità, u dawk l-istejjer huma l-aktar veri grazzi għax-xogħol spettakolari li Google għamel bil-magna V8 JavaScript. F'dan il-każ għalkemm, għalkemm l-app ta 'malajr tagħna tisboq l-iskrittura tal-Flask, in-natura unika tagħha bil-kamin hija megħluba mill-erba' proċessi asinkroniċi mħaddma mill-Kavallier Starlette li jgħid "Ni!".
Ejja nġibu aktar għajnuna!
╰─➤ 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 │
└────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
Tajjeb! Issa huwa battalja erbgħa fuq erba! Ejja's benchmark!
╰─➤ 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
Għadu mhux pjuttost fil-livell ta 'Starlette, iżda mhux ħażin għal hack ta' JavaScript ta 'ħames minuti malajr. Mill-ittestjar tiegħi stess, dan l-iskript fil-fatt qed jinżamm ftit lura fil-livell tal-interfacing tad-database minħabba li node-postgres mhu qrib daqshekk effiċjenti daqs psycopg għal Python. Taqleb għal sqlite peress li s-sewwieq tad-database jagħti aktar minn 3000 talba kull sekonda għall-istess kodiċi ExpressJS.
Il-ħaġa ewlenija li għandek tinnota hija li minkejja l-veloċità ta 'eżekuzzjoni bil-mod ta' Python, l-oqfsa ASGI jistgħu fil-fatt ikunu kompetittivi b'soluzzjonijiet Node.js għal ċertu xogħol.
Allura issa, qed noqorbu lejn il-quċċata tal-muntanji, u bil-muntanji, nifhem l-ogħla punteġġi ta 'referenza rreġistrati mill-ġrieden u l-irġiel bl-istess mod.
Jekk tħares lejn il-biċċa l-kbira tal-benchmarks tal-qafas disponibbli fuq il-web, tinnota li hemm żewġ lingwi li għandhom tendenza li jiddominaw il-quċċata: C++ u Rust. Jien ħdimt mas-C++ mis-snin 90, u anke kelli l-qafas tiegħi Win32 C++ lura qabel ma l-MFC/ATL kienet xi ħaġa, għalhekk għandi ħafna esperjenza bil-lingwa. Mhuwiex pjaċevoli li taħdem ma 'xi ħaġa meta diġà tafha, għalhekk aħna se nagħmlu verżjoni Rust minflok. ;)
Rust huwa relattivament ġdid f'dak li għandu x'jaqsam mal-lingwi tal-ipprogrammar, iżda sar oġġett ta' kurżità għalija meta Linus Torvalds ħabbar li kien se jaċċetta Rust bħala lingwa tal-programmazzjoni tal-kernel Linux. Għalina l-programmaturi anzjani, dak's madwar l-istess bħal ngħidu li din il-ħaġa hippie ġdida fangled new age kienet se tkun emenda ġdida għall-Kostituzzjoni tal-Istati Uniti.
Issa, meta int programmatur b'esperjenza, għandek it-tendenza li ma tiżdiedx fuq il-bandwagon malajr daqs kemm jagħmlu n-nies iżgħar, jew inkella tista 'tinħaraq minn bidliet rapidi fil-lingwa jew fil-libreriji. (Kull min uża l-ewwel verżjoni ta 'AngularJS se jkun jaf x'qed nitkellem dwar.) Rust għadu kemmxejn f'dak l-istadju ta 'żvilupp sperimentali, u nsibha umoristiċi li dawk l-eżempji ta' kodiċi ħafna fuq il-web lanqas ma' jikkompila aktar mal-verżjonijiet kurrenti ta 'pakketti.
Madankollu, il-prestazzjoni murija mill-applikazzjonijiet Rust ma tistax tiġi miċħuda. Jekk qatt ma ppruvajt ripgrep jew fd-sib fuq siġar kbar tal-kodiċi tas-sors, għandek definittivament tagħtihom spin. Huma anke disponibbli għall-biċċa l-kbira tad-distribuzzjonijiet tal-Linux sempliċement mill-maniġer tal-pakketti. Inti qed tiskambja verbosity għall-prestazzjoni ma Rust... a lott ta' verbosity għal a lott tal-prestazzjoni.
Il-kodiċi komplut għal Rust huwa daqsxejn kbir, għalhekk aħna'na nagħtu ħarsa lejn il-handlers rilevanti hawn:
// =====================================================================
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
)))
}
Dan huwa ħafna aktar ikkumplikat mill-verżjonijiet Python/Node.js...
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
U ħafna aktar performant!
Is-server Rust tagħna li juża Actix/deadpool_postgres jegħleb b'mod faċli ċ-champion preċedenti tagħna Starlette b'+125%, ExpressJS b'+362%, u PHP pur b'+1366%. (Jien inħalli d-delta tal-prestazzjoni bil-verżjoni Laravel bħala eżerċizzju għall-qarrej.)
I'sibt li t-tagħlim tal-lingwa Rust innifsu kien aktar diffiċli minn lingwi oħra peress li għandu ħafna aktar gotchas minn kull ħaġa li rajt barra minn 6502 Assembly, imma jekk is-server Rust tiegħek jista 'jieħu 14x in-numru ta' utenti bħala s-server PHP tiegħek, allura forsi hemm xi ħaġa x'jikseb bit-tibdil tat-teknoloġiji wara kollox. Huwa għalhekk li l-verżjoni li jmiss tal-Qafas Pafera se tkun ibbażata fuq Rust. Il-kurva tat-tagħlim hija ħafna ogħla mil-lingwi tal-kitba, iżda l-prestazzjoni tkun worth it. Jekk ma tistax tpoġġi l-ħin biex titgħallem Rust, allura li tibbaża l-munzell tat-teknoloġija tiegħek fuq Starlette jew Node.js lanqas hija deċiżjoni ħażina.
F'dawn l-aħħar għoxrin sena, morna minn siti ta 'hosting statiċi irħas għal hosting kondiviż ma' munzelli LAMP għal kiri ta 'VPSes għal AWS, Azure, u servizzi oħra tal-cloud. Illum il-ġurnata, ħafna kumpaniji huma sodisfatti li jieħdu deċiżjonijiet ta 'disinn ibbażati fuq min jistgħu jsibu li's disponibbli jew irħas peress li l-miġja ta 'servizzi cloud konvenjenti għamluha faċli biex tarmi aktar ħardwer fuq servers u applikazzjonijiet bil-mod. Dan tahom qligħ kbir fuq żmien qasir għall-ispiża ta 'dejn tekniku fit-tul.
70 sena ilu, kien hemm tellieqa spazjali kbira bejn l-Unjoni Sovjetika u l-Istati Uniti. Is-Sovjetiċi rebħu ħafna mill-istadji bikrin. Kellhom l-ewwel satellita fl-Sputnik, l-ewwel kelb fl-ispazju f’Laika, l-ewwel vettura spazjali tal-qamar f’Luna 2, l-ewwel raġel u mara fl-ispazju f’Yuri Gagarin u Valentina Tereshkova, eċċ...
Iżda bil-mod kienu qed jakkumulaw dejn tekniku.
Għalkemm is-Sovjetiċi kienu l-ewwel għal kull waħda minn dawn il-kisbiet, il-proċessi u l-għanijiet tal-inġinerija tagħhom kienu qed iwassluhom biex jiffokaw fuq sfidi għal żmien qasir aktar milli fattibilità fit-tul. Huma rebħu kull darba li qabżu, iżda kienu qed isiru aktar għajjien u aktar bil-mod filwaqt li l-avversarji tagħhom komplew jagħmlu passi konsistenti lejn il-linja finali.
Ladarba Neil Armstrong ħa l-passi storiċi tiegħu fuq il-qamar fuq it-televiżjoni diretta, l-Amerikani ħadu t-tmexxija, u mbagħad baqgħu hemm hekk kif il-programm Sovjetiku batut. Dan mhux differenti minn kumpaniji llum li ffukaw fuq il-ħaġa kbira li jmiss, il-ħlas kbir li jmiss, jew it-teknoloġija kbira li jmiss filwaqt li naqsu milli jiżviluppaw drawwiet u strateġiji xierqa għat-tul.
Li tkun l-ewwel fis-suq ma jfissirx li inti se ssir l-attur dominanti f'dak is-suq. Inkella, li tieħu l-ħin biex tagħmel l-affarijiet sewwa ma tiggarantixxix is-suċċess, iżda ċertament iżid iċ-ċansijiet tiegħek ta’ kisbiet fit-tul. Jekk int il-mexxej teknoloġiku għall-kumpanija tiegħek, agħżel id-direzzjoni u l-għodda t-tajba għall-ammont tax-xogħol tiegħek. Tħallix il-popolarità tissostitwixxi l-prestazzjoni u l-effiċjenza.
Trid tniżżel fajl 7z li fih l-iskripts Rust, ExpressJS, Flask, Starlette, u Pure PHP?
Dwar l-Awtur |
|
![]() |
Jim ilu jipprogramma minn meta kiseb IBM PS/2 lura matul is-snin 90. Sal-lum, għadu jippreferi jikteb HTML u SQL bl-idejn, u jiffoka fuq l-effiċjenza u l-korrettezza fix-xogħol tiegħu. |