დიახ, ვირჯინია, არსებობს *არის* ა თოვლის ბაბუა სხვაობა ვებ ჩარჩოებს შორის 2023 წელს

ერთი გამომწვევი პროგრამისტის მოგზაურობა სწრაფი შესრულების ვებ სერვერის კოდის მოსაძებნად, სანამ ის არ დაემორჩილება ბაზრის ზეწოლას და ტექნიკურ ვალს.
2023-03-24 11:52:06
👁️ 780
💬 0

შინაარსი

  1. შესავალი
  2. ტესტი
  3. PHP/Laravel
  4. სუფთა PHP
  5. ლარაველის ხელახლა მონახულება
  6. ჯანგო
  7. კოლბა
  8. ვარსკვლავი
  9. Node.js/ExpressJS
  10. Rust/Actix
  11. ტექნიკური დავალიანება
  12. რესურსები

შესავალი

ჩემი ერთ-ერთი უახლესი სამუშაო ინტერვიუს შემდეგ, გამიკვირდა, რომ მივხვდი, რომ კომპანია, რომელზეც მე მივმართე, კვლავ იყენებდა Laravel-ს, PHP ჩარჩოს, რომელიც დაახლოებით ათი წლის წინ ვცადე. ეს იყო ღირსეული იმ დროისთვის, მაგრამ თუ არსებობს ერთი მუდმივი ტექნოლოგიაში და მოდაში, ეს არის სტილისა და კონცეფციების მუდმივი ცვლილება და ხელახალი გამოჩენა. თუ JavaScript პროგრამისტი ხართ, ალბათ იცნობთ ამ ძველ ხუმრობას

პროგრამისტი 1: "მე არ მომწონს ეს ახალი JavaScript ჩარჩო!"

პროგრამისტი 2: "არ არის საჭირო ინერვიულოთ. დაელოდე ექვს თვეს და კიდევ ერთი იქნება მის ჩანაცვლება!"

ცნობისმოყვარეობის გამო გადავწყვიტე მენახა ზუსტად რა ხდება, როცა ძველს და ახალს გამოცდას ვაძლევთ. რა თქმა უნდა, ვებ ივსება კრიტერიუმებითა და პრეტენზიებით, რომელთაგან ყველაზე პოპულარულია ალბათ TechEmpower Web Framework ბენჩმარკები აქ . თუმცა დღეს ჩვენ არ ვაპირებთ ისეთი რთული რამის გაკეთებას, როგორც მათ. ჩვენ ორივე ლამაზად და მარტივს შევინარჩუნებთ, რათა ეს სტატია არ იქცეს ომი და მშვიდობა , და რომ თქვენ გექნებათ მცირე შანსი, რომ ფხიზლად დარჩეთ იმ დროისთვის, როცა კითხვას დაასრულებთ. გამოიყენება ჩვეულებრივი გაფრთხილებები: ეს შეიძლება ერთნაირად არ იმუშაოს თქვენს აპარატზე, პროგრამული უზრუნველყოფის სხვადასხვა ვერსიამ შეიძლება გავლენა მოახდინოს შესრულებაზე და შრედინგერის კატა რეალურად გახდა ზომბი კატა, რომელიც ნახევრად ცოცხალი და ნახევრად მკვდარი იყო ზუსტად ამავე დროს.

ტესტი

ტესტირების გარემო

ამ ტესტისთვის მე გამოვიყენებ ჩემს ლეპტოპს, რომელიც აღჭურვილია პატარა i5-ით, რომელიც მუშაობს Manjaro Linux-ზე, როგორც ეს ნაჩვენებია აქ.

╰─➤  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

ამოცანა ხელთ

ჩვენს კოდს ექნება სამი მარტივი დავალება თითოეული მოთხოვნისთვის:

  1. წაიკითხეთ მიმდინარე მომხმარებლის სესიის ID ქუქიიდან
  2. ჩატვირთეთ დამატებითი ინფორმაცია მონაცემთა ბაზიდან
  3. დაუბრუნეთ ეს ინფორმაცია მომხმარებელს

როგორი იდიოტური ტესტია, შეიძლება იკითხოთ? კარგად, თუ გადახედავთ ქსელის მოთხოვნებს ამ გვერდისთვის, შეამჩნევთ ერთს სახელად sessionvars.js, რომელიც ზუსტად იგივეს აკეთებს.

sessionvars.js-ის შინაარსი

ხედავთ, თანამედროვე ვებ გვერდები რთული არსებებია და ერთ-ერთი ყველაზე გავრცელებული ამოცანაა რთული გვერდების ქეშირება მონაცემთა ბაზის სერვერზე ზედმეტი დატვირთვის თავიდან ასაცილებლად.

თუ კომპლექსურ გვერდს ხელახლა გამოვიტანთ ყოველ ჯერზე, როცა მომხმარებელი ითხოვს მას, მაშინ ჩვენ შეგვიძლია მოვემსახუროთ მხოლოდ 600 მომხმარებელს წამში.

╰─➤  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

მაგრამ თუ ამ გვერდს სტატიკური HTML ფაილის ქეშირებას მოვახდენთ და Nginx-ს მივცეთ საშუალება სწრაფად გადააგდოს იგი ფანჯრიდან მომხმარებლისთვის, მაშინ ჩვენ შეგვიძლია მოვემსახუროთ 32000 მომხმარებელს წამში, რაც გაზრდის ეფექტურობას 50-ჯერ.

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

სტატიკური index.en.html არის ის ნაწილი, რომელიც მიდის ყველას და მხოლოდ ის ნაწილები, რომლებიც განსხვავდება მომხმარებლის მიხედვით, იგზავნება sessionvars.js-ში. ეს არა მხოლოდ ამცირებს მონაცემთა ბაზის დატვირთვას და ქმნის უკეთეს გამოცდილებას ჩვენი მომხმარებლებისთვის, არამედ ამცირებს კვანტურ ალბათობას, რომ ჩვენი სერვერი სპონტანურად ორთქლდება Warp core გარღვევის დროს კლინგონის თავდასხმის დროს.

კოდის მოთხოვნები

თითოეული ფრეიმორისთვის დაბრუნებულ კოდს ექნება ერთი მარტივი მოთხოვნა: აჩვენეთ მომხმარებელს რამდენჯერ განაახლეს გვერდი თქვით „თვლა არის x“. იმისათვის, რომ ყველაფერი მარტივი იყოს, ჩვენ ამ დროისთვის შორს ვიქნებით Redis რიგებისგან, Kubernetes კომპონენტებისგან ან AWS Lambdas-ისგან.

ნაჩვენებია რამდენჯერ ეწვიეთ გვერდს

თითოეული მომხმარებლის სესიის მონაცემები შეინახება PostgreSQL მონაცემთა ბაზაში.

მომხმარებელთა სესიების ცხრილი

და ეს მონაცემთა ბაზის ცხრილი შეიკვეცება ყოველი ტესტის წინ.

მაგიდა შეკვეცის შემდეგ

მარტივი, მაგრამ ეფექტურია Pafera-ს დევიზი... მაინც ყველაზე ბნელი ვადების მიღმა...

ტესტის რეალური შედეგები

PHP/Laravel

კარგი, ახლა ჩვენ საბოლოოდ შეგვიძლია დავიწყოთ ხელების დაბინძურება. ჩვენ გამოვტოვებთ Laravel-ის დაყენებას, რადგან ის მხოლოდ კომპოზიტორისა და ხელოსნის ჯგუფია ბრძანებებს.

პირველ რიგში, ჩვენ დავაყენებთ მონაცემთა ბაზის პარამეტრებს .env ფაილში

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

შემდეგ ჩვენ დავაყენებთ ერთ სარეზერვო მარშრუტს, რომელიც ყველა მოთხოვნას უგზავნის ჩვენს კონტროლერს.

Route::fallback(SessionController::class);

და დააყენეთ კონტროლერი, რომ აჩვენოს რაოდენობა. ლარაველი, ნაგულისხმევად, ინახავს სესიებს მონაცემთა ბაზაში. ის ასევე უზრუნველყოფს session() ჩვენი სესიის მონაცემებთან ინტერფეისის ფუნქცია, ასე რომ ჩვენი გვერდის გამოსატანად მხოლოდ კოდის რამდენიმე ხაზი დასჭირდა.

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

    $count  += 1;

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

    return 'Count is ' . $count;
  }
}

php-fpm და Nginx-ის დაყენების შემდეგ, ჩვენი გვერდი საკმაოდ კარგად გამოიყურება...

╰─➤  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

ყოველ შემთხვევაში, სანამ რეალურად არ დავინახავთ ტესტის შედეგებს...

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

არა, ეს არ არის შეცდომა. ჩვენი სატესტო მანქანა გადავიდა 600 მოთხოვნიდან წამში რთული გვერდის რენდერით... 21 მოთხოვნამდე წამში რენდერამდე "თვლა არის 1".

მერე რა მოხდა? რამე არასწორია ჩვენს PHP ინსტალაციასთან? Nginx რატომღაც ანელებს php-fpm-თან ინტერფეისის დროს?

სუფთა PHP

მოდით ხელახლა გავაკეთოთ ეს გვერდი სუფთა PHP კოდით.

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

ჩვენ ახლა გამოვიყენეთ 98 სტრიქონი კოდი, რათა გაგვეკეთებინა ის, რაც გააკეთა Laravel-ში კოდის ოთხი ხაზი (და კონფიგურაციის მთელი თაიგული). (რა თქმა უნდა, თუ ჩვენ გავაკეთეთ შეცდომების სწორად დამუშავება და მომხმარებლის წინაშე შეტყობინებები, ეს იქნება დაახლოებით ორჯერ მეტი ხაზების რაოდენობა.) იქნებ შეგვიძლია მივაღწიოთ 30 მოთხოვნას წამში?

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

უი! როგორც ჩანს, ჩვენს PHP ინსტალაციაში ცუდი არაფერია. სუფთა PHP ვერსია აკეთებს 700 მოთხოვნას წამში.

თუ PHP-ში ცუდი არაფერია, იქნებ არასწორად დავაკონფიგურიროთ Laravel?

ლარაველის ხელახლა მონახულება

კონფიგურაციის პრობლემებისა და შესრულების რჩევების ძიების შემდეგ, ორი ყველაზე პოპულარული ტექნიკა იყო კონფიგურაციისა და მარშრუტის მონაცემების ქეშირება, რათა თავიდან აიცილოთ მათი დამუშავება ყოველი მოთხოვნისთვის. ამიტომ, ჩვენ გავითვალისწინებთ მათ რჩევებს და გამოვიყენებთ ამ რჩევებს.

╰─➤  php artisan config:cache

   INFO  Configuration cached successfully.  

╰─➤  php artisan route:cache

   INFO  Routes cached successfully.  

ყველაფერი კარგად გამოიყურება ბრძანების ხაზზე. მოდი ხელახლა გავიმეოროთ საორიენტაციო ნიშანი.

╰─➤  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

ახლა ჩვენ გავზარდეთ შესრულება 21.04-დან 28.80 მოთხოვნამდე წამში, რაც დრამატული ამაღლებაა თითქმის 37%-ით! ეს იქნება საკმაოდ შთამბეჭდავი ნებისმიერი პროგრამული პაკეტისთვის... გარდა იმისა, რომ ჩვენ კვლავ ვასრულებთ სუფთა PHP ვერსიის მოთხოვნების მხოლოდ 1/24-ს.

თუ ფიქრობთ, რომ რაღაც არასწორია ამ ტესტში, უნდა ესაუბროთ Lucinda PHP ჩარჩოს ავტორს. მისი ტესტის შედეგებში მას აქვს ლუსინდა ლარაველს სცემს 36x-ით HTML მოთხოვნებისთვის და 90x-ით JSON მოთხოვნებისთვის.

საკუთარ აპარატზე როგორც Apache-სთან, ასევე Nginx-თან ტესტირების შემდეგ, მასში ეჭვის შეტანის საფუძველი არ მაქვს. ლარაველი მართლაც სამართლიანია რომ ნელი! PHP თავისთავად არც ისე ცუდია, მაგრამ როგორც კი დაამატებთ დამატებით დამუშავებას, რომელსაც Laravel ამატებს თითოეულ მოთხოვნას, მაშინ ძალიან მიჭირს რეკომენდაცია Laravel-ის არჩევანს 2023 წელს.

ჯანგო

PHP/Wordpress ანგარიშები ინტერნეტში არსებული ყველა ვებსაიტის დაახლოებით 40%. , რაც მას ყველაზე დომინანტურ ჩარჩოდ აქცევს. თუმცა, პირადად მე ვხვდები, რომ პოპულარობა სულაც არ ითარგმნება ხარისხში, ვიდრე მე მაქვს მოულოდნელი უკონტროლო სურვილი ამ არაჩვეულებრივი გურმანული საკვებისადმი. ყველაზე პოპულარული რესტორანი მსოფლიოში ... მაკდონალდსი. მას შემდეგ, რაც ჩვენ უკვე გამოვცადეთ სუფთა PHP კოდი, ჩვენ არ ვაპირებთ თავად Wordpress-ის ტესტირებას, რადგან ყველაფერი, რაც მოიცავს Wordpress-ს, უდავოდ დაბალი იქნება ვიდრე 700 მოთხოვნა წამში, რასაც ჩვენ ვაკვირდებოდით სუფთა PHP-ით.

Django არის კიდევ ერთი პოპულარული ჩარჩო, რომელიც უკვე დიდი ხანია არსებობს. თუ თქვენ იყენებდით მას წარსულში, ალბათ სიამოვნებით ახსოვთ მისი სანახაობრივი მონაცემთა ბაზის ადმინისტრირების ინტერფეისი და რამდენად შემაშფოთებელი იყო ყველაფრის კონფიგურაცია ისე, როგორც თქვენ გსურდათ. ვნახოთ, რამდენად კარგად მუშაობს Django 2023 წელს, განსაკუთრებით ახალი ASGI ინტერფეისით, რომელიც მან დაამატა 4.0 ვერსიისთვის.

Django-ს დაყენება საოცრად წააგავს Laravel-ის დაყენებას, რადგან ორივე იყო იმ ასაკიდან, სადაც MVC არქიტექტურები იყო თანამედროვე და სწორი. ჩვენ გამოვტოვებთ მოსაწყენ კონფიგურაციას და პირდაპირ გადავალთ ხედის დაყენებაზე.

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

კოდის ოთხი ხაზი იგივეა, რაც Laravel-ის ვერსიაში. ვნახოთ, როგორ მუშაობს.

╰─➤  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

სულაც არ არის ცუდი 355 მოთხოვნა წამში. ის მხოლოდ ნახევარი აქვს სუფთა PHP ვერსიის შესრულებას, მაგრამ ასევე 12-ჯერ აღემატება Laravel-ის ვერსიას. ჯანგო ლარაველის წინააღმდეგ, როგორც ჩანს, საერთოდ არ არის კონკურსი.

კოლბა

გარდა ყველაფრისა, მათ შორის, სამზარეულოს ნიჟარის ჩარჩოებისა, ასევე არის უფრო მცირე ჩარჩოები, რომლებიც უბრალოდ აკეთებენ ძირითად დაყენებას, ხოლო დანარჩენს საშუალებას გაძლევთ გაუმკლავდეთ. ერთ-ერთი საუკეთესო გამოსაყენებელია Flask და მისი ASGI კოლეგა Quart. ჩემი საკუთარი PaferaPy Framework აშენებულია Flask-ის თავზე, ამიტომ მე კარგად ვიცი, რამდენად ადვილია საქმეების შესრულება და შესრულების შენარჩუნებისას.

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

როგორც ხედავთ, Flask სკრიპტი უფრო მოკლეა ვიდრე სუფთა PHP სკრიპტი. მე ვხვდები, რომ ყველა ენიდან, რომელიც მე გამოვიყენე, Python არის ალბათ ყველაზე გამომხატველი ენა კლავიშების აკრეფის თვალსაზრისით. ფრჩხილებისა და ფრჩხილების ნაკლებობა, სიებისა და დიქტების გაგება და დაბლოკვა, რომელიც დაფუძნებულია ჩაღრმავებაზე და არა მძიმით, პითონს საკმაოდ მარტივს, მაგრამ მის შესაძლებლობებში ძლიერს ხდის.

სამწუხაროდ, პითონი ასევე არის ყველაზე ნელი ზოგადი დანიშნულების ენა, მიუხედავად იმისა, თუ რამდენი პროგრამაა დაწერილი მასში. Python-ის ხელმისაწვდომი ბიბლიოთეკების რაოდენობა დაახლოებით ოთხჯერ მეტია, ვიდრე მსგავსი ენები და მოიცავს დომენების დიდ რაოდენობას, მაგრამ არავინ იტყვის, რომ Python არის სწრაფი და მოქმედი ნიშების გარეთ, როგორიცაა NumPy.

ვნახოთ, როგორ ადარებს ჩვენი Flask ვერსია ჩვენს წინა ჩარჩოებს.

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

ჩვენი Flask სკრიპტი რეალურად უფრო სწრაფია ვიდრე ჩვენი სუფთა PHP ვერსია!

თუ ეს გაგიკვირდებათ, უნდა გესმოდეთ, რომ ჩვენი Flask აპი აკეთებს მთელ თავის ინიციალიზაციას და კონფიგურაციას, როდესაც ჩვენ ჩართავთ Gunicorn სერვერს, ხოლო PHP ხელახლა ახორციელებს სკრიპტს ყოველ ჯერზე, როცა ახალი მოთხოვნა შემოდის. ეს&#x27 ;ეს ექვივალენტურია, რომ Flask იყო ახალგაზრდა, მონდომებული ტაქსის მძღოლი, რომელმაც უკვე დაძრა მანქანა და ელოდება გზის პირას, ხოლო PHP არის მოხუცი მძღოლი, რომელიც რჩება სახლში ზარის მოლოდინში და მხოლოდ ამის შემდეგ ატარებს მანქანას. გადმოგცეთ. როგორც ძველი სკოლის ბიჭი და მოვიდა იმ დროიდან, როდესაც PHP იყო მშვენიერი ცვლილება უბრალო HTML და SHTML ფაილებში, ცოტა სამწუხაროა იმის გაგება, თუ რამდენი დრო გავიდა, მაგრამ დიზაინის განსხვავებები ნამდვილად ართულებს PHP-ს. კონკურენცია გაუწიეთ Python-ის, Java-ს და Node.js სერვერებს, რომლებიც უბრალოდ რჩებიან მეხსიერებაში და ამუშავებენ მოთხოვნას ჟონგლერის მოხერხებულად.

ვარსკვლავი

Flask შეიძლება იყოს ჩვენი ყველაზე სწრაფი ფრეიმერი ჯერჯერობით, მაგრამ ის რეალურად საკმაოდ ძველი პროგრამული უზრუნველყოფაა. პითონის საზოგადოებამ გადაერთო ახალ ასიქრონულ ASGI სერვერებზე რამდენიმე წლის წინ და, რა თქმა უნდა, მეც გადავედი მათთან ერთად.

Pafera Framework-ის უახლესი ვერსია, PaferaPyAsync , დაფუძნებულია Starlette-ზე. მიუხედავად იმისა, რომ არსებობს Flask-ის ASGI ვერსია, სახელწოდებით Quart, Quart-სა და Starlette-ს შორის შესრულების განსხვავებები საკმარისი იყო იმისთვის, რომ ჩემი კოდი Starlette-ზე გადამეყენებინა.

ასიქრონული პროგრამირება შეიძლება ბევრისთვის შეშინებული იყოს, მაგრამ სინამდვილეში ეს არ არის რთული კონცეფცია Node.js-ის ბიჭების წყალობით, რომლებიც ამ კონცეფციის პოპულარიზაციას ახდენენ ათწლეულზე მეტი ხნის წინ.

ჩვენ ვიბრძოდით კონკურენტულობასთან მრავალთრეადინგთან, მრავალპროცესირებასთან, განაწილებულ გამოთვლებთან, დაპირებების ჯაჭვებთან და ყველა იმ სახალისო პერიოდთან, რამაც ნაადრევად დაბერა და გაანადგურა მრავალი ვეტერანი პროგრამისტი. ახლა ჩვენ უბრალოდ აკრიფეთ async ჩვენი ფუნქციების წინაშე და await ნებისმიერი კოდის წინ, რომლის შესრულებასაც შეიძლება გარკვეული დრო დასჭირდეს. ის მართლაც უფრო ვრცელია, ვიდრე ჩვეულებრივი კოდი, მაგრამ გაცილებით ნაკლებად შემაშფოთებელია გამოსაყენებლად, ვიდრე სინქრონიზაციის პრიმიტიულებთან, შეტყობინებების გაცემასთან და დაპირებების გადაჭრასთან.

ჩვენი Starlette ფაილი ასე გამოიყურება:

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

როგორც ხედავთ, ის საკმაოდ კოპირებულია და ჩასმულია ჩვენი Flask სკრიპტიდან მხოლოდ რამდენიმე მარშრუტიზაციის ცვლილებით და async/await საკვანძო სიტყვები.

რამხელა გაუმჯობესება შეიძლება მოგვცეს რეალურად კოპირება და ჩასმა კოდი?

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

ახალი ჩემპიონი გვყავს, ქალბატონებო და ბატონებო! ჩვენი წინა მაღალი იყო ჩვენი სუფთა PHP ვერსია 704 მოთხოვნა წამში, რომელსაც შემდეგ გადაუსწრო ჩვენი Flask ვერსია 1080 მოთხოვნით წამში. ჩვენი Starlette სკრიპტი ანადგურებს ყველა წინა კანდიდატს 4562 მოთხოვნით წამში, რაც ნიშნავს 6-ჯერ გაუმჯობესებას სუფთა PHP-თან შედარებით და 4-ჯერ გაუმჯობესებას Flask-თან შედარებით.

თუ ჯერ არ შეგიცვლიათ თქვენი WSGI Python კოდი ASGI-ზე, ახლა შეიძლება კარგი დრო იყოს დასაწყებად.

Node.js/ExpressJS

ჯერჯერობით, ჩვენ მხოლოდ PHP და Python ჩარჩოებს ვფარავდით. თუმცა, მსოფლიოს დიდი ნაწილი რეალურად იყენებს Java, DotNet, Node.js, Ruby on Rails და სხვა ასეთ ტექნოლოგიებს მათი ვებსაიტებისთვის. ეს არავითარ შემთხვევაში არ არის მსოფლიოს ყველა ეკოსისტემისა და ბიომის ყოვლისმომცველი მიმოხილვა, ამიტომ ორგანული ქიმიის პროგრამირების ეკვივალენტის თავიდან ასაცილებლად, ჩვენ ავირჩევთ მხოლოდ იმ ჩარჩოებს, რომელთათვისაც ყველაზე ადვილია კოდის აკრეფა. რომელი ჯავა ნამდვილად არ არის.

თუ არ იმალებოდით K&R C-ის ან Knuth&#x27;s-ის თქვენი ასლის ქვეშ კომპიუტერული პროგრამირების ხელოვნება ბოლო თხუთმეტი წლის განმავლობაში, თქვენ ალბათ გსმენიათ Node.js-ის შესახებ. ჩვენ, ვინც JavaScript-ის დაწყების დღიდან ვიმყოფებით, ან წარმოუდგენლად შეშინებული, გაოცებული ან ორივე ერთად ვართ თანამედროვე JavaScript-ის მდგომარეობით, მაგრამ არ შეიძლება უარვყოთ, რომ JavaScript გახდა ძალა, რომელსაც სერვერებზეც უნდა გავითვალისწინოთ. როგორც ბრაუზერები. ყოველივე ამის შემდეგ, ჩვენ უკვე გვაქვს მშობლიური 64 ბიტიანი მთელი რიცხვები ახლა ენაში! ეს ბევრად უკეთესია, ვიდრე ყველაფერი, რაც ინახება 64-ბიტიან მოცურავებში!

ExpressJS არის ალბათ ყველაზე მარტივი Node.js სერვერი გამოსაყენებლად, ამიტომ ჩვენ გავაკეთებთ სწრაფ და ბინძურ 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}`));

ამ კოდის დაწერა რეალურად უფრო ადვილი იყო, ვიდრე პითონის ვერსიები, თუმცა მშობლიური JavaScript ხდება საკმაოდ მოუხერხებელი, როდესაც აპლიკაციები უფრო დიდი ხდება და ამის გამოსწორების ყველა მცდელობა, როგორიცაა TypeScript, სწრაფად ხდება უფრო ვრცელი ვიდრე Python.

ვნახოთ, როგორ მუშაობს ეს!

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

შეიძლება გსმენიათ უძველესი (უძველესი ინტერნეტ სტანდარტებით მაინც...) ხალხური ზღაპრები Node.js&#x27; სიჩქარე, და ეს ისტორიები ძირითადად ჭეშმარიტია იმ სანახაობრივი მუშაობის წყალობით, რომელიც Google-მა გააკეთა V8 JavaScript ძრავით. თუმცა, ამ შემთხვევაში, მიუხედავად იმისა, რომ ჩვენი სწრაფი აპლიკაცია აჯობებს Flask სკრიპტს, მისი ერთი ხრახნიანი ბუნება დამარცხებულია ოთხი ასინქრონული პროცესით, რომელსაც ახორციელებს Starlette Knight, რომელიც ამბობს „Ni!“.

მოდი მივიღოთ მეტი დახმარება!

╰─➤  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 │
└────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

კარგი! ახლა ეს არის ლუწი ოთხი ოთხზე ბრძოლა! მოდით შევაფასოთ!

╰─➤  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

ჯერ კიდევ არ არის Starlette-ის დონეზე, მაგრამ ეს არ არის ცუდი JavaScript-ის სწრაფი ხუთწუთიანი ჰაკერისთვის. ჩემი საკუთარი ტესტირებიდან, ეს სკრიპტი რეალურად შეჩერებულია მონაცემთა ბაზის ინტერფეისის დონეზე, რადგან node-postgres არ არის ისეთი ეფექტური, როგორც psycopg არის Python-ისთვის. sqlite-ზე გადართვა, როგორც მონაცემთა ბაზის დრაივერი, იძლევა 3000-ზე მეტ მოთხოვნას წამში იმავე ExpressJS კოდისთვის.

მთავარია აღინიშნოს, რომ მიუხედავად Python-ის შესრულების ნელი სიჩქარისა, ASGI ჩარჩოები რეალურად შეიძლება იყოს კონკურენტუნარიანი Node.js გადაწყვეტილებებთან გარკვეული დატვირთვისთვის.

Rust/Actix

ასე რომ, ახლა ჩვენ ვუახლოვდებით მთის მწვერვალს და მთაში ვგულისხმობ თაგვებისა და მამაკაცების მიერ დაფიქსირებულ უმაღლეს საორიენტაციო ქულებს.

თუ გადახედავთ ინტერნეტში არსებულ ჩარჩო კრიტერიუმების უმეტესობას, შეამჩნევთ, რომ არსებობს ორი ენა, რომლებიც დომინირებენ ზედა ნაწილში: C++ და Rust. მე ვმუშაობდი C++-თან 90-იანი წლებიდან და მე მქონდა საკუთარი Win32 C++ ჩარჩო, სანამ MFC/ATL იყო საქმე, ასე რომ, მე მაქვს ენის გამოყენების დიდი გამოცდილება. არც თუ ისე სახალისოა რაიმესთან მუშაობა, როცა უკვე იცი, ამიტომ ჩვენ ვაპირებთ Rust-ის ვერსიის გაკეთებას. ;)

Rust შედარებით ახალია პროგრამირების ენების თვალსაზრისით, მაგრამ ის ჩემთვის ცნობისმოყვარეობის ობიექტი გახდა, როდესაც ლინუს ტორვალდსმა გამოაცხადა, რომ ის მიიღებდა Rust-ს, როგორც Linux ბირთვის პროგრამირების ენას. ჩვენთვის ძველი პროგრამისტებისთვის, ეს დაახლოებით იგივეა, რაც იმის თქმა, რომ ეს ახალი დახვეწილი ახალი ეპოქის ჰიპი იყო აშშ-ს კონსტიტუციის ახალი შესწორება.

ახლა, როცა გამოცდილი პროგრამისტი ხარ, არ ახტები ბანდას ისე სწრაფად, როგორც ამას ახალგაზრდები აკეთებენ, წინააღმდეგ შემთხვევაში შეიძლება დაიწვა ენის ან ბიბლიოთეკების სწრაფი ცვლილებებით. (ვინც გამოიყენა AngularJS-ის პირველი ვერსია, გაიგებს, რაზეც ვსაუბრობ.) Rust ჯერ კიდევ გარკვეულწილად იმ ექსპერიმენტული განვითარების ეტაპზეა და მე სასაცილოდ მიმაჩნია, რომ ბევრი კოდის მაგალითი ინტერნეტში არც კი არის. შედგენა აღარ არის პაკეტების მიმდინარე ვერსიებით.

თუმცა, Rust აპლიკაციების მიერ ნაჩვენები შესრულება არ შეიძლება უარყო. თუ არასდროს გიცდიათ რიპგრეპი ან ფდ-იპოვე დიდი წყაროს კოდის ხეებზე, თქვენ აუცილებლად უნდა მისცეთ მათ დატრიალება. ისინი ხელმისაწვდომია Linux-ის დისტრიბუციების უმეტესობისთვის უბრალოდ პაკეტის მენეჯერისგან. თქვენ უცვლით სიტყვიერებას პერფორმანსზე Rust-თან... ა ბევრი სიტყვიერების ა ბევრი შესრულების.

Rust-ის სრული კოდი ცოტა დიდია, ამიტომ ჩვენ უბრალოდ გადავხედავთ შესაბამის დამმუშავებლებს აქ:

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

ეს ბევრად უფრო რთულია, ვიდრე 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

და ბევრად უფრო შემსრულებელი!

ჩვენი Rust სერვერი Actix/deadpool_postgres-ით იყენებს ჩვენს წინა ჩემპიონ Starlette-ს +125%-ით, ExpressJS-ს +362%-ით და სუფთა PHP-ს +1366%-ით. (სპექტაკლის დელტას დავტოვებ Laravel ვერსიას, როგორც სავარჯიშო მკითხველისთვის.)

მე აღმოვაჩინე, რომ თავად Rust ენის სწავლა უფრო რთული იყო, ვიდრე სხვა ენებზე, რადგან მას აქვს ბევრად მეტი ჭურჭელი, ვიდრე ყველაფერი, რაც მინახავს 6502 ასამბლეის გარეთ, მაგრამ თუ თქვენს Rust სერვერს შეუძლია მიიღოს 14-ჯერ მეტი მომხმარებლები, როგორც თქვენი PHP სერვერი, მაშინ შესაძლოა, ბოლოს და ბოლოს, არსებობს რაღაცის მოპოვება გადართვის ტექნოლოგიებით. სწორედ ამიტომ, Pafera Framework-ის შემდეგი ვერსია დაფუძნებული იქნება Rust-ზე. სწავლის მრუდი გაცილებით მაღალია, ვიდრე სკრიპტის ენები, მაგრამ შესრულება ღირს. თუ თქვენ არ შეგიძლიათ დრო დაუთმოთ Rust-ის სწავლას, მაშინ თქვენი ტექნიკური სტეკის დაფუძნება Starlette-ზე ან Node.js-ზე არც ისე ცუდი გადაწყვეტილებაა.

ტექნიკური დავალიანება

ბოლო ოცი წლის განმავლობაში, ჩვენ გადავედით იაფი სტატიკური ჰოსტინგის საიტებიდან საერთო ჰოსტინგზე LAMP სტეკებით VPS-ების დაქირავებამდე AWS, Azure და სხვა ღრუბლოვან სერვისებზე. დღესდღეობით, ბევრი კომპანია კმაყოფილია დიზაინის გადაწყვეტილების მიღებით, იმის მიხედვით, თუ ვისი პოვნაა ხელმისაწვდომი ან იაფი, ვინაიდან მოსახერხებელი ღრუბლოვანი სერვისების გამოჩენამ გააადვილა მეტი ტექნიკის გადაგდება ნელი სერვერებისა და აპლიკაციებისთვის. ამან მათ დიდი მოკლევადიანი მოგება მისცა გრძელვადიანი ტექნიკური დავალიანების ფასად.

კალიფორნიის გენერალური ქირურგის გაფრთხილება: ეს არ არის ნამდვილი კოსმოსური ძაღლი.

70 წლის წინ საბჭოთა კავშირსა და შეერთებულ შტატებს შორის დიდი კოსმოსური რბოლა გაიმართა. საბჭოელებმა მოიგეს ადრეული ეტაპების უმეტესი ნაწილი. მათ ჰყავდათ პირველი თანამგზავრი Sputnik-ში, პირველი ძაღლი კოსმოსში ლაიკაში, პირველი მთვარის კოსმოსური ხომალდი ლუნა 2-ში, პირველი კაცი და ქალი კოსმოსში იური გაგარინსა და ვალენტინა ტერეშკოვაში და ა.შ.

მაგრამ ნელ-ნელა აგროვებდნენ ტექნიკურ ვალს.

მიუხედავად იმისა, რომ საბჭოთა კავშირი პირველი იყო თითოეულ ამ მიღწევაში, მათი საინჟინრო პროცესები და მიზნები იწვევდა მათ ფოკუსირებას მოკლევადიან გამოწვევებზე და არა გრძელვადიან მიზანშეწონილობაზე. ისინი იმარჯვებდნენ ყოველ ჯერზე, როდესაც ხტუნავდნენ, მაგრამ ისინი უფრო დაღლილები და ნელა ხდებოდნენ, ხოლო მათი ოპონენტები განაგრძობდნენ თანმიმდევრული ნაბიჯების გადადგმას ფინიშის ხაზისკენ.

მას შემდეგ, რაც ნილ არმსტრონგმა გადადგა თავისი ისტორიული ნაბიჯები მთვარეზე პირდაპირ ეთერში, ამერიკელებმა აიღეს ლიდერობა, შემდეგ კი იქ დარჩნენ, რადგან საბჭოთა პროგრამა შეფერხდა. ეს არაფრით განსხვავდება დღევანდელი კომპანიებისგან, რომლებიც ფოკუსირებულნი არიან მომავალ დიდ საქმეზე, მომავალ დიდ ანაზღაურებაზე ან შემდეგ დიდ ტექნოლოგიაზე, ხოლო გრძელვადიან პერსპექტივაში სათანადო ჩვევებისა და სტრატეგიების შემუშავებას ვერ ახერხებენ.

ბაზარზე პირველობა არ ნიშნავს, რომ ამ ბაზარზე დომინანტური მოთამაშე გახდებით. ალტერნატიულად, დროის დახარჯვა საქმის სწორად გასაკეთებლად არ იძლევა წარმატების გარანტიას, მაგრამ რა თქმა უნდა ზრდის თქვენს შანსებს გრძელვადიანი მიღწევებისთვის. თუ თქვენ ხართ თქვენი კომპანიის ტექნიკური ლიდერი, აირჩიეთ სწორი მიმართულება და ინსტრუმენტები თქვენი სამუშაო დატვირთვისთვის. ნუ მისცემთ უფლებას პოპულარობას შეცვალოს შესრულება და ეფექტურობა.

რესურსები

გსურთ ჩამოტვირთოთ 7z ფაილი, რომელიც შეიცავს Rust, ExpressJS, Flask, Starlette და Pure PHP სკრიპტებს?

ავტორის შესახებ

ჯიმი პროგრამირებას აგრძელებს მას შემდეგ, რაც მან მიიღო IBM PS/2 90-იან წლებში. დღემდე ურჩევნია HTML-ისა და SQL-ის ხელით წერა და სამუშაოს ეფექტურობასა და სისწორეზე აკეთებს აქცენტს.