Pärast ühte oma viimastest tööintervjuudest sain üllatusega aru, et ettevõte, kuhu kandideerisin, kasutas endiselt Laravelit, PHP raamistikku, mida proovisin kümmekond aastat tagasi. See oli selle aja kohta korralik, kuid kui nii tehnoloogias kui ka moes on üks konstant, siis on see stiilide ja kontseptsioonide pidev muutumine ja uuesti esile kerkimine. Kui olete JavaScripti programmeerija, on see vana nali teile ilmselt tuttav
Programmeerija 1: "Mulle ei meeldi see uus JavaScripti raamistik!"
Programmeerija 2: "Pole vaja muretseda. Lihtsalt oodake kuus kuud ja selle asemele tuleb mõni teine!"
Huvi pärast otsustasin vaadata, mis täpselt juhtub, kui paneme proovile vana ja uue. Loomulikult on veeb täis võrdlusaluseid ja väiteid, millest populaarseim on ilmselt TechEmpower Web Frameworki võrdlusnäitajad siin . Kuid me ei tee täna midagi nii keerulist kui nemad. Hoiame asjad kena ja lihtsana, et see artikkel ei muutuks Sõda ja rahu , ja et lugemise lõpetamise ajaks on teil väike võimalus ärkvel püsida. Kehtivad tavalised hoiatused: see ei pruugi teie masinas samamoodi töötada, erinevad tarkvaraversioonid võivad jõudlust mõjutada ja Schrödingeri kassist sai tegelikult zombikass, kes oli pooleldi elus ja pooleldi surnud täpselt samal ajal.
Selle testi jaoks kasutan oma sülearvutit, mis on varustatud väikese i5-ga, milles töötab Manjaro Linux, nagu siin näidatud.
╰─➤ 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
Meie koodil on iga päringu jaoks kolm lihtsat ülesannet:
Mis idiootne test see on, võite küsida? Noh, kui vaatate selle lehe võrgupäringuid, märkate seda nimega sessionvars.js, mis teeb täpselt sama asja.
Näete, tänapäevased veebilehed on keerulised olendid ja üks levinumaid ülesandeid on keerukate lehtede vahemällu salvestamine, et vältida andmebaasiserveri liigset koormust.
Kui renderdame keeruka lehe uuesti iga kord, kui kasutaja seda nõuab, saame teenindada ainult umbes 600 kasutajat sekundis.
╰─➤ 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
Kui aga hoiame selle lehe vahemällu staatilise HTML-failina ja laseme Nginxil selle kiiresti kasutajale aknast välja visata, saame teenindada 32 000 kasutajat sekundis, suurendades jõudlust 50 korda.
╰─➤ 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
Staatiline index.en.html on see osa, mis läheb kõigile ja faili sessionvars.js saadetakse ainult need osad, mis kasutajati erinevad. See mitte ainult ei vähenda andmebaasi koormust ja loob meie kasutajatele parema kasutuskogemuse, vaid vähendab ka kvanttõenäosust, et meie server aurustub spontaanselt, kui klingonid rünnavad.
Iga raamistiku tagastataval koodil on üks lihtne nõue: näidake kasutajale, mitu korda ta on lehte värskendanud, öeldes: "Arv on x". Asjade lihtsaks hoidmiseks hoiame praegu eemal Redise järjekordadest, Kubernetese komponentidest või AWS-i lambdadest.
Iga kasutaja seansi andmed salvestatakse PostgreSQL-i andmebaasi.
Ja see andmebaasi tabel kärbitakse enne iga testi.
Lihtne, kuid tõhus on Pafera moto... niikuinii väljaspool kõige tumedamat ajajoont...
Olgu, nüüd saame lõpuks hakata käsi määrima. Jätame Laraveli seadistamise vahele, kuna see on vaid hulk heliloojaid ja käsitöölisi käske.
Esiteks seadistame oma andmebaasi sätted .env-failis
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=sessiontest
DB_USERNAME=sessiontest
DB_PASSWORD=sessiontest
Seejärel seame ühe varumarsruudi, mis saadab kõik päringud meie kontrollerile.
Route::fallback(SessionController::class);
Ja seadke kontroller loendust kuvama. Laravel salvestab vaikimisi seansid andmebaasi. Samuti pakub see session()
funktsioon meie seansiandmetega liidestamiseks, nii et meie lehe renderdamiseks kulus vaid paar rida koodi.
class SessionController extends Controller
{
public function __invoke(Request $request)
{
$count = session('count', 0);
$count += 1;
session(['count' => $count]);
return 'Count is ' . $count;
}
}
Pärast php-fpm ja Nginxi seadistamist näeb meie leht päris hea välja...
╰─➤ 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
Vähemalt seni, kuni me tegelikult testi tulemusi näeme...
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
Ei, see ei ole kirjaviga. Meie testmasin on muutunud keeruka lehe renderdamisel 600 päringult sekundis... 21 päringule sekundis renderdades "Arv on 1".
Mis siis valesti läks? Kas meie PHP installiga on midagi valesti? Kas Nginx aeglustub kuidagi php-fpm-iga liidestamisel?
Teeme selle lehe puhtas PHP-koodis uuesti.
<?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'];
Oleme nüüd kasutanud 98 koodirida, et teha seda, mida neli koodirida (ja terve hulk konfiguratsioonitöid) Laravelis tegid. (Muidugi, kui teeksime õigesti veakäsitlust ja kasutajale suunatud sõnumeid, oleks see umbes kaks korda suurem ridade arv.) Võib-olla suudame saavutada 30 päringut sekundis?
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
Vau! Näib, et meie PHP installimisel pole lõppude lõpuks midagi viga. Puhas PHP versioon teeb 700 päringut sekundis.
Kui PHP-l pole midagi viga, võib-olla konfigureerisime Laraveli valesti?
Pärast veebis konfiguratsiooniprobleemide ja jõudlusnõuannete otsimist olid kaks kõige populaarsemat tehnikat konfiguratsiooni- ja marsruudiandmete vahemällu salvestamine, et vältida nende töötlemist iga päringu puhul. Seetõttu võtame nende nõuandeid arvesse ja proovime neid näpunäiteid järgi.
╰─➤ php artisan config:cache
INFO Configuration cached successfully.
╰─➤ php artisan route:cache
INFO Routes cached successfully.
Käsurea pealt näeb kõik hästi välja. Teeme võrdlusaluse uuesti.
╰─➤ 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
Noh, oleme nüüd suurendanud jõudlust 21.04-lt 28.80 päringule sekundis, mis on dramaatiline tõus peaaegu 37%! See oleks iga tarkvarapaketi puhul üsna muljetavaldav... välja arvatud asjaolu, et me teeme endiselt vaid 1/24 puhta PHP versiooni päringute arvust.
Kui arvate, et selle testiga peab midagi valesti olema, peaksite rääkima Lucinda PHP raamistiku autoriga. Oma testitulemustes on tal Lucinda lööb Laraveli 36x HTML-päringute ja 90x JSON-päringute puhul.
Pärast oma masinas katsetamist nii Apache kui ka Nginxiga pole mul põhjust temas kahelda. Laravel on tõesti õiglane et aeglane! PHP iseenesest pole nii halb, kuid kui lisate kogu lisatöötluse, mille Laravel igale päringule lisab, on mul väga raske Laravelit 2023. aastal valikuna soovitada.
PHP/Wordpress kontod umbes 40% kõigist veebis leiduvatest veebisaitidest , muutes selle kõige domineerivamaks raamistikuks. Isiklikult leian aga, et populaarsus ei tähenda tingimata kvaliteeti, kuna tunnen end äkilise kontrollimatu soovi järele selle erakordse gurmeetoidu järele. maailma populaarseim restoran ... McDonald's. Kuna oleme puhast PHP-koodi juba testinud, ei hakka me Wordpressi ennast testima, kuna kõik Wordpressiga seotud toimingud on kahtlemata madalam kui 700 päringut sekundis, mida me puhta PHP puhul täheldasime.
Django on veel üks populaarne raamistik, mis on olnud juba pikka aega. Kui olete seda varem kasutanud, mäletate tõenäoliselt hea sõnaga selle suurepärast andmebaasihaldusliidest ja seda, kui tüütu oli kõike täpselt nii, nagu soovite, konfigureerida. Vaatame, kui hästi Django 2023. aastal töötab, eriti uue ASGI liidesega, mille ta on alates versioonist 4.0 lisanud.
Django seadistamine on märkimisväärselt sarnane Laraveli seadistamisega, kuna mõlemad olid pärit ajast, mil MVC arhitektuurid olid stiilsed ja korrektsed. Jätame igava konfiguratsiooni vahele ja asume otse vaate seadistamise juurde.
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}")
Neli koodirida on sama, mis Laraveli versioonil. Vaatame, kuidas see toimib.
╰─➤ 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
Pole üldse paha, 355 päringut sekundis. See on vaid poole väiksem kui puhta PHP versiooni jõudlus, kuid see on ka 12 korda suurem kui Laraveli versioon. Tundub, et Django vs Laravel pole üldse võistlus.
Peale suuremate kõikehõlmavate raamistike, sealhulgas köögivalamu, on ka väiksemaid raamistikke, mis teevad vaid põhiseadistuse, lasevad teil ülejäänuga hakkama saada. Üks parimaid, mida kasutada, on Flask ja selle ASGI vaste Quart. Minu oma PaferaPy raamistik on üles ehitatud Flaski peale, nii et olen hästi kursis sellega, kui lihtne on jõudlust säilitades asju ajada.
#!/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}'
Nagu näete, on Flask skript lühem kui puhas PHP skript. Leian, et kõigist minu kasutatud keeltest on Python ilmselt kõige väljendusrikkam keel klahvivajutuste osas. Sulgude ja sulgude puudumine, loendi ja dikteerimise mõistmine ning blokeerimine, mis põhineb pigem taandel kui semikoolonil, muudavad Pythoni oma võimalustelt üsna lihtsaks, kuid võimsaks.
Kahjuks on Python ka kõige aeglasem üldotstarbeline keel, hoolimata sellest, kui palju sellesse tarkvara on kirjutatud. Saadaolevate Pythoni teekide arv on umbes neli korda suurem kui sarnastel keeltel ja hõlmab suurt hulka domeene, kuid keegi ei ütleks, et Python on kiire ega toimiv väljaspool selliseid nišše nagu NumPy.
Vaatame, kuidas meie Flaski versioon on võrreldes meie eelmiste raamistikega.
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
Meie Flask skript on tegelikult kiirem kui meie puhas PHP versioon!
Kui olete sellest üllatunud, peaksite mõistma, et meie rakendus Flask teeb kogu oma lähtestamise ja seadistamise, kui käivitame gunicorn serveri, samal ajal kui PHP käivitab skripti uuesti iga kord, kui uus päring saabub. See' ; võrdub sellega, et Flask on noor innukas taksojuht, kes on juba auto käivitanud ja ootab tee ääres, samas kui PHP on vana juht, kes jääb oma majja ootama kõnet ja alles siis sõidab sulle järele tulla. Olles vana kooli mees ja pärit aegadest, mil PHP oli imeline muutus tavalistele HTML- ja SHTML-failidele, on natuke kurb tõdeda, kui palju aega on möödunud, kuid disainierinevused muudavad PHP jaoks tõesti raskeks Võistelda Pythoni, Java ja Node.js serveritega, mis jäävad lihtsalt mällu ja tegelevad päringutega žonglööri krapsakas kerguses.
Flask võib olla meie seni kiireim raamistik, kuid tegelikult on see üsna vana tarkvara. Pythoni kogukond läks paar aastat tagasi üle uuematele asünkroonsetele ASGI serveritele ja loomulikult olen ka mina ise nendega koos.
Pafera Frameworki uusim versioon, PaferaPyAsync , põhineb Starlette'il. Kuigi Flaskist on ASGI versioon nimega Quart, piisas Quart ja Starlette'i jõudluse erinevustest, et saaksin oma koodi hoopis Starlette'ile tugineda.
Asünkroonne programmeerimine võib paljudele inimestele hirmutada, kuid tänu Node.js-i poistele, kes seda kontseptsiooni üle kümne aasta tagasi populariseerisid, pole see tegelikult keeruline.
Võitlesime samaaegsuse vastu mitme lõimestamise, multitöötluse, hajutatud andmetöötluse, lubaduste aheldamise ja kõigi nende lõbusate aegadega, mis paljud veteranprogrammeerijad enneaegselt vananesid ja kuivasid. Nüüd me lihtsalt kirjutame async
meie funktsioonide ees ja await
mis tahes koodi ees, mille käivitamine võib veidi aega võtta. See on tõepoolest paljusõnalisem kui tavaline kood, kuid palju vähem tüütu kasutada kui sünkroonimisprimitiivide, sõnumite edastamise ja lubaduste lahendamisega tegelemine.
Meie Starlette fail näeb välja selline:
#!/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",
)
Nagu näete, on see meie Flaski skriptist üsna palju kopeeritud ja kleebitud vaid paari marsruudimuudatusega ja async/await
märksõnad.
Kui palju paranemist võib kopeerida ja kleebitud kood meile tegelikult anda?
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
Meil on uus meister, daamid ja härrad! Meie eelmine tipp oli meie puhas PHP-versioon kiirusega 704 päringut sekundis, millest möödus meie Flask versioon kiirusega 1080 päringut sekundis. Meie Starlette'i skript purustab kõik varasemad võistlejad kiirusega 4562 päringut sekundis, mis tähendab 6-kordset paranemist puhta PHP-ga võrreldes ja 4-kordset paranemist võrreldes Flaskiga.
Kui te pole veel oma WSGI Pythoni koodi ASGI-ks muutnud, võib nüüd olla õige aeg alustada.
Seni oleme käsitlenud ainult PHP ja Pythoni raamistikke. Kuid suur osa maailmast kasutab oma veebisaitide jaoks tegelikult Java, DotNet, Node.js, Ruby on Rails ja muid selliseid tehnoloogiaid. See ei ole kaugeltki kõikehõlmav ülevaade kõigist maailma ökosüsteemidest ja bioomidest, nii et vältimaks orgaanilise keemia programmeerimise ekvivalendi tegemist, valime ainult need raamistikud, mille jaoks koodi on kõige lihtsam sisestada. millest Java kindlasti ei ole.
Välja arvatud juhul, kui olete oma K&R C või Knuth'i koopia alla peitnud. Arvutiprogrammeerimise kunst viimase viieteistkümne aasta jooksul olete ilmselt Node.js-ist kuulnud. Need meist, kes oleme olnud JavaScripti algusest peale, on kas uskumatult ehmunud, üllatunud või mõlemad tänapäeva JavaScripti oleku üle, kuid ei saa eitada, et JavaScript on muutunud ka serverites jõuks, millega tuleb arvestada. brauseritena. Lõppude lõpuks on meil praegu keeles isegi 64-bitised täisarvud! See on palju parem, kui kõike seda, mida hoitakse 64-bitistes ujukites!
ExpressJS on tõenäoliselt kõige lihtsamini kasutatav Node.js-server, seega teeme oma loenduri teenindamiseks kiire ja määrdunud rakenduse Node.js/ExpressJS.
/**********************************************************************
* 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}`));
Seda koodi oli tegelikult lihtsam kirjutada kui Pythoni versioone, kuigi natiivne JavaScript muutub rakenduste suuremaks muutumisel üsna kohmakaks ja kõik katsed seda parandada, näiteks TypeScript, muutuvad Pythonist kiiresti paljusõnalisemaks.
Vaatame, kuidas see toimib!
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
Võib-olla olete kuulnud iidseid (Interneti standardite järgi iidseid) rahvajutte Node.js' kiirus ja need lood on enamasti tõesed tänu suurejoonelisele tööle, mida Google on V8 JavaScripti mootoriga teinud. Kuigi meie kiirrakendus ületab Flask-skripti, on selle ühelõimeline olemus sel juhul lüüa saanud nelja asünkroonimisprotsessiga, mida kasutab Starlette Knight, kes ütleb "Ni!".
Saame veel abi!
╰─➤ 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 │
└────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
Olgu! Nüüd on tasavägine neli nelja vastu! Teeme võrdlusaluse!
╰─➤ 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
Ikka mitte päris Starlette'i tasemel, kuid kiireks viieminutiliseks JavaScripti häkkimiseks pole see halb. Minu enda testimise põhjal on see skript tegelikult andmebaasi liidese tasemel veidi tagasi hoitud, sest node-postgres pole kaugeltki nii tõhus kui psycopg Pythoni jaoks. Andmebaasi draiverina sqlite'ile lülitumine annab sama ExpressJS-koodi kohta üle 3000 päringu sekundis.
Peamine asi, mida tuleb tähele panna, on see, et hoolimata Pythoni aeglasest täitmiskiirusest võivad ASGI-raamistikud teatud töökoormuste puhul olla Node.js-i lahendustega konkurentsivõimelised.
Nüüd jõuame mäetipule lähemale ja mäe all pean silmas nii hiirte kui ka meeste kõrgeimat võrdlusalust.
Kui vaatate enamikku veebis saadaolevatest raamistiku võrdlusnäitajatest, märkate, et tipptasemel domineerivad kaks keelt: C++ ja Rust. Olen töötanud C++-ga alates 90ndatest ja mul oli isegi oma Win32 C++ raamistik juba enne seda, kui MFC/ATL-i asja oli, nii et mul on keelega palju kogemusi. Millegiga töötamine ei ole kuigi lõbus, kui seda juba tead, seega teeme selle asemel Rusti versiooni. ;)
Rust on programmeerimiskeelte osas suhteliselt uus, kuid see sai minu jaoks uudishimulikuks, kui Linus Torvalds teatas, et nõustub Rustiga Linuxi tuuma programmeerimiskeelena. Meie, vanemate programmeerijate jaoks on see umbes sama, mis öelda, et see uus fantastiline uue aja hipivärk on USA põhiseaduse uus muudatus.
Nüüd, kui olete kogenud programmeerija, ei hüppa te tavaliselt nii kiiresti kui nooremad inimesed, vastasel juhul võite keele või raamatukogude kiirete muutuste tõttu põletada. (Igaüks, kes kasutas AngularJS-i esimest versiooni, teab, millest ma räägin.) Rust on veel mõnevõrra selles eksperimentaalses arendusetapis ja minu arvates on naljakas, et paljud veebis olevad koodinäited ei ole isegi sellised. enam kompileerida pakettide praeguste versioonidega.
Siiski ei saa eitada Rusti rakenduste näidatud jõudlust. Kui te pole kunagi proovinud ripgrep või fd-leida suurtel lähtekoodipuudel, peaksite neid kindlasti proovima. Need on saadaval isegi enamiku Linuxi distributsioonide jaoks lihtsalt paketihaldurist. Vahetate paljusõnalisust Rustiga esinemise vastu... a palju paljusõnalisus a jaoks palju jõudlusest.
Täielik Rusti kood on natuke suur, nii et vaatame siin lihtsalt asjakohaseid töötlejaid:
// =====================================================================
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
)))
}
See on palju keerulisem kui Python/Node.js versioonid...
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
Ja palju tulemuslikum!
Meie Rust-server, mis kasutab Actix/deadpool_postgres'i, edestab meie eelmist meistrit Starlette'i +125%, ExpressJS-i +362% ja puhast PHP-d +1366%. (Jätan jõudluse delta Laraveli versiooniga lugejale harjutuseks.)
Olen avastanud, et Rusti keele õppimine on olnud teistest keeltest keerulisem, kuna sellel on palju rohkem probleeme kui mis tahes muus, mida olen näinud väljaspool 6502 Assemblyt, kuid kui teie Rusti server suudab vastu võtta 14 korda rohkem kasutajaid teie PHP-serverina, siis võib-olla on tehnoloogia vahetamisega siiski midagi võita. Seetõttu põhineb Pafera Frameworki järgmine versioon Rustil. Õppimiskõver on palju kõrgem kui skriptikeeltel, kuid jõudlus on seda väärt. Kui te ei saa Rusti õppimiseks aega pühendada, pole ka Starlette'i või Node.js'i tehnikakogumi kasutamine halb otsus.
Viimase kahekümne aasta jooksul oleme liikunud odavatelt staatiliselt hostimissaitidelt LAMP-virnadega jagatud hostimiseni kuni VPS-ide rentimiseni AWS-i, Azure'i ja muude pilveteenuste jaoks. Tänapäeval on paljud ettevõtted rahul sellega, et teevad disainiotsuseid selle põhjal, kes nende arvates on saadaval või odavaim, kuna mugavate pilveteenuste tulek on muutnud aeglasesse serverisse ja rakendustesse suurema riistvara viskamise lihtsaks. See on andnud neile suure lühiajalise kasu pikaajalise tehnilise võla hinnaga.
70 aastat tagasi toimus suur kosmosevõistlus Nõukogude Liidu ja USA vahel. Nõukogude võitsid enamiku esimestest verstapostidest. Neil oli esimene satelliit Sputnikus, esimene koer kosmoses Laikas, esimene Kuu-kosmoselaev Luna 2-s, esimene mees ja naine kosmoses Juri Gagarinis ja Valentina Tereškovas ja nii edasi...
Kuid nad kogusid aeglaselt tehnilisi võlgu.
Kuigi nõukogud olid kõigi nende saavutuste ees esimesed, panid nende inseneriprotsessid ja eesmärgid keskenduma pigem lühiajalistele väljakutsetele kui pikaajalisele teostatavusele. Nad võitsid iga kord, kui nad hüppasid, kuid nad muutusid üha väsinud ja aeglasemaks, samal ajal kui nende vastased jätkasid järjekindlaid samme finišijoone poole.
Kui Neil Armstrong astus televisiooni otseülekandes oma ajaloolisi samme Kuul, võtsid ameeriklased juhtrolli ja jäid siis sinna, kui nõukogude programm kõikus. See ei erine tänapäeval ettevõtetest, kes on keskendunud järgmisele suurele asjale, järgmisele suurele väljamaksele või järgmisele suurele tehnoloogiale, kuid ei suuda välja töötada õigeid harjumusi ja strateegiaid pikaks ajaks.
Esimesena turule jõudmine ei tähenda, et teist saab sellel turul domineeriv tegija. Teise võimalusena ei taga asjade õigeks tegemiseks aja võtmine edu, kuid kindlasti suurendab teie võimalusi pikaajaliste saavutuste saavutamiseks. Kui olete oma ettevõtte tehniline juht, valige oma töökoormuse jaoks õige suund ja tööriistad. Ärge laske populaarsusel asendada jõudlust ja tõhusust.
Kas soovite alla laadida 7z-faili, mis sisaldab Rust, ExpressJS, Flask, Starlette ja Pure PHP skripte?
Autori kohta |
|
![]() |
Jim on programmeerimisega tegelenud alates sellest, kui ta 90ndatel sai IBM PS/2. Tänaseni eelistab ta HTML-i ja SQL-i käsitsi kirjutamist ning keskendub oma töös efektiivsusele ja korrektsusele. |