Ya, Virginia, Ana * Ana * a Santa Claus Bedane antarane Web Frameworks ing 2023

Siji programmer sing nantang kanggo nemokake kode server web kanthi cepet sadurunge nyerah marang tekanan pasar lan utang teknis.
2023-03-24 11:52:06
๐Ÿ‘๏ธ 785
๐Ÿ’ฌ 0

Isine

  1. Pambuka
  2. Tes
  3. PHP/Laravel
  4. PHP resik
  5. Ngunjungi maneh Laravel
  6. Django
  7. Flask
  8. Starlette
  9. Node.js/ExpressJS
  10. Karat / Actix
  11. Utang Teknis
  12. Sumber daya

Pambuka

Sawise salah sawijining wawancara kerja sing paling anyar, aku kaget ngerti manawa perusahaan sing daklamar isih nggunakake Laravel, kerangka PHP sing dakcoba udakara sepuluh taun kepungkur. Iku prayoga kanggo wektu, nanging yen ana siji pancet ing teknologi lan fashion padha, iku owah-owahan terus-terusan lan resurfacing saka gaya lan konsep & # x27; Yen sampeyan programmer JavaScript, sampeyan mbokmenawa wis kenal karo guyon lawas iki

Programmer 1: "Aku ora seneng karo kerangka JavaScript anyar iki!"

Programmer 2: "Ora usah kuwatir. Enteni wae nem sasi lan bakal ana siji liyane sing ngganti!"

Amarga penasaran, aku mutusake kanggo ndeleng persis apa sing kedadeyan nalika kita nyoba lawas lan anyar. Mesthine, web kasebut diisi pathokan lan pratelan, sing paling populer yaiku TechEmpower Web Framework Benchmarks kene . Kita ora bakal nindakake apa-apa sing meh rumit kaya saiki. Kita bakal tetep apik lan prasaja supaya artikel iki ora dadi Perang lan Perdamaian , lan sampeyan bakal duwe kasempatan sethitik kanggo tetep siyaga nalika sampeyan wis rampung maca & # x27; Caveats biasane ditrapake: iki bisa uga ora bisa digunakake ing mesin sampeyan, versi piranti lunak sing beda bisa mengaruhi kinerja, lan kucing Schrรถdinger bener-bener dadi kucing zombie sing setengah urip lan setengah mati ing wektu sing padha.

Tes

Lingkungan Pengujian

Kanggo tes iki, aku bakal nggunakake laptopku sing nganggo i5 cilik sing nganggo Manjaro Linux kaya sing ditampilake ing kene.

โ•ฐโ”€โžค  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

Tugas ing Tangan

Kode kita bakal duwe telung tugas sing gampang kanggo saben panyuwunan:

  1. Waca ID sesi pangguna saiki saka cookie
  2. Muat informasi tambahan saka database
  3. Mulihake informasi kasebut menyang pangguna

Apa jenis tes bodho iku, sampeyan bisa takon? Inggih, yen sampeyan ndeleng panjalukan jaringan kanggo kaca iki, sampeyan bakal weruh sing diarani sessionvars.js sing nindakake perkara sing padha.

Isi sessionvars.js

Sampeyan ndeleng, kaca web modern minangka makhluk sing rumit, lan salah sawijining tugas sing paling umum yaiku caching kaca sing kompleks kanggo nyegah beban sing berlebihan ing server database.

Yen kita nggawe maneh kaca sing kompleks saben pangguna njaluk, mula kita mung bisa nglayani sekitar 600 pangguna saben detik.

โ•ฐโ”€โžค  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

Nanging yen kita cache kaca iki minangka file HTML statis lan supaya Nginx cepet mbuwang metu jendhela kanggo pangguna, banjur kita bisa ngawula 32.000 pangguna saben detik, nambah kinerja dening faktor 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

Statis index.en.html bagean sing dadi kanggo everyone, lan mung bagean sing beda-beda dening pangguna dikirim ing sessionvars.js. Iki ora mung nyuda beban database lan nggawe pengalaman sing luwih apik kanggo pangguna, nanging uga nyuda kemungkinan kuantum manawa server bakal nguap kanthi spontan ing pelanggaran inti warp nalika serangan Klingons.

Syarat Kode

Kode bali kanggo saben framework bakal duwe siji syarat prasaja: nuduhake pangguna carane kakehan padha refresh kaca kanthi matur "Count is x". Kanggo tetep prasaja, kita bakal adoh saka antrian Redis, komponen Kubernetes, utawa AWS Lambdas saiki.

Nuduhake kaping pirang-pirang sampeyan wis ngunjungi kaca kasebut

Data sesi saben pangguna bakal disimpen ing database PostgreSQL.

Tabel pangguna

Lan tabel database iki bakal dipotong sadurunge saben tes.

Tabel sawise dipotong

Semboyan Pafera sing prasaja nanging efektif yaiku ... ing njaba garis wektu sing paling peteng ...

Asil Tes Nyata

PHP/Laravel

Oke, dadi saiki kita bisa miwiti njupuk tangan kita reged. Kita bakal ngliwati persiyapan kanggo Laravel amarga mung akeh komposer lan artisan. dhawuh.

Kaping pisanan, kita bakal nyetel setelan basis data ing file .env

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

Banjur kita bakal nyetel siji rute fallback sing ngirim saben panjalukan kanggo controller kita.

Route::fallback(SessionController::class);

Lan nyetel controller kanggo nampilake count. Laravel, minangka standar, nyimpen sesi ing basis data. Iku uga nyedhiyakake session() fungsi kanggo antarmuka karo data sesi kita, supaya mung sawetara baris kode kanggo nerjemahake kaca kita.

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

    $count  += 1;

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

    return 'Count is ' . $count;
  }
}

Sawise nyiyapake php-fpm lan Nginx, kaca kita katon apik banget ...

โ•ฐโ”€โžค  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

Paling ora nganti kita bisa ndeleng asil tes ...

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

Ora, iku dudu typo. Mesin tes kita wis ilang saka 600 panjalukan per detik sing nggawe kaca rumit ... dadi 21 panjaluk per detik rendering "Count is 1".

Dadi apa sing salah? Apa ana sing salah karo instalasi PHP kita? Apa Nginx rada alon nalika interfacing karo php-fpm?

PHP resik

Ayo maneh kaca iki ing kode PHP murni.

<?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'];

Saiki kita wis nggunakake 98 baris kode kanggo nindakake apa papat baris kode (lan akeh karya konfigurasi) ing Laravel. (Mesthi, yen kita nindakake kesalahan sing tepat lan pesen sing diadhepi pangguna, iki kira-kira kaping pindho nomer baris.) Mbok menawa kita bisa nggawe 30 panjalukan saben detik?

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

woy! Kayane ora ana sing salah karo instalasi PHP kita. Versi PHP murni nindakake 700 panjalukan saben detik.

Yen ora ana sing salah karo PHP, bisa uga kita salah ngatur Laravel?

Ngunjungi maneh Laravel

Sawise njelajah web kanggo masalah konfigurasi lan tips kinerja, rong teknik sing paling populer yaiku cache data konfigurasi lan rute supaya ora diproses kanggo saben panyuwunan. Mulane, kita bakal njupuk saran lan nyoba tips iki.

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

   INFO  Configuration cached successfully.  

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

   INFO  Routes cached successfully.  

Kabeh katon apik ing baris printah. Ayo gawe pathokan maneh.

โ•ฐโ”€โžค  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

Saiki, kita wis nambah kinerja saka 21.04 dadi 28.80 panyuwunan per detik, munggah dramatis meh 37%! Iki bakal cukup nyengsemaken kanggo sembarang paket piranti lunak ... kajaba kasunyatan sing kita isih mung nindakake 1/24 saka jumlah panjalukan saka versi PHP murni.

Yen sampeyan mikir yen ana sing salah karo tes iki, sampeyan kudu ngobrol karo penulis kerangka Lucinda PHP. Ing asil tes, dheweke wis Lucinda ngalahake Laravel dening 36x kanggo panjalukan HTML lan 90x kanggo panjalukan JSON.

Sawise nyoba ing mesin dhewe karo Apache lan Nginx, aku ora duwe alesan kanggo mangu-mangu. Laravel pancen mung sing alon! PHP dhewe ora ala, nanging yen sampeyan nambahake kabeh proses ekstra sing ditambahake Laravel kanggo saben panyuwunan, mula angel banget kanggo nyaranake Laravel minangka pilihan ing taun 2023.

Django

Akun PHP/Wordpress kanggo kira-kira 40% kabeh situs web ing web , nggawe framework sing paling dominan. Nanging kanthi pribadi, aku nemokake manawa popularitas ora kudu nerjemahake kualitas luwih saka aku duwe dorongan sing ora bisa dikendhaleni kanthi cepet kanggo panganan gourmet sing luar biasa saka restoran paling populer ing donya ... McDonald & # x27; Amarga kita wis nyoba kode PHP murni, kita ora bakal nyoba Wordpress dhewe, amarga apa wae sing ana gandhengane karo Wordpress mesthi bakal luwih murah tinimbang 700 panjaluk per detik sing diamati nganggo PHP murni.

Django minangka kerangka kerja populer liyane sing wis suwe. Yen sampeyan wis nggunakake ing sasi, sampeyan mbokmenawa fondly ngelingi antarmuka administrasi database spektakuler bebarengan karo carane ngganggu iku kanggo ngatur kabeh mung cara sing pengin. Ayo ndeleng kepiye kerjane Django ing 2023, utamane karo antarmuka ASGI anyar sing ditambahake ing versi 4.0.

Nyiyapake Django pancen padha karo nyetel Laravel, amarga loro-lorone saka umur arsitektur MVC apik lan bener. Kita bakal ngliwati konfigurasi sing mboseni lan langsung nyetel tampilan.

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}")

Patang baris kode padha karo versi Laravel. Ayo ndeleng carane performs & # x27;

โ•ฐโ”€โžค  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

Ora ala ing kabeh panjalukan 355 per detik. Iku mung setengah kinerja versi PHP murni, nanging uga 12x saka versi Laravel & # x27; Django vs. Laravel kayane ora ana kontes.

Flask

Kajaba saka kerangka kabeh-kalebu-pawon-sink sing luwih gedhe, ana uga kerangka sing luwih cilik sing mung nindakake sawetara persiyapan dhasar nalika ngidini sampeyan nangani liyane. Salah siji sing paling apik kanggo digunakake yaiku Flask lan pasangan ASGI Quart. Kula piyambak Kerangka PaferaPy dibangun ing ndhuwur Flask, supaya aku ngerti carane gampang iku rampung nalika njaga kinerja & # x27;

#!/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}'

Kaya sing sampeyan ngerteni, skrip Flask luwih cendhek tinimbang skrip PHP murni. Aku nemokake manawa saka kabeh basa sing wis digunakake, Python bisa uga minangka basa sing paling ekspresif babagan keystrokes sing diketik. Kurang kurung lan kurung, dhaptar lan pangerten dict, lan pamblokiran adhedhasar indentasi tinimbang titik koma nggawe Python luwih gampang nanging kuat ing kemampuane.

Sayange, Python uga minangka basa tujuan umum sing paling alon, sanajan akeh piranti lunak sing wis ditulis. Jumlah perpustakaan Python sing kasedhiya kira-kira kaping papat luwih akeh tinimbang basa sing padha lan nyakup akeh domain, nanging ora ana sing ngomong yen Python cepet utawa performa ing njaba ceruk kaya NumPy.

Ayo ndeleng kepiye versi Flask dibandhingake karo kerangka kerja sadurunge.

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

Skrip Flask kita bener-bener luwih cepet tinimbang versi PHP murni kita!

Yen sampeyan kaget karo iki, sampeyan kudu ngerti manawa aplikasi Flask nindakake kabeh wiwitan lan konfigurasi nalika miwiti server gunicorn, nalika PHP nglakokake maneh skrip saben ana panjaluk anyar. Iku & # x27; Setara karo Flask minangka sopir taksi sing enom lan semangat sing wis miwiti mobil lan ngenteni ing pinggir dalan, nalika PHP minangka sopir tuwa sing nginep ing omahe ngenteni telpon mlebu lan mung nyopir. liwat kanggo njupuk sampeyan. Dadi wong sekolah lawas lan teka saka dina ngendi PHP ana owah-owahan apik kanggo HTML kosong lan file SHTML, iku rada sedih kanggo รฉling carane akeh wektu wis liwati, nanging beda desain tenan nggawe hard kanggo PHP kanggo. saingan karo Python, Jawa, lan server Node.js sing mung tetep ing memori lan nangani request karo ease gesit saka juggler a.

Starlette

Flask bisa dadi kerangka kerja paling cepet nganti saiki, nanging sejatine piranti lunak sing lawas. Komunitas Python ngalih menyang server ASGI asychronous anyar sawetara taun kepungkur, lan mesthi, aku dhewe wis ngalih bebarengan karo wong-wong mau.

Versi paling anyar saka Kerangka Pafera, PaferaPyAsync , adhedhasar Starlette. Senajan ana versi ASGI Flask disebut Quart, beda kinerja antarane Quart lan Starlette cukup kanggo kula kanggo rebase kode marang Starlette tinimbang.

Pemrograman asychronous bisa medeni kanggo akeh wong, nanging sejatine dudu konsep sing angel amarga wong-wong Node.js sing populer babagan konsep kasebut sajrone sepuluh taun kepungkur.

Kita biyen nglawan konkurensi karo multithreading, multiprocessing, komputasi sing disebarake, chaining janji, lan kabeh wektu sing nyenengake sing umure prematur lan ngrusak akeh programer veteran. Saiki, kita mung ngetik async ing ngarep kita fungsi lan await ing ngarep kode apa wae sing butuh sawetara wektu kanggo dieksekusi. Pancen luwih wicaksana tinimbang kode biasa, nanging luwih gampang digunakake tinimbang kudu ngatasi primitif sinkronisasi, ngirim pesen, lan ngrampungake janji.

File Starlette kita katon kaya iki:

#!/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",
)

Kaya sing sampeyan ngerteni, cukup akeh disalin lan ditempel saka skrip Flask kita kanthi mung sawetara owah-owahan rute lan async/await tembung kunci.

Pinten dandan bisa nyalin lan nempel kode tenan menehi kita?

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

Kita duwe juara anyar, Ladies lan purun! Dhuwur kita sadurunge yaiku versi PHP murni ing 704 panjalukan per detik, sing banjur disusul versi Flask kita kanthi panjaluk 1080 per detik. Skrip Starlette kita ngrusak kabeh pesaing sadurunge kanthi 4562 panjaluk per detik, tegese paningkatan 6x tinimbang PHP murni lan perbaikan 4x saka Flask.

Yen sampeyan durung ngganti kode WSGI Python menyang ASGI, saiki bisa dadi wektu sing apik kanggo miwiti.

Node.js/ExpressJS

Nganti saiki, kita mung nutupi kerangka PHP lan Python. Nanging, sebagรฉyan gedhรฉ donya bener-bener nggunakake Java, DotNet, Node.js, Ruby on Rails, lan teknologi liya kanggo situs web kasebut. Iki dudu ringkesan lengkap kabeh ekosistem lan bioma ing donya, supaya supaya ora nindakake program sing padha karo kimia organik, kita bakal milih mung kerangka kerja sing paling gampang kanggo ngetik kode. .

Kajaba sampeyan wis ndhelikake ing ngisor salinan K&R C utawa Knuth&#x27; Seni Pemrograman Komputer suwene limalas taun, sampeyan mbokmenawa wis krungu saka Node.js & # x27; Kita sing wis ana wiwit wiwitan JavaScript uga wedi banget, kaget, utawa loro-lorone ing negara JavaScript modern, nanging ora bisa nolak yen JavaScript wis dadi kekuwatan sing kudu dianggep ing server uga. minangka browser. Sawise kabeh, saiki kita duwe integer 64 bit asli ing basa kasebut! Iku luwih apik tinimbang kabeh sing disimpen ing 64 bit floats adoh!

ExpressJS mbokmenawa server Node.js paling gampang digunakake, supaya kita bakal nindakake app Node.js/ExpressJS cepet lan reged kanggo ngawula counter kita.

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

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

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

const PORT    = 8000;

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

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

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

Kode iki luwih gampang ditulis tinimbang versi Python, sanajan JavaScript asli dadi rada angel nalika aplikasi dadi luwih gedhe, lan kabeh upaya kanggo mbenerake iki kayata TypeScript kanthi cepet dadi luwih verbose tinimbang Python.

Ayo ndeleng carane iki performs & # x27;

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

Sampeyan bisa uga wis krungu kuna (kuna dening standar Internet anyways ...) folktales bab Node.js & # x27; kacepetan, lan crita sing biasane bener thanks kanggo karya spektakuler sing Google wis rampung karo mesin V8 JavaScript. Ing kasus iki sanadyan, sanajan app cepet kita outperforms script Flask, alam Utas siji dikalahakรฉ dening papat pangolahan async wielded dening Starlette Knight sing ngandika &quot;Ni!&quot;.

Ayo njaluk bantuan liyane!

โ•ฐโ”€โžค  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 โ”‚
โ””โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Oke! Saiki iku malah papat ing papat perang! Ayo dadi pathokan!

โ•ฐโ”€โžค  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

Isih ora cukup ing tingkat Starlette, nanging ora ala kanggo hack JavaScript limang menit cepet & # x27; Saka tesku dhewe, skrip iki bener-bener ditahan maneh ing tingkat antarmuka database amarga node-postgres ora ana sing efisien kaya psycopg kanggo Python. Ngalih menyang sqlite minangka driver database ngasilake luwih saka 3000 panjalukan per detik kanggo kode ExpressJS padha.

Wangsulan: Bab ingkang utama kanggo Wigati iku senadyan kacepetan eksekusi Python alon, frameworks ASGI bener bisa competitive karo solusi Node.js kanggo workloads tartamtu.

Karat / Actix

Dadi saiki, kita wis nyedhak menyang pucuk gunung, lan miturut gunung, maksudku skor benchmark paling dhuwur sing dicathet dening tikus lan wong lanang.

Yen katon ing paling saka pathokan framework kasedhiya ing web, sampeyan bakal sok dong mirsani sing ana rong basa sing kathah dominasi ndhuwur: C ++ lan Rust. Aku wis kerjo karo C ++ wiwit 90s, lan aku malah wis Win32 C ++ framework bali sadurunge MFC / ATL ana bab, supaya aku duwe akรจh pengalaman karo basa. Iku ora akeh fun karya karo soko nalika sampeyan wis ngerti, supaya kita bakal nindakake versi Rust tinimbang. ;)

Rust relatif anyar minangka adoh saka basa program, nanging dadi obyek penasaran kanggo kula nalika Linus Torvalds announced sing bakal nampa Rust minangka basa program kernel Linux. Kanggo kita programer lawas, iku bab padha ngomong sing fangled anyar hippie thingie anyar iki bakal dadi amandemen anyar kanggo Konstitusi AS.

Saiki, yen sampeyan dadi programmer sing berpengalaman, sampeyan cenderung ora cepet-cepet mlayu kaya sing ditindakake wong enom, utawa sampeyan bisa uga kobong amarga owah-owahan cepet ing basa utawa perpustakaan. (Sapa wae sing nggunakake versi pisanan AngularJS bakal ngerti apa sing dakkandhakake.) Rust isih ana ing tahap pangembangan eksperimen kasebut, lan aku rumangsa lucu yen akeh conto kode ing web ora malah. kompilasi maneh karo versi paket saiki.

Nanging, kinerja sing dituduhake dening aplikasi Rust ora bisa ditolak. Yen sampeyan durung tau nyoba ripgrep utawa fd-golek metu ing wit kode sumber gedhe, temtunipun kudu menehi wong muter . Malah kasedhiya kanggo umume distribusi Linux mung saka manajer paket. Sampeyan ngganti verbosity kanggo kinerja karo Rust ... a akeh saka verbosity kanggo a akeh saka kinerja.

Kode lengkap kanggo Rust rada gedhe, mula kita bakal mriksa panangan sing relevan ing kene:

// =====================================================================
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
  )))
}

Iki luwih rumit tinimbang versi 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

Lan performa luwih akeh!

Server Rust kita nggunakake Actix/deadpool_postgres kanthi gampang ngalahake juara Starlette sadurunge kanthi +125%, ExpressJS kanthi +362%, lan PHP murni kanthi +1366%. (Aku bakal ninggalake delta kinerja karo versi Laravel minangka latihan kanggo maca.)

Aku wis ketemu sing sinau basa Rust dhewe wis luwih angel tinimbang basa liyane amarga wis akeh gotchas luwih saka apa aku wis katon njaba 6502 Majelis, nanging yen server Rust bisa njupuk ing 14x nomer kedhaftar minangka server PHP, banjur mbok menawa ana soko kanggo gained karo teknologi ngoper sawise kabeh. Pramila versi sabanjure Framework Pafera bakal adhedhasar Rust. Kurva sinau luwih dhuwur tinimbang basa skrip, nanging kinerja bakal migunani. Yen sampeyan ora bisa nglampahi wektu kanggo sinau Rust, banjur basis tumpukan teknologi ing Starlette utawa Node.js uga ora kaputusan ala.

Utang Teknis

Ing rong puluh taun kepungkur, kita wis lunga saka situs hosting statis sing murah menyang hosting bareng karo tumpukan LAMP kanggo nyewa VPS menyang AWS, Azure, lan layanan maya liyane. Saiki, akeh perusahaan sing marem nggawe keputusan desain adhedhasar sapa wae sing bisa ditemokake sing kasedhiya utawa paling murah amarga ana layanan awan sing trep nggawe gampang mbuwang luwih akeh hardware ing server lan aplikasi sing alon. Iki wis menehi keuntungan jangka pendek kanthi biaya utang teknis jangka panjang.

Peringatan Umum Surgeon California: Iki dudu asu ruang nyata.

70 taun kepungkur, ana balapan antariksa gedhe antarane Uni Soviet lan Amerika Serikat. Uni Soviet menangakรฉ akรจh-akรจhรฉ tonggak sejarah awal. Dheweke duwe satelit pisanan ing Sputnik, asu pisanan ing ruang angkasa ing Laika, pesawat ruang angkasa rembulan pisanan ing Luna 2, wong lanang lan wadon pisanan ing ruang angkasa ing Yuri Gagarin lan Valentina Tereshkova, lan liya-liyane ...

Nanging padha alon accumulating utang technical.

Senadyan Uni Sovyรจt minangka sing pisanan kanggo saben prestasi kasebut, proses lan tujuan rekayasa kasebut nyebabake dheweke fokus marang tantangan jangka pendek tinimbang kelayakan jangka panjang. Dheweke menang saben-saben mlumpat, nanging saya kesel lan alon-alon nalika mungsuh terus maju menyang garis finish.

Sawise Neil Armstrong njupuk langkah sajarah ing rembulan ing televisi langsung, Amerika njupuk timbal, lan banjur tetep ana minangka program Soviet faltered. Iki ora beda karo perusahaan saiki sing fokus ing perkara gedhe sabanjure, bayaran gedhe sabanjure, utawa teknologi gedhe sabanjure nalika gagal ngembangake kabiasaan lan strategi sing tepat kanggo jangka panjang.

Dadi pisanan menyang pasar ora ateges sampeyan bakal dadi pemain dominan ing pasar kasebut. Utawa, njupuk wektu kanggo nindakake samubarang sing bener ora njamin sukses, nanging mesthi nambah kasempatan kanggo prestasi jangka panjang. Yen sampeyan pimpinan teknologi kanggo perusahaan sampeyan, pilih arah lan alat sing bener kanggo beban kerja sampeyan. Aja popularitas ngganti kinerja lan efisiensi.

Sumber daya

Pengin ndownload file 7z sing ngemot skrip Rust, ExpressJS, Flask, Starlette, lan Pure PHP?

Babagan Penulis

Jim wis program wiwit entuk IBM PS / 2 bali sak 90s. Nganti saiki, dheweke isih luwih seneng nulis HTML lan SQL kanthi tangan, lan fokus ing efisiensi lan bener ing karyane.