Այո, Վիրջինիա, կա *Կա* ա Ձմեռ պապ Վեբ շրջանակների միջև տարբերությունը 2023 թվականին

Մի անհնազանդ ծրագրավորողի ճամփորդությունը՝ արագ կատարող վեբ սերվերի կոդը գտնելու համար, նախքան շուկայի ճնշմանը և տեխնիկական պարտքին ենթարկվելը
2023-03-24 11:52:06
👁️ 793
💬 0

Բովանդակություն

  1. Ներածություն
  2. Թեստը
  3. PHP/Laravel
  4. Մաքուր PHP
  5. Կրկին այցելել Laravel
  6. Ջանգո
  7. Կոլբա
  8. Աստղիկ
  9. Node.js/ExpressJS
  10. Rust/Actix
  11. Տեխնիկական պարտք
  12. Ռեսուրսներ

Ներածություն

Իմ ամենավերջին աշխատանքային հարցազրույցներից մեկից հետո ես զարմացա, երբ հասկացա, որ ընկերությունը, որի համար ես դիմել էի, դեռ օգտագործում էր Laravel՝ PHP շրջանակ, որը ես փորձեցի մոտ մեկ տասնամյակ առաջ: Ժամանակի համար դա պարկեշտ էր, բայց եթե տեխնոլոգիայի և նորաձևության մեջ կա մեկ հաստատուն, դա ոճերի և գաղափարների շարունակական փոփոխությունն ու վերարտադրումը: Եթե ​​դուք JavaScript ծրագրավորող եք, հավանաբար ծանոթ եք այս հին կատակին.

Ծրագրավորող 1. «Ինձ դուր չի գալիս այս նոր JavaScript շրջանակը:«

Ծրագրավորող 2. «Անհանգստանալու կարիք չկա: Պարզապես սպասեք վեց ամիս, և կլինի ևս մեկը, որը կփոխարինի այն:"

Հետաքրքրությունից դրդված որոշեցի տեսնել, թե կոնկրետ ինչ է տեղի ունենում, երբ փորձության ենք ենթարկում հինն ու նորը: Իհարկե, համացանցը լցված է հենանիշերով և պահանջներով, որոնցից ամենահայտնին, հավանաբար, այն է TechEmpower Web Framework-ի հենանիշերն այստեղ . Այնուամենայնիվ, մենք չենք պատրաստվում անել այնպիսի բարդ բան, որքան նրանք այսօր: Մենք կպահենք ամեն ինչ գեղեցիկ և պարզ, որպեսզի այս հոդվածը չվերածվի. Պատերազմ և խաղաղություն , և որ դուք մի փոքր հնարավորություն կունենաք արթուն մնալու մինչև կարդալը ավարտեք: Կիրառվում են սովորական նախազգուշացումները. սա կարող է նույնը չաշխատել ձեր մեքենայի վրա, ծրագրաշարի տարբեր տարբերակները կարող են ազդել աշխատանքի վրա, և Շրյոդինգերի կատուն իրականում դարձել է զոմբի կատու, որը կիսով չափ կենդանի էր և կիսով չափ մեռած:

Թեստը

Փորձարկման միջավայր

Այս թեստի համար ես կօգտագործեմ իմ նոութբուքը, որը զինված է Manjaro Linux-ով աշխատող մանր i5-ով, ինչպես ցույց է տրված այստեղ:

╰─➤  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-ում: Սա ոչ միայն նվազեցնում է տվյալների բազայի ծանրաբեռնվածությունը և ավելի լավ փորձ է ստեղծում մեր օգտատերերի համար, այլ նաև նվազեցնում է քվանտային հավանականությունը, որ մեր սերվերը ինքնաբերաբար գոլորշիացվելու է աղավաղված միջուկի խախտման ժամանակ, երբ Klingons հարձակմանը:

Կոդի պահանջներ

Յուրաքանչյուր շրջանակի համար վերադարձված կոդը կունենա մեկ պարզ պահանջ՝ օգտագործողին ցույց տալ, թե քանի անգամ է նա թարմացրել էջը՝ ասելով «Հաշիվը 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);

Եվ դրեք կարգավորիչը, որպեսզի ցուցադրի հաշվարկը: Laravel-ը լռելյայնորեն պահում է նիստերը տվյալների բազայում: Այն նաև ապահովում է 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-ը:

Կրկին այցելել 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 շրջանակի հեղինակի հետ: Իր թեստի արդյունքներում նա ունի Լուսինդան հաղթում է Laravel-ին 36 անգամ HTML հարցումների համար և 90 անգամ՝ JSON հարցումների համար:

Իմ սեփական մեքենայի վրա թե՛ Apache-ի, թե՛ Nginx-ի հետ փորձարկումից հետո ես նրան կասկածելու պատճառ չունեմ: Laravel-ը իսկապես արդար է որ դանդաղ! PHP-ն ինքնին այնքան էլ վատ չէ, բայց երբ ավելացնեք բոլոր լրացուցիչ մշակումները, որոնք Laravel-ը ավելացնում է յուրաքանչյուր հարցում, ապա ես դժվարանում եմ առաջարկել Laravel-ին որպես ընտրություն 2023 թվականին:

Ջանգո

PHP/Wordpress հաշիվների համար Համացանցում բոլոր կայքերի մոտ 40%-ը , դարձնելով այն ամենից գերիշխող շրջանակը: Անձամբ ես, սակայն, գտնում եմ, որ ժողովրդականությունը պարտադիր չէ որակի վերածել այլևս, քան ես ինքս ինձ զգում եմ այդ արտասովոր գուրման ուտելիքի հանկարծակի անկառավարելի ցանկությունը: աշխարհի ամենահայտնի ռեստորանը ... McDonald&#x27;s. Քանի որ մենք արդեն փորձարկել ենք մաքուր 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-ը նաև ամենադանդաղ ընդհանուր նշանակության լեզուն է այնտեղ, չնայած նրանում որքան ծրագրային ապահովում է գրված: 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 Դա հավասարազոր է Ֆլասկին երիտասարդ, եռանդուն տաքսու վարորդին, ով արդեն գործի է դրել մեքենան և սպասում է ճանապարհի կողքին, մինչդեռ PHP-ն ծեր վարորդն է, ով մնում է իր տանը՝ սպասելով ներս մտնելու զանգին և միայն դրանից հետո քշում: քեզ վերցնելու համար: Լինելով հին դպրոցական տղա և գալիս է այն օրերից, երբ PHP-ն հիանալի փոփոխություն էր պարզ HTML և SHTML ֆայլերի համար, մի փոքր տխուր է գիտակցել, թե որքան ժամանակ է անցել, բայց դիզայնի տարբերություններն իսկապես դժվարացնում են PHP-ի համար մրցել Python-ի, Java-ի և Node.js սերվերների դեմ, որոնք պարզապես մնում են հիշողության մեջ և կատարում են հարցումները ձեռնածության հեշտությամբ:

Աստղիկ

Flask-ը կարող է լինել մեր ամենաարագ շրջանակը մինչ այժմ, բայց այն իրականում բավականին հին ծրագրակազմ է: Python համայնքը մի քանի տարի առաջ անցավ նոր ասիկրոն ASGI սերվերներին, և, իհարկե, ես ինքս անցել եմ նրանց հետ միասին:

Pafera Framework-ի նորագույն տարբերակը, PaferaPyAsync , հիմնված է Starlette-ի վրա: Չնայած կա ASGI Flask-ի տարբերակ, որը կոչվում է Quart, Quart-ի և Starlette-ի միջև կատարողական տարբերությունները բավական էին, որպեսզի ես փոխարենը վերահիմնավորեմ իմ կոդը Starlette-ի վրա:

Asychronous ծրագրավորումը կարող է վախեցնել շատ մարդկանց համար, բայց դա իրականում բարդ հայեցակարգ չէ՝ շնորհիվ 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 և նման այլ տեխնոլոգիաներ իրենց կայքերի համար: Սա ամենևին էլ աշխարհի բոլոր էկոհամակարգերի և բիոմների համապարփակ ակնարկ չէ, ուստի օրգանական քիմիայի ծրագրավորման համարժեքը խուսափելու համար մենք կընտրենք միայն այն շրջանակները, որոնց համար ամենահեշտ է մուտքագրել կոդը: որից Java-ն հաստատ չէ։

Եթե ​​դուք թաքնվել եք 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}`));

Այս կոդը իրականում ավելի հեշտ էր գրել, քան Python-ի տարբերակները, թեև բնիկ 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 հավելվածների ցուցադրած կատարումը չի կարելի հերքել: Եթե ​​երբեք չեք փորձել ripgrep կամ fd-գտնել դուրս մեծ կոդով ծառերի վրա, դուք անպայման պետք է պտտեք նրանց: Դրանք նույնիսկ հասանելի են 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

Եվ շատ ավելի կատարողական:

Actix/deadpool_postgres օգտագործող մեր Rust սերվերը հեշտությամբ հաղթում է մեր նախորդ չեմպիոն Starlette-ին +125%-ով, ExpressJS-ին +362%-ով և մաքուր PHP-ին +1366%-ով։ (Ես կթողնեմ կատարման դելտան Laravel տարբերակով որպես վարժություն ընթերցողի համար):

Ես հասկացա, որ Rust լեզուն սովորելն ինքնին ավելի դժվար է եղել, քան մյուս լեզուները, քանի որ այն ունի շատ ավելի շատ բան, քան այն ամենը, ինչ ես տեսել եմ 6502 Assembly-ից դուրս, բայց եթե ձեր Rust սերվերը կարող է ընդունել 14 անգամ ավելի օգտվողները որպես ձեր PHP սերվեր, ապա, ի վերջո, միգուցե ինչ-որ բան կարելի է ձեռք բերել փոխարկման տեխնոլոգիաներով: Ահա թե ինչու Pafera Framework-ի հաջորդ տարբերակը հիմնված կլինի Rust-ի վրա: Ուսուցման կորը շատ ավելի բարձր է, քան սկրիպտային լեզուները, բայց կատարումը արժե այն: Եթե ​​դուք չեք կարող ժամանակ տրամադրել Rust-ին սովորելու համար, ապա ձեր տեխնոլոգիական փաթեթը Starlette-ի կամ Node.js-ի վրա հիմնելը նույնպես վատ որոշում չէ:

Տեխնիկական պարտք

Վերջին քսան տարիների ընթացքում մենք էժան ստատիկ հոսթինգ կայքերից անցել ենք LAMP կույտերով համօգտագործվող հոսթինգի մինչև VPS-ներ վարձակալել AWS, Azure և այլ ամպային ծառայություններին: Մեր օրերում, շատ ընկերություններ բավարարված են դիզայնի որոշումներ կայացնելով՝ հիմնվելով նրանց վրա, ում նրանք կարող են գտնել այն հասանելի կամ ամենաէժանը, քանի որ հարմար ամպային ծառայությունների հայտնվելը հեշտացրել է ավելի շատ սարքավորումներ նետել դանդաղ սերվերների և հավելվածների վրա: Սա նրանց մեծ կարճաժամկետ շահույթ է տվել երկարաժամկետ տեխնիկական պարտքի գնով:

Կալիֆորնիայի գլխավոր վիրաբույժի զգուշացում. սա իրական տիեզերական շուն չէ:

70 տարի առաջ Խորհրդային Միության և Միացյալ Նահանգների միջև մեծ տիեզերական մրցավազք էր: Խորհրդային Միությունը հաղթեց վաղ փուլերի մեծ մասը: Նրանք ունեին առաջին արբանյակը Sputnik-ում, առաջին շունը տիեզերքում Լայկայում, առաջին լուսնային տիեզերանավը Լունա 2-ում, առաջին տղամարդն ու կինը տիեզերքում Յուրի Գագարինում և Վալենտինա Տերեշկովայում և այլն...

Բայց կամաց-կամաց տեխնիկական պարտք էին կուտակում։

Չնայած Խորհրդային Միությունը առաջինն էր այս նվաճումներից յուրաքանչյուրին, նրանց ինժեներական գործընթացները և նպատակները ստիպում էին նրանց կենտրոնանալ կարճաժամկետ մարտահրավերների վրա, քան երկարաժամկետ իրագործելիության: Նրանք հաղթում էին ամեն անգամ, երբ ցատկեցին, բայց ավելի հոգնած ու դանդաղ էին դառնում, մինչդեռ մրցակիցները շարունակում էին հետևողական քայլեր կատարել դեպի վերջնագիծը:

Երբ Նիլ Արմսթրոնգը լուսնի վրա իր պատմական քայլերն արեց ուղիղ հեռուստատեսությամբ, ամերիկացիները ստանձնեցին առաջատարը, իսկ հետո մնացին այնտեղ, քանի որ սովետական ​​ծրագիրը խափանում էր: Սա ոչնչով չի տարբերվում այն ​​ընկերություններից, որոնք այսօր կենտրոնացել են հաջորդ մեծ բանի, հաջորդ մեծ վարձատրության կամ հաջորդ մեծ տեխնոլոգիայի վրա՝ չմշակելով համապատասխան սովորություններ և ռազմավարություններ երկարաժամկետ հեռանկարում:

Շուկայում առաջինը լինելը չի ​​նշանակում, որ դուք կդառնաք այդ շուկայում գերիշխող խաղացողը։ Որպես այլընտրանք, ժամանակ հատկացնելն ամեն ինչ ճիշտ անելու համար չի երաշխավորում հաջողությունը, բայց, իհարկե, մեծացնում է երկարաժամկետ ձեռքբերումների ձեր հնարավորությունները: Եթե ​​դուք տեխնոլոգիական առաջատարն եք ձեր ընկերության համար, ընտրեք ճիշտ ուղղությունը և գործիքները ձեր ծանրաբեռնվածության համար: Թույլ մի տվեք, որ ժողովրդականությունը փոխարինի կատարողականությանը և արդյունավետությանը:

Ռեսուրսներ

Ցանկանու՞մ եք ներբեռնել Rust, ExpressJS, Flask, Starlette և Pure PHP սկրիպտները պարունակող 7z ֆայլ:

Հեղինակի մասին

Ջիմը ծրագրավորում է այն պահից, երբ 90-ական թվականներին ստացել է IBM PS/2: Մինչ օրս նա դեռ նախընտրում է HTML և SQL գրել ձեռքով և կենտրոնանում է աշխատանքի արդյունավետության և կոռեկտության վրա։