Как я сделал самый быстрый сайт в Таиланде

image


Предисловие


Заняться этим проектом и написать эту статью вдохновил меня «молодой и шутливый человек который ускорял страницу с reactjs». Если кто-то помнит нашумевшую в своей время статью от сайта pingdom.com, о том что «Страницы в интернете прилично обросли жиром» их вывод складывался к тому, что раньше к весу страницы в основном добавляли изображения, теперь к этому «жиру» накинули и JavaScript. Страница шутливого молодого человека не дает особой практической пользы — больше разминка для его мозгов. Я же решил помочь своей девушке с продажей самого популярного продукта из ее ассортимента.



Я уже и раньше пытался делать крайне минималистичные сайты. Такие проекты были вызваны желаниям выйти за рамки фреймворков и возможно закончить проект быстрее. Такой подход достаточно популярен и среди моих друзей — популярность KISS (Keep It Simple, Stupid) дает свои плоды. Будучи в основном бэкенд программистом — я много времени провожу с оптимизацией кода на стороне сервера. На практике понял, как это важно не делать такие изменения вслепую, а подкреплять их метрикой.


Подготовка


Фронт-енд фреймворк


Я не фронтенд программист, мне приходится полагаться на фронтенд фреймворки. Чаще всего это означает bootstrap со всем «его добром». И хотя bootstrap компоненты можно использовать селективно, это все равно означает что я притащу с собой jQuery. Я видел уже достаточно native-javascript библиотек которые работают без Jquery. Размер бутстрапа и его компонент тоже меня не устраивал, мне всегда казалось что он «стилизует» чуть больше чем нужно.


Поэтому немного погуглив немного, я нашел фреймворки которые позиционируют себя как минималистычные. Вот примерный список того что я рассматривал


  • Base → http://getbase.org
  • Skeleton → http://getskeleton.com/
  • Fluidable → http://fluidable.com/
  • Purecss → http://purecss.io/

Я выбрал фрейворк base. Большинство из причин глубокого личные:


  1. Я уже работал с более раней версией
  2. Мне казалось что javascript компоненты мне не нужны, а если нужны, то я бы хотел их выбрать сам. Base — чисто css фреймворк.
  3. Использует gulp как билд тул. О котором я слышал много хорошего, некоторые умельцы даже пытались его интегрировать в рельсу (с которой я часто имею дело).
  4. Я нашел темплейт от автора фреймворка который подходил для моей цели. Я если честно не хотел много возиться с версткой, цель этого проекта была другая. Поэтому я с удовольствием отдал несколько долларов автору за это.

Поэтому я не буду рекомендовать только Base, а выдал список. Но я должен оговориться, что можно начать еще более минималистично, только с grid system — sussy grid, например.


Хостинг


Я не собирался делать динамический контент. И хоть казалось бы логичным сделать форму для покупки, большинство тайских магазинов что я видел избегали этого. Форма для покупки еще усложняется тем, что их бы пришлось интегрировать с банками, а в Тайланде нету четких лидеров в банковской сфере. Их больше 10 и все они более или менее пользуются спросом, даже среди моих друзей иностранцев\тайцев выбор банка крайне разнится. Очень много покупок делается непосредственно через интернет банки и мессенджер line. Поэтому я решил не ломать привычных паттернов, тем более это крайне упрощает мою задачу.


Мне нужен хостинг только для статического контента. Самым популярным выбором тут является github pages и AWS. Я побоялся использовать github pages из-за ограничений по трафику, а AWS мне показался не самым дешевым решением (по моим крайне грубым оценкам около 4 баксов в месяц). У меня уже было три сайта, которые крутятся на nearlyfreespeech.net — и десяти баксов хватило на них чтобы они работали больше года. Дешевле не всегда значит лучше, но в этом случае я уже имел опыт и никаких нареканий у меня нету.


image


Важная поправочка

_ В комментариях отметили, что мой географический выбор сервера не самый разумный для Тайского сайта. Так же, какой бы легковесный не был apache — он все равно создает новый поток для каждого соединения.


Метрика


Я изначально договорился сам с собой, что я не буду полагаться на собственные «ощущения». А буду полагаться на общественно признанный инструментарий.


gtmetrix.com — Я выбрал как основной тул для измерения «скорости» моего сайта. Он в себя включал два самых популярных инструмента google page speed и yslow. Оказалось, что оригинальный page speed все таки нашел чуть больше ошибок. Это привело меня к выводу, что полагаться на 100 процентный показатель в gtmetrix и схожие тулы — возможно не самая лучшая идея, это же скриптованая проверка на «самые популярные» ошибки. Вы всегда можете пойти дальше.


Измерять как быстро сайт будет открываться под нагрузкой я не собирался. Поэтому возможные проблемы с производительностью у Apache я игнорировал. Но вам возможно стоит задуматься.


Оптимизация


Gzip


Самая простая оптимизация которую можно было сделать — это предоставлять статические файлы в gzip формате. Это я делал во всех проектах в которых я работал до этого. Поэтому крайне быстро набросал таск в gulpfile.js, чтобы автоматизировать процесс создания .gz файлов.


    var gzip = require('gulp-gzip');

    gulp.task('gzip', function (){
      return gulp.src('./public//**/*.+(js|css|html)')
        .pipe(gzip())
        .pipe(gulp.dest('./public/'))
      });

И так же быстро набросал .htaccess файл, который подсовывает браузеру архивированный файл.


    Header add Vary accept-encoding
    RewriteEngine on
    RewriteCond %{HTTP:Accept-Encoding} gzip
    RewriteCond %{REQUEST_FILENAME}.gz -f
    RewriteRule ^(.*)$ $1.gz [L]

Мне так же пришлось копировать этот файл в билд папочку, без особых изменений. Это оказалось еще проще:


    gulp.task('htaccess', function () {
      return gulp.src('./src/**/.htaccess')
        .pipe(gulp.dest('./public/'));
    });

Так же, пришлось обновить и build таск


    gulp.task('build', function() {
      runSequence('clean', 'sass', 'build-img', 'jsmin', 'inlinesource', 'htaccess', 'gzip');
    });

Помоему это самый простой способ оптимизировать страницу, только ленивый не делает это в своих проектах. Во многих современных движках это все доводится до банальщины — добавить плагин или включить опцию.


Кэширование


Менее тривиальной оказалась задача по указыванию заголовков для кэширования. С AWS эти уже автоматизировано. Я пытался вспомнить когда я это делал последний раз, но так и не вспомнил когда. Поэтому с помощью магической силы гугла, пару попыток я все-таки сделал что-то 100% приеемлемое для gtmetrix.


    
        ExpiresActive On
        ExpiresByType image/jpg "access plus 1 year"
        ExpiresByType image/jpeg "access plus 1 year"
        ExpiresByType image/gif "access plus 1 year"
        ExpiresByType image/png "access plus 1 year"
        ExpiresByType text/css "access plus 1 month"
        ExpiresByType application/pdf "access plus 1 month"
        ExpiresByType text/x-javascript "access plus 1 month"
        ExpiresByType text/javascript "access plus 2 month"
        ExpiresByType application/javascript "access plus 2 month"
        ExpiresByType application/x-shockwave-flash "access plus 1 month"
        ExpiresByType image/x-icon "access plus 1 year"
        ExpiresByType image/icon "access plus 1 year"
        ExpiresByType application/ico "access plus 12 month"
        ExpiresDefault "access plus 2 days"
    

Но чтобы избежать двух попыток, я бы рекомендовал воспользоваться темплейтом для сервера из проекта html5-boilerplate. Я в следующий раз обязательно это сделаю :)


Изображения


Для человека у которого нету Photoshop’a на компьютере — работа с изображениями это большая попаболь. Половина проблем с изображениеми было решено за меня за счет того что я выбрал готовый темплейт — мне не пришлось возиться с спрайтами, векторами. Но это не решило всех моих проблем — мне нужны были другие иконки и другие изображения.


Правильный выбор формата для изображений


Я сделал самую грубую ошибку из тех ошибок что можно сделать. Я выбрал .png как формат по умолчанию для изображений, у меня было представление что png оптимизирован для веба. На самом деле для изображений насыщенных цветами (как например фото) — jpeg все-таки остается лучшим форматом, я оставил png для иконок.


Больше ликбеза на эту тему можно найти на страницах гугла (от людей которые понимают в этом больше чем я).


Компрессия без потерь


Как человек с инженерным образованием, я знаю цену специализированным инструментам. Они очень часто облегчают работу больше чем one-fits-all инструмент. Можно программировать на notepad’e, но чаще становится нашим основным рабочим инструментом — sublime text, rubymine. В данном случае imageoptim хорош, но не достаточно хорош. Так как у меня было много jpeg файлов, я нашел сравнительный анализ лослесс сжатий — выйграл jpegtran.


в gulp это оказалось очень просто:


var jpegtran = require('imagemin-jpegtran');

gulp.task('build-jpg', function () {
    gulp.src('./src/img/*.+(jpg|jpeg)')
    .pipe(jpegtran({ progressive: true })())
    .pipe(gulp.dest('./public/img'));
});

Играемся с оптимизацией цветов в jpeg


Но выбором только правильного формата все не окончилось. Изображения все-равно были слишком большие. Мой hero background занимал больше половины мегабайта.


image

У меня нету photoshop’a, а что без него тут делать я не очень понимал. Но друзья подсобили и посоветовали отличный проект TinyJpg — все оказалось слишком просто.


CSS


Я все таки решил заинлайнить css. Это немного контринтуитивно, люди советуют держать все это в отдельном файле чтобы стили могли закэшироваться. Есть даже вероятность, что у юзера уже закешировано все это, особенно если используешь популярный фрэймворк.


Я скорее так бы и сделал, если бы использовал bootstrap. Но так как я использовал менее распространеный base и не очень то ожидал что пользовали будут возвращаться на мой сайт — то я решил удалить лишний http запрос.


В gulp это оказалось как обычно проще всего:



Fonts


Интересная и неожиданная ситуация для меня сложилось с Google Fonts, в темплейте было использовано два разных фонта. И вроде даже разумно оптимизированы:


  1. Они загружались за один http запрос
  2. Использовал WebFontLoader, который асинхронно загружал фонты и рендерил страницу после загрузки.

Но gtmetrix продолжал ругаться на фонты — у них небыло cache headers. Я решил пойти по пути уже предложенному в статье на которую я ссылался в начале и избавился от google fonts. во всех девайсах есть вполне приличные встроеные фонты. Поэтому я оставил вот такой вот набор:


font-family: "Helvetica Neue", "Calibri Light", Roboto, sans-serif;

Геолокация серверов


Пока в Тайланде, я набросал быстрый скрипт для проверки скорости из Тайланда:


require "uri"

SERVERS = [
  {
    :name => "nearlyfreespeach",
    :url  => "http://euphorbia.soihok.com/"
  },
  {
    :name => "AWS servers",
    :url => "https://d4s21h4msr5q2.cloudfront.net/"
  }
]

SERVERS.each do |server|
  uri = URI.parse(server[:url])
  puts "Performing HTTP speed test for #{server[:name]}"
  puts `wget -O /dev/null #{server[:url]} 2>&1 | awk '/\\/dev\\/null/ {speed=\$3 \$4} END {gsub(/\\(|\\)/,"",speed); print speed}'`
end

И получил я такие результаты:


$ rspec speed_test.rb
Performing HTTP speed test for nearlyfreespeach
85.1KB/s
Performing HTTP speed test for AWS servers
203KB/s

Performing HTTP speed test for nearlyfreespeach
92.5KB/s
Performing HTTP speed test for AWS servers
192KB/s

$ rspec speed_test.rb
Performing HTTP speed test for nearlyfreespeach
83.2KB/s
Performing HTTP speed test for AWS servers
168KB/s

Поэтому не совершайте моих ошибок. Выбирайте хостеров с серверами близкими к вашей целевой аудитории. Сайты будут открываться как минимум в два раза быстрее!


Выводы


Вот такой вот получился вебсайт — http://euphorbia.soihok.com/
А вот последнии метрики с gtmetrix — https://gtmetrix.com/reports/euphorbia.soihok.com/zhMn6OhU


Как утверждают многие СЕОшники — быстрый сайт дает бонусы в гугле. Я если честно на это надеялся, но не проверял. Поэтому здесь мне метриками крыть не получится.


Я часто путешествую и периодический приходится работать с крайне сомнительным соединением — будь то мобильное соединение, отели или просто не развитый интернет где то в Азии. И меня сильно расстраивает что нету мобильных версий у сайтов, что сайты даже для десктопов не оптимизированы.


Я очень много фокусировался чтобы делать быстрый бэкенд, которому не нужно много ресурсов. Но я никогда не фокусировался сильно на фронт-енде, хотя бы потому что считал что 100% показатели иметь не возможно. Но это оказалось, хоть и не просто, но возможно. Более того, можно пойти дальше и ускорить более «требуемого минимума». Все эти принципы и опыт построения быстрых страниц универсальны и последующие сайты ускорять будет гораздо легче и быстрее! Об этом лишь надо начать думать, где-то там на подсознании! И ваши пользователи скажут вам спасибо и будут возвращаться.


Желаю быстрых сайтов всем нам!


* «самый быстрый сайт в Таиланде» — это мой «кричащий» заголовок, я не берусь это утверждать со 100% уверенностью. Но большинство сайтов что я видел в Тайланде — не самые быстрые.

Комментарии (17)

  • 21 января 2017 в 20:41

    +4

    Кто живет в Таиланде, просветите — какой у вас там пинг до Северной Америки? Возможно, сайт можно было бы сделать раза в 2 быстрее, банально сменив хостера.

    Еще немного не понял, почему вы решили раздавать статику через Apache, ставя цель сделать сайт быстрым?

    • 21 января 2017 в 20:51

      0

      Спасибо за комментарий.

      Добавил скриншот с nearlyfreespeach, у них очень облегченый Apache для статитических сайтов — без PHP, CGI, Daemon’ов.

      • 21 января 2017 в 21:06

        +4

        Проблема Apache в алгоритмах, а не модулях. Там, кажется, до сих пор создается отдельный поток на каждое соединение. Кто работает с этим сервером, поправьте меня, если я не прав.
        • 21 января 2017 в 22:44 (комментарий был изменён)

          0

          Я тоже нашел вопрос и статью что подтверждают ваши слова. Будем знать и исправляться.


          Спасибо. Попробую AWS, у них были сервера в Сингапуре.

          • 21 января 2017 в 23:20

            +1

            Если у вас была цель не потратить ни цента на маркетинговый сайт:
            Я же решил помочь своей девушке с продажей самого популярного продукта из ее ассортимента.
            AWS мне показался не самым дешевым решением (по моим крайне грубым оценкам около 4 баксов в месяц)
            То могу предложить попробовать бесплатный тариф у Cloudflare. Хотя это сервис для защиты от DDoS, у них имеется реально крутой CDN, о чем почему-то мало кто задумывается. Суть в том, что оригинал сайта будет по-прежнему храниться у вашего хостера, но его копии будут оставаться на кеширующих серверах CF. Как бонус еще и бесплатный SSL с поддержкой HTTP/2.0. Только проверьте, чтобы у вас заголовки правильные отдавались.
            Offtopic
            А еще можно и заработать, размещая на своем сайте рекламу конкурентов. Нет, ну серьезно: бизнесмены, которым жалко 4 бакса в месяц на хостинг…
        • 21 января 2017 в 22:44

          0

          Там, кажется, до сих пор создается отдельный поток на каждое соединение. Кто работает с этим сервером, поправьте меня, если я не прав.
          Правы. Если у автора apache еще и с mpm prefork, то явно есть задел для ускорения :). worker и event многопоточные, но все равно по потоку на соединение. Особенность архитектуры. Вообще использование apache для раздачи статики — неправильное решение. Бывают случаи, когда без apache — никак, но этот явно не из их числа.

          Единственное, что я не понял — что значит «No Daemons» в описании хостера.

    • 21 января 2017 в 21:08

      0

      А причём тут вообще Северная Америка?

      • 21 января 2017 в 21:11

        0

        Увидел «Test Server Region: Vancouver, Canada».


        Всё равно не понимаю, какой смысл в том, чтобы сайт «в Тайланде» (очевидно, предназначенный для тайской аудитории) быстро грузился из Канады :)

        • 21 января 2017 в 21:33

          0

          Вот и я о том же — зачем?
          $ dig euphorbia.soihok.com

          ; <<>> DiG 9.10.4-P5-RedHat-9.10.4-3.P5.fc24 <<>> euphorbia.soihok.com
          ;; global options: +cmd
          ;; Got answer:
          ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49939
          ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 9, ADDITIONAL: 1

          ;; OPT PSEUDOSECTION:
          ; EDNS: version: 0, flags:; udp: 4096
          ;; QUESTION SECTION:
          ;euphorbia.soihok.com. IN A

          ;; ANSWER SECTION:
          euphorbia.soihok.com. 1783 IN CNAME euphorbia.nfshost.com.
          euphorbia.nfshost.com. 414 IN A 208.94.118.202

          ;; AUTHORITY SECTION:
          nfshost.com. 4014 IN NS ns.phx5.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.phx1.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.phx4.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.ord1.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.phx7.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.phx6.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.phx2.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.phx3.nearlyfreespeech.net.
          nfshost.com. 4014 IN NS ns.phx8.nearlyfreespeech.net.

          ;; Query time: 2 msec
          ;; SERVER: 192.168.0.1#53(192.168.0.1)
          ;; WHEN: Sat Jan 21 19:31:21 CET 2017
          ;; MSG SIZE rcvd: 315

          $ whois 208.94.118.202
          [Querying whois.arin.net]
          [whois.arin.net]

          #
          # ARIN WHOIS data and services are subject to the Terms of Use
          # available at: https://www.arin.net/whois_tou.html
          #
          # If you see inaccuracies in the results, please report at
          # https://www.arin.net/public/whoisinaccuracy/index.xhtml
          #

          #
          # The following results may also be obtained via:
          # https://whois.arin.net/rest/nets;q=208.94.118.202?showDetails=true&showARIN=false&showNonArinTopLevelNet=false&ext=netref2
          #

          # start

          NetRange: 208.94.116.0 - 208.94.119.255
          CIDR: 208.94.116.0/22
          NetName: CELL-1-ARIN-1
          NetHandle: NET-208-94-116-0-1
          Parent: NET208 (NET-208-0-0-0-0)
          NetType: Direct Allocation
          OriginAS: AS40630
          Organization: GridFury, LLC (CELL-1)
          RegDate: 2008-08-28
          Updated: 2012-03-02
          Comment: http://www.capequilog.com/
          Ref: https://whois.arin.net/rest/net/NET-208-94-116-0-1

          OrgName: GridFury, LLC
          OrgId: CELL-1
          Address: 255 PRIMERA BLVD STE 160
          City: LAKE MARY
          StateProv: FL
          PostalCode: 32746
          Country: US
          RegDate: 2007-08-29
          Updated: 2014-06-17
          Comment: http://www.gridfury.com/
          Ref: https://whois.arin.net/rest/org/CELL-1

          OrgAbuseHandle: CNAT-ARIN
          OrgAbuseName: CapEquiLog Network Abuse Team
          OrgAbusePhone: +1-877-275-0926
          OrgAbuseEmail: noc@gridfury.com
          OrgAbuseRef: https://whois.arin.net/rest/poc/CNAT-ARIN

          OrgTechHandle: CNO41-ARIN
          OrgTechName: CapEquiLog Network Operations
          OrgTechPhone: +1-877-275-0926
          OrgTechEmail: noc@gridfury.com
          OrgTechRef: https://whois.arin.net/rest/poc/CNO41-ARIN

          OrgNOCHandle: CNO41-ARIN
          OrgNOCName: CapEquiLog Network Operations
          OrgNOCPhone: +1-877-275-0926
          OrgNOCEmail: noc@gridfury.com
          OrgNOCRef: https://whois.arin.net/rest/poc/CNO41-ARIN

          RNOCHandle: CNO41-ARIN
          RNOCName: CapEquiLog Network Operations
          RNOCPhone: +1-877-275-0926
          RNOCEmail: noc@gridfury.com
          RNOCRef: https://whois.arin.net/rest/poc/CNO41-ARIN

          RAbuseHandle: CNAT-ARIN
          RAbuseName: CapEquiLog Network Abuse Team
          RAbusePhone: +1-877-275-0926
          RAbuseEmail: noc@gridfury.com
          RAbuseRef: https://whois.arin.net/rest/poc/CNAT-ARIN

          RTechHandle: CNO41-ARIN
          RTechName: CapEquiLog Network Operations
          RTechPhone: +1-877-275-0926
          RTechEmail: noc@gridfury.com
          RTechRef: https://whois.arin.net/rest/poc/CNO41-ARIN

          # end

          # start

          NetRange: 208.94.118.128 - 208.94.118.255
          CIDR: 208.94.118.128/25
          NetName: NFSNI-1-CELL-1-6
          NetHandle: NET-208-94-118-128-1
          Parent: CELL-1-ARIN-1 (NET-208-94-116-0-1)
          NetType: Reassigned
          OriginAS: AS40630
          Organization: NearlyFreeSpeech.NET (NFSNI-1)
          RegDate: 2012-05-15
          Updated: 2012-05-15
          Comment: https://www.NearlyFreeSpeech.NET/
          Comment: All abuse reports must go to the abuse contact.
          Ref: https://whois.arin.net/rest/net/NET-208-94-118-128-1

          OrgName: NearlyFreeSpeech.NET
          OrgId: NFSNI-1
          Address: 1035 PRIMERA BLVD STE 1041
          City: LAKE MARY
          StateProv: FL
          PostalCode: 32746-2193
          Country: US
          RegDate: 2008-02-01
          Updated: 2011-11-07
          Comment: https://www.NearlyFreeSpeech.NET/
          Comment: All abuse reports must go to the abuse contact.
          Ref: https://whois.arin.net/rest/org/NFSNI-1

          OrgNOCHandle: NOC2951-ARIN
          OrgNOCName: Network Operations Center
          OrgNOCPhone: +1-888-741-4678
          OrgNOCEmail: noc@nearlyfreespeech.net
          OrgNOCRef: https://whois.arin.net/rest/poc/NOC2951-ARIN

          OrgTechHandle: NOC2951-ARIN
          OrgTechName: Network Operations Center
          OrgTechPhone: +1-888-741-4678
          OrgTechEmail: noc@nearlyfreespeech.net
          OrgTechRef: https://whois.arin.net/rest/poc/NOC2951-ARIN

          OrgAbuseHandle: ABUSE1857-ARIN
          OrgAbuseName: Abuse Team
          OrgAbusePhone: +1-888-741-4678
          OrgAbuseEmail: abuse@nearlyfreespeech.net
          OrgAbuseRef: https://whois.arin.net/rest/poc/ABUSE1857-ARIN

          # end

          #
          # ARIN WHOIS data and services are subject to the Terms of Use
          # available at: https://www.arin.net/whois_tou.html
          #
          # If you see inaccuracies in the results, please report at
          # https://www.arin.net/public/whoisinaccuracy/index.xhtml
          #


          • 21 января 2017 в 21:58

            0

            Вопрос разумный крыть нечем. Не подумал, сила привычки сработала.


            Даже Сингапур быстрее бы открывался в данном случае. Сделаю замеры для наглядности и обновлю статью.


            Спасибо.

  • 21 января 2017 в 22:14

    0

    выйти за рамки традиционных фреймворков и возможность закончить проект быстрее

    Лишь бы фреймворщики не набежали и не начали советовать. :)
    популярность KISS (Keep It Simple, Stupid) дает свои плоды

    +1 за KISS.
    Но он не популярен. :)
    Скорее всего из-за того, что просто делать сложно и сложно делать просто. :)
    • 21 января 2017 в 22:39

      0

      Смотря где — в руби набирают оборот библиотеки в духе dry-rb, roda, sequel и так далее;-)
      • 22 января 2017 в 00:05

        0

        Я о PHP. :)
  • 21 января 2017 в 22:37

    0

    Может Вы имели ввиду http://getbase.org
    • 21 января 2017 в 22:38

      0

      Да, спасибо. Исправил
  • 22 января 2017 в 01:50

    0

    Тайланд — это там, где войны-андройды выйграли стоймость мозайки?

    (В заголовке написано правильно, во всех остальных местах в тексте — нет.)

  • 22 января 2017 в 02:02

    0

    Здесь лежат две книги по ускорению работы сайтов
    Книга «Реактивные веб-сайты»
    Книга «Разгони свой сайт»
    И хотя книги выпущены давно, а технологии постоянно совершенствуются и выходят новые (к примеру HTTP/2),
    рекомендую для ознакомления

© Habrahabr.ru