No engem vu menge rezenten Jobinterviews war ech iwwerrascht ze realiséieren datt d'Firma fir déi ech ugemellt hunn nach ëmmer Laravel benotzt, e PHP Kader deen ech virun ongeféier engem Joerzéngt probéiert hunn. Et war anstänneg fir d'Zäit, awer wann et eng Konstant an der Technologie a Moud ass, ass et kontinuéierlech Ännerung an d'Resurfacing vu Stiler a Konzepter. Wann Dir e JavaScript Programméierer sidd, sidd Dir wahrscheinlech mat dësem ale Witz vertraut
Programmer 1: "Ech hunn dësen neie JavaScript Kader net gär!"
Programmer 2: "Keng Suergen. Waart just sechs Méint, an et gëtt nach en ersat!"
Aus Virwëtz hunn ech decidéiert genee ze kucken wat geschitt wa mir al an nei op d'Test stellen. Natierlech ass de Web mat Benchmarks a Fuerderungen gefüllt, vun deenen déi populärste wahrscheinlech ass TechEmpower Web Framework Benchmarks hei . Mir wäerten awer näischt bal sou komplizéiert maachen wéi haut. Mir halen d'Saache flott an einfach souwuel sou datt dësen Artikel net verwandelt gëtt Krich a Fridden , an datt Dir eng liicht Chance hutt fir waakreg ze bleiwen an der Zäit wou Dir fäerdeg sidd ze liesen. Déi üblech Virwarnungen gëllen: dëst funktionnéiert vläicht net d'selwecht op Ärer Maschinn, verschidde Softwareversioune kënnen d'Performance beaflossen, an d'Schrödinger's Kaz gouf tatsächlech eng Zombiekaz, déi gläichzäiteg hallef lieweg an hallef dout war.
Fir dësen Test wäert ech mäi Laptop benotzen, bewaffnet mat engem schwaache i5 deen Manjaro Linux leeft wéi hei gewisen.
╰─➤ 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
Eise Code wäert dräi einfach Aufgaben fir all Ufro hunn:
Wat fir eng idiotesch Test ass dat, kënnt Dir froen? Ee, wann Dir op d'Netz Demanden fir dës Säit kucken, Dir wäert mierken een genannt sessionvars.js déi genee déi selwecht Saach mécht.
Dir gesitt, modern Websäite si komplizéiert Kreaturen, an eng vun den heefegsten Aufgaben ass komplex Säiten ze cachen fir iwwerschësseg Belaaschtung op den Datebankserver ze vermeiden.
Wa mir eng komplex Säit all Kéier wann e Benotzer et freet nei maachen, da kënne mir nëmmen ongeféier 600 Benotzer pro Sekonn déngen.
╰─➤ 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
Awer wa mir dës Säit als statesch HTML-Datei cache loossen an Nginx se séier aus der Fënster op de Benotzer werfen, da kënne mir 32.000 Benotzer pro Sekonn déngen, d'Performance vun engem Faktor vun 50x erhéijen.
╰─➤ 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
De statesche index.en.html ass den Deel deen un jiddereen geet, an nëmmen déi Deeler déi vum Benotzer ënnerscheeden ginn an sessionvars.js geschéckt. Dëst reduzéiert net nëmmen d'Datebankbelaaschtung a schaaft eng besser Erfarung fir eis Benotzer, awer reduzéiert och d'Quante Wahrscheinlechkeeten datt eise Server spontan an engem Warp Core Verstouss verdampft wann d'Klingons attackéieren.
De zréckginn Code fir all Kader wäert eng einfach Fuerderung hunn: weist de Benotzer wéi oft se d'Säit erfrëscht hunn andeems se "Count is x" soen. Fir d'Saachen einfach ze halen, bleiwe mir fir de Moment ewech vu Redis Schlaangen, Kubernetes Komponenten oder AWS Lambdas.
All Benotzer Sessiounsdaten ginn an enger PostgreSQL Datebank gespäichert.
An dës Datebank Tabelle gëtt virun all Test ofgeschnidden.
Einfach awer effektiv ass de Pafera Motto ... ausserhalb vun der däischterster Timeline souwisou ...
Okay, also elo kënne mir endlech ufänken eis Hänn dreckeg ze maachen. Mir sprangen de Setup fir Laravel well et just eng Rëtsch Komponist an Handwierker ass commandéiert.
Als éischt setze mir eis Datebank Astellungen an der .env Datei op
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=sessiontest
DB_USERNAME=sessiontest
DB_PASSWORD=sessiontest
Da setzen mir eng eenzeg Fallback Route déi all Ufro un eise Controller schéckt.
Route::fallback(SessionController::class);
A set de Controller fir d'Zuel ze weisen. Laravel, par défaut, späichert Sessiounen an der Datebank. Et bitt och de session()
Funktioun fir mat eise Sessiounsdaten ze interface, also alles wat et gedauert huet war e puer Zeilen Code fir eis Säit ze maachen.
class SessionController extends Controller
{
public function __invoke(Request $request)
{
$count = session('count', 0);
$count += 1;
session(['count' => $count]);
return 'Count is ' . $count;
}
}
Nodeems Dir php-fpm an Nginx opgeriicht hutt, gesäit eis Säit zimlech gutt aus ...
╰─➤ 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
Op d'mannst bis mir d'Testresultater tatsächlech gesinn ...
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
Nee, dat ass keen Tippfeeler. Eis Testmaschinn ass vu 600 Ufroe pro Sekonn gaang fir eng komplex Säit ze maachen ... op 21 Ufroen pro Sekonn Rendering "Count is 1".
Also wat ass falsch gaang? Ass eppes falsch mat eiser PHP Installatioun? Ass Nginx iergendwéi méi lues wann Dir mat php-fpm interfacéiert?
Loosst eis dës Säit am pure PHP Code nei maachen.
<?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'];
Mir hunn elo 98 Zeilen Code benotzt fir ze maachen wat véier Zeilen Code (an eng ganz Rëtsch Konfiguratiounsaarbechten) zu Laravel gemaach hunn. (Natierlech, wa mir richteg Feeler Ëmgank an Benotzer konfrontéiert Messagen gemaach, dëst wier ongeféier duebel d'Zuel vun Linnen.) Vläicht kënne mir et maachen 30 Demanden pro Sekonn?
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! Et gesäit aus wéi wann et näischt falsch mat eiser PHP Installatioun ass. Déi reng PHP Versioun mécht 700 Ufroen pro Sekonn.
Wann et näischt falsch mat PHP ass, hu mir vläicht Laravel falsch konfiguréiert?
Nodeem de Web fir Konfiguratiounsprobleemer a Performance Tipps duerchgefouert gouf, waren zwee vun de populäersten Technike fir d'Configuratioun an d'Routedaten ze cache fir se fir all Ufro ze vermeiden. Dofir wäerte mir hir Rotschléi huelen an dës Tipps probéieren.
╰─➤ php artisan config:cache
INFO Configuration cached successfully.
╰─➤ php artisan route:cache
INFO Routes cached successfully.
Alles gesäit gutt op der Kommandozeil. Loosst eis de Benchmark nei maachen.
╰─➤ 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
Gutt, mir hunn elo d'Performance vun 21,04 op 28,80 Ufro pro Sekonn erhéicht, en dramateschen Uplift vu bal 37%! Dëst wier zimmlech beandrockend fir all Software Package ... ausser datt mir nach ëmmer nëmmen 1/24 vun der Unzuel vun Ufroe vun der purer PHP Versioun maachen.
Wann Dir denkt datt eppes mat dësem Test falsch muss sinn, sollt Dir mam Auteur vum Lucinda PHP Kader schwätzen. A sengen Testresultater huet hien Lucinda schléit Laravel duerch 36x fir HTML Ufroen an 90x fir JSON Ufroen.
Nodeems ech op menger eegener Maschinn mat Apache an Nginx getest hunn, hunn ech kee Grond him ze zweifelen. Laravel ass wierklech just dat lues! PHP vu sech selwer ass net sou schlecht, awer wann Dir all déi extra Veraarbechtung bäidréit, déi Laravel zu all Ufro bäidréit, dann fannen ech et ganz schwéier Laravel als Choix am Joer 2023 ze recommandéieren.
PHP / Wordpress Konte fir iwwer 40% vun all Websäite op de Web , mécht et bei wäitem de dominante Kader. Perséinlech fannen ech awer, datt d'Popularitéit net onbedéngt an d'Qualitéit iwwersetzt, wéi ech en plötzlechen onkontrolléierbaren Drang fir dat aussergewéinlecht Gourmet-Iessen vun de beléifste Restaurant an der Welt ... McDonald & # x27; Well mir scho pure PHP Code getest hunn, wäerte mir Wordpress net selwer testen, well alles wat Wordpress involvéiert wier ouni Zweifel méi niddereg wéi déi 700 Ufroen pro Sekonn, déi mir mat pure PHP observéiert hunn.
Django ass en anere populäre Kader dee scho laang existéiert. Wann Dir et an der Vergaangenheet benotzt hutt, erënnert Dir Iech wahrscheinlech gär un seng spektakulär Datebankverwaltungsinterface zesumme mat wéi lästeg et war alles ze konfiguréieren just wéi Dir wollt. Loosst eis kucken wéi gutt Django am Joer 2023 funktionnéiert, besonnesch mat der neier ASGI Interface, déi et bäigefüügt huet wéi d'Versioun 4.0.
Django opbauen ass bemierkenswäert ähnlech wéi Laravel opzestellen, well se allebéid aus dem Alter waren wou MVC Architekturen stilvoll a korrekt waren. Mir sprangen déi langweileg Konfiguratioun iwwer a ginn direkt fir d'Vue opzestellen.
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}")
Véier Zeilen vum Code ass d'selwecht wéi mat der Laravel Versioun. Loosst eis kucken wéi et funktionnéiert.
╰─➤ 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
guer net schlecht bei 355 Ufroen pro Sekonn. Et ass nëmmen d'Halschent vun der Leeschtung vun der purer PHP Versioun, awer et ass och 12x déi vun der Laravel Versioun. Django vs Laravel schéngt guer kee Concours ze sinn.
Ausser de gréisseren alles-inklusive-de-Kichen-Sink-Frameworks, ginn et och méi kleng Kaderen déi just e puer Basis-Setup maachen, während Dir de Rescht léisst. Ee vun de beschten ze benotzen ass Flask a säin ASGI Kolleg Quart. Meng eegen PaferaPy Kader ass op der Flask gebaut, sou datt ech gutt vertraut sinn wéi einfach et ass fir Saachen ze maachen wärend d'Leeschtung behalen.
#!/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}'
Wéi Dir kënnt gesinn, ass de Flask Skript méi kuerz wéi de pure PHP Skript. Ech fannen datt aus all de Sproochen déi ech benotzt hunn, Python ass wahrscheinlech déi expressivst Sprooch wat d'Tastekombinatiounen ugeet. Mangel u Klameren a Klammern, Lëscht an Diktat Verständnis, a Blockéierung baséiert op Indentatioun anstatt Semikolon maachen de Python zimlech einfach awer mächteg a senge Fäegkeeten.
Leider ass Python och déi luesst allgemeng Zweck Sprooch dobaussen, trotz wéi vill Software dra geschriwwe gouf. D'Zuel vun de verfügbare Python-Bibliothéiken ass ongeféier véier Mol méi wéi ähnlech Sproochen an deckt eng grouss Quantitéit un Domainen, awer kee géif soen datt Python séier a performant ausserhalb vun Nischen wéi NumPy ass.
Loosst eis kucken wéi eis Flask Versioun mat eise fréiere Kaderen vergläicht.
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
Eise Flask Skript ass tatsächlech méi séier wéi eis reng PHP Versioun!
Wann Dir iwwerrascht sidd vun dësem, sollt Dir mierken datt eis Flask App all seng Initialiséierung a Konfiguratioun mécht wa mir den Gunicorn Server starten, während PHP de Skript nei ausféiert all Kéier wann eng nei Ufro erakënnt. ;s gläichwäerteg mam Flask dee jonken, ängschtleche Taxichauffer deen den Auto scho gestart huet an nieft der Strooss waart, während PHP den ale Chauffer ass, dee bei sengem Haus bleift fir en Uruff ze waarden an eréischt dann fiert eriwwer fir dech opzehuelen. Eng al Schoul Guy ze sinn a kommen aus den Deeg wou PHP eng wonnerbar Ännerung fir einfach HTML an SHTML Dateien war, et ass e bëssen traureg ze realiséieren wéi vill Zäit vergaang ass, awer d'Designdifferenzen maachen et wierklech schwéier fir PHP Konkurréiere géint Python, Java, an Node.js Serveren déi just an der Erënnerung bleiwen an d'Ufro mat der flësseger Liichtegkeet vun engem Jongléier handelen.
Flask kéint eise schnellsten Kader bis elo sinn, awer et ass tatsächlech zimlech al Software. D'Python Gemeinschaft huet e puer Joer zréck op déi méi nei asychron ASGI Server gewiesselt, an natierlech hunn ech selwer mat hinnen gewiesselt.
Déi lescht Versioun vum Pafera Framework, PaferaPyAsync , baséiert op Starlette. Och wann et eng ASGI Versioun vu Flask genannt Quart gëtt, waren d'Performance Differenzen tëscht Quart a Starlette genuch fir mech mäi Code op Starlette ze rebaséieren amplaz.
Asychron Programméiere kann fir vill Leit erschreckend sinn, awer et ass tatsächlech kee schwéiert Konzept dank den Node.js Kärelen déi d'Konzept virun engem Joerzéngt populariséiert hunn.
Mir hunn d'Konkurrenz mat Multithreading, Multiprocessing, verdeelt Computing, verspriechen Ketten, an all déi lëschteg Zäiten ze kämpfen, déi vill Veteran Programméierer virzäiteg alen an desicéiert hunn. Elo, mir tippen just async
virun eise Funktiounen an await
virun all Code, datt eng Zäit huelen kann auszeféieren. Et ass wierklech méi verbose wéi normale Code, awer vill manner lästeg ze benotzen wéi mat Synchroniséierungsprimitiven ze këmmeren, Message Passage, a Verspriechen ze léisen.
Eis Starlette Datei gesäit esou aus:
#!/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",
)
Wéi Dir gesitt, ass et zimlech kopéiert a gepecht aus eisem Flask Skript mat nëmmen e puer Routing Ännerungen an der async/await
Schlësselwieder.
Wéi vill Verbesserung kann kopéieren a gepaste Code eis wierklech ginn?
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
Mir hunn en neie Champion, Dammen an dir Hären! Eise fréiere Héich war eis reng PHP Versioun bei 704 Ufroen pro Sekonn, déi dunn vun eiser Flask Versioun mat 1080 Ufroen pro Sekonn iwwerholl gouf. Eis Starlette Skript zerstéiert all fréiere Kandidate mat 4562 Ufroen pro Sekonn, dat heescht eng 6x Verbesserung iwwer pure PHP a 4x Verbesserung iwwer Flask.
Wann Dir Äre WSGI Python Code nach net op ASGI geännert hutt, ass elo vläicht eng gutt Zäit fir unzefänken.
Bis elo hu mir nëmmen PHP a Python Kaderen ofgedeckt. Wéi och ëmmer, e groussen Deel vun der Welt benotzt Java, DotNet, Node.js, Ruby on Rails, an aner sou Technologien fir hir Websäiten. Dëst ass op kee Fall eng ëmfaassend Iwwersiicht iwwer all d'Ökosystemer a Biome vun der Welt, also fir d'Programméierungs-Äquivalent vun der organescher Chimie ze vermeiden, wäerte mir nëmmen d'Frame wielen déi am einfachsten sinn fir Code ze tippen. . vun deem Java definitiv net ass.
Ausser Dir hutt Iech ënner Ärer Kopie vu K&R C oder Knuth's verstoppt D'Konscht vum Computerprogramméiere fir déi lescht fofzéng Joer, Dir hutt wahrscheinlech vun Node.js héieren & # x27; Déi vun eis, déi zënter dem Ufank vu JavaScript ronderëm sinn, sinn entweder onheemlech erschreckt, iwwerrascht, oder souwuel um Zoustand vum modernen JavaScript, awer et gëtt kee verleegnen datt JavaScript eng Kraaft gouf fir och op Serveren ze berechnen. als Browser. Iwwerhaapt hu mir souguer gebierteg 64 Bit ganz Zuelen elo an der Sprooch! Dat ass vill besser wéi alles dat bei wäitem a 64-Bit Floats gespäichert gëtt!
ExpressJS ass wahrscheinlech deen einfachsten Node.js Server fir ze benotzen, also maache mir eng séier an dreckeg Node.js/ExpressJS App fir eise Konter ze déngen.
/**********************************************************************
* 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}`));
Dëse Code war tatsächlech méi einfach ze schreiwen wéi d'Python Versiounen, och wann native JavaScript zimlech onbestänneg gëtt wann d'Applikatioune méi grouss ginn, an all Versuche fir dëst ze korrigéieren wéi TypeScript séier méi verbose wéi Python.
Loosst eis kucken wéi dëst funktionnéiert!
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
Dir hutt vläicht antike héieren (antal vun Internet Standarden souwisou ...) Folktales iwwer Node.js & # x27; Geschwindegkeet, an déi Geschichte si meeschtens richteg dank der spektakulärer Aarbecht déi Google mam V8 JavaScript-Motor gemaach huet. An dësem Fall awer, obwuel eis Schnell App de Flask Skript besser ass, ass seng eenzeg threaded Natur vun de véier Async Prozesser besiegt, déi vum Starlette Knight ausgeübt ginn, deen "Ni!" seet.
Loosst eis e bësse méi Hëllef kréien!
╰─➤ 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 │
└────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
Okay! Elo ass et e souguer véier op véier Schluecht! Loosst eis benchmarken!
╰─➤ 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
Nach net ganz um Niveau vun Starlette, mee et ass net schlecht fir eng séier fënnef Minutt JavaScript Hack. Vu menger eegener Tester gëtt dëst Skript tatsächlech e bëssen op der Datebank-Interfacing-Niveau zréckgehal well Node-postgres néierens sou effizient ass wéi psycopg fir Python ass. Wiesselen op sqlite als Datebank Chauffer bréngt iwwer 3000 Ufroe pro Sekonn fir deeselwechten ExpressJS Code.
D'Haapt Saach ze notéieren ass datt trotz der lueser Ausféierungsgeschwindegkeet vum Python, ASGI Frameworks tatsächlech kompetitiv kënne mat Node.js Léisunge fir bestëmmte Workloads sinn.
Also elo komme mir méi no un d'Spëtzt vum Bierg, a mam Bierg mengen ech déi héchste Benchmarkscores, déi vu Mais a Männer opgeholl goufen.
Wann Dir déi meescht vun de Framework Benchmarks kuckt, déi um Internet verfügbar sinn, mierkt Dir datt et zwou Sproochen sinn déi d'Spëtzt dominéieren: C ++ a Rust. Ech hu mat C ++ zënter den 90er Joren geschafft, an ech hat souguer meng eegen Win32 C ++ Kader zréck ier MFC / ATL eng Saach war, also hunn ech vill Erfahrung mat der Sprooch. Et ass net vill Spaass mat eppes ze schaffen wann Dir et scho wësst, also wäerte mir eng Rust Versioun maachen amplaz. ;)
Rust ass relativ nei sou wäit wéi d'Programméierungssprooche goen, awer et gouf en Objet vu Virwëtzegkeet fir mech wann de Linus Torvalds ugekënnegt huet datt hien Rust als Linux Kernel Programmiersprache géif akzeptéieren. Fir eis eeler Programméierer, dat ass ongeféier d'selwecht wéi ze soen datt dës nei fangled New Age Hippie Saach eng nei Ännerung vun der US Verfassung wäert sinn.
Elo, wann Dir en erfuerene Programméierer sidd, tendéiert Dir net sou séier op de Wagon ze sprangen wéi déi méi jonk Leit, oder soss kënnt Dir duerch séier Ännerunge vun der Sprooch oder Bibliothéiken verbrannt ginn. (Jiddereen deen déi éischt Versioun vun AngularJS benotzt, weess wat ech schwätzen iwwer.) Rust ass nach e bëssen an där experimentell Entwécklung Etapp, an ech fannen et witzeg, datt vill Code Beispiller op de Web net emol & # 8220; kompiléiere méi mat aktuellen Versioune vu Packagen.
Wéi och ëmmer, d'Performance gewisen vu Rust Uwendungen kann net ofgeleent ginn. Wann Dir ni probéiert hutt ripgrep oder fd fannen eraus op grouss Quelltext Beem, Dir sollt hinnen definitiv e spin ginn. Si si souguer verfügbar fir déi meescht Linux Verdeelungen einfach vum Package Manager. Dir austauscht Verbositéit fir Leeschtung mat Rust ... a vill vun Verbositéit fir a vill vun Leeschtung.
De komplette Code fir Rust ass e bësse grouss, also wäerte mir just déi relevant Handler hei kucken:
// =====================================================================
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
)))
}
Dëst ass vill méi komplizéiert wéi d'Python/Node.js Versiounen ...
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
A vill méi performant!
Eise Rust Server mat Actix/deadpool_postgres schléit praktesch eise fréiere Champion Starlette mat +125%, ExpressJS mat +362%, a pure PHP ëm +1366%. (Ech loossen d'Performance Delta mat der Laravel Versioun als Übung fir de Lieser.)
Ech hu festgestallt datt d'Rust Sprooch selwer ze léieren méi schwéier war wéi aner Sproochen well et vill méi Gotchas huet wéi alles wat ech ausserhalb vun der 6502 Assemblée gesinn hunn, awer wann Äre Rust Server 14x d'Zuel vun Benotzer als Äre PHP-Server, dann ass et vläicht nach eppes ze gewannen mat Schalttechnologien. Dofir wäert déi nächst Versioun vum Pafera Framework op Rust baséieren. D'Léierkurve ass vill méi héich wéi Skriptsproochen, awer d'Leeschtung wäert et wäert sinn. Wann Dir d'Zäit net setzt fir Rust ze léieren, dann ass Ären Tech Stack op Starlette oder Node.js och keng schlecht Entscheedung ze baséieren.
An de leschten zwanzeg Joer si mir vu bëllegen statesche Hosting-Siten op Shared Hosting mat LAMP-Stacken gaang fir VPSen op AWS, Azure an aner Cloud-Servicer ze lounen. Hautdesdaags si vill Firmen zefridden mat Designentscheedungen ze huelen baséiert op wien se fannen dat ass verfügbar oder bëllegst zënter dem Advent vu praktesche Cloud-Servicer et einfach gemaach hunn méi Hardware op luesen Serveren an Uwendungen ze werfen. Dëst huet hinnen grouss kuerzfristeg Gewënn op Käschte vun laangfristeg technesch Schold ginn.
Virun 70 Joer gouf et eng grouss Weltraumrace tëscht der Sowjetunioun an den USA. D'Sowjets hunn déi meescht vun de fréie Meilesteen gewonnen. Si haten den éischte Satellit am Sputnik, den éischten Hond am Weltraum zu Laika, déi éischt Moundraumschëff am Luna 2, den éischte Mann a Fra am Weltraum am Yuri Gagarin a Valentina Tereshkova, a sou weider ...
Awer si hu lues a lues technesch Scholden accumuléiert.
Och wann d'Sowjets als éischt fir all dës Erreeche waren, hunn hir Ingenieursprozesser an Ziler dozou gefouert datt se op kuerzfristeg Erausfuerderunge konzentréieren anstatt laangfristeg Machbarkeet. Si gewannen all Kéier wann se sprangen, awer si gi méi midd a méi lues, während hir Géigner konsequent Schrëtt op d'Arrivée maachen.
Eemol den Neil Armstrong seng historesch Schrëtt um Mound op Live Fernseh gemaach huet, hunn d'Amerikaner d'Féierung geholl, an dunn do bliwwen wéi de sowjetesche Programm gefall ass. Dëst ass net anescht wéi Firmen haut, déi sech op déi nächst grouss Saach, déi nächst grouss Ausbezuelung, oder déi nächst grouss Technologie konzentréiert hunn, wärend se net passend Gewunnechten a Strategien fir laang Strecken entwéckelen.
Als éischt um Maart ze sinn heescht net datt Dir den dominante Spiller op deem Maart gëtt. Alternativ, d'Zäit ze huelen fir d'Saachen richteg ze maachen, garantéiert net Erfolleg, awer erhéicht sécher Är Chancen op laangfristeg Leeschtungen. Wann Dir den Tech Lead fir Är Firma sidd, wielt déi richteg Richtung an Tools fir Är Aarbechtsbelaaschtung. Loosst d'Popularitéit net Leeschtung an Effizienz ersetzen.
Wëllt Dir eng 7z Datei eroflueden mat de Rust, ExpressJS, Flask, Starlette, a Pure PHP Scripten?
Iwwer den Auteur |
|
![]() |
Den Jim huet programméiert zënter hien en IBM PS/2 an den 90er krut. Bis haut huet hien nach léiwer HTML a SQL mat der Hand ze schreiwen, a konzentréiert sech op Effizienz a Korrektheet a senger Aarbecht. |