One place for hosting & domains

      сервера

      Работа сервера Nginx и алгоритмов выбора блока расположения


      Введение

      Nginx — один из самых популярных веб-серверов в мире. Он может успешно выдерживать высокую нагрузку с множеством одновременных подключений клиентов и функционировать как веб-сервер, почтовый сервер или обратный прокси-сервер.

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

      Конфигурации блока Nginx

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

      В первую очередь мы расскажем о блоках server и location.

      Блок server — это часть конфигурации Nginx, которая определяет виртуальный сервер, используемый для обработки запросов заданного типа. Администраторы часто настраивают несколько блоков server и определяют, какой из них будет отвечать за конкретное соединение на основании запрошенного доменного имени, порта и IP-адреса.

      Блок location располагается внутри блока server и определяет, как Nginx будет обрабатывать запросы различных ресурсов и URI для родительского сервера. Администратор, использующий эти блоки, может разделить пространство URI любым удобным способом. Это чрезвычайно гибкая модель.

      Как Nginx решает, какой серверный блок будет обрабатывать запрос

      Поскольку Nginx разрешает администратору определять несколько серверных блоков, работающих как отдельные экземпляры виртуального веб-сервера, ему требуется процедура, определяющая, какие серверные блоки будут использоваться для выполнения запроса.

      Для этого используется фиксированная система проверок, служащих для подбора оптимального совпадения. Главные директивы серверного блока, которые учитывает Nginx в этом процессе — директивы listen и server_name.

      Синтаксический анализ директивы “listen” для поиска возможных совпадений

      Прежде всего, Nginx смотрит IP-адрес и порт запроса. Он сверяет их с директивой listen каждого сервера, создавая список серверных блоков, которые могут обработать данный запрос.

      Директива listen обычно определяет IP-адрес и порт, на которые отвечает серверный блок. Любой серверный блок, не включающий директиву listen, по умолчанию имеет параметры прослушивания 0.0.0.0:80 (или 0.0.0.0:8080, если Nginx запускается обычным пользователем без привилегий root). Это позволяет данным блокам отвечать на запросы любого интерфейса на порту 80, но данное значение по умолчанию не имеет большого веса в процессе выбора сервера.

      Директиву listen можно задать следующим образом:

      • Сочетание IP-адреса и порта.
      • Отдельный IP-адрес, который будет прослушивать порт 80 по умолчанию.
      • Одиночный порт, который прослушивает каждый интерфейс этого порта.
      • Путь к сокету Unix.

      Последняя опция обычно влияет только на передачу запросов между разными серверами.

      Вначале Nginx попробует выбрать серверный блок, на который будет отправлен запрос, на основе специфики директивы listen, используя следующие правила:

      • Nginx преобразует все неполные директивы listen, заменяя отсутствующие значения значениями по умолчанию так, что каждый блок оценивается по IP-адресу и порту. Вот несколько примеров такого преобразования:
        • Блок без директивы listen использует значение 0.0.0.0:80.
        • Блок с заданным IP-адресом 111.111.111.111 без номера порта использует значение 111.111.111.111:80
        • Блок с заданным портом 8888 без IP-адреса использует значение 0.0.0.0:8888
      • Затем Nginx пытается собрать список серверных блоков, наиболее точно соответствующих запросу по IP-адресу и порту. Это означает, что блок со значением IP-адреса 0.0.0.0 (соответствующим любому интерфейсу) не будет выбран, если будут найден блоки, где указан конкретный IP-адрес. Точное совпадение порта обязательно в любом случае.
      • Если будет найдено одно наиболее точное совпадение, запрос будет обработан с помощью данного серверного блока. Если будет найдено несколько серверных блоков с одинаковым уровнем соответствия, Nginx начнет оценку директивы server_name каждого серверного блока.

      Важно понимать, что Nginx будет использовать для оценки директиву server_name, только если будет нужно выбрать из серверных блоков с одинаковым уровнем соответствия в директиве listen. Например, в случае размещения example.com на порту 80 с IP-адресом 192.168.1.10, запрос example.com всегда будет обслуживаться первым блоком из данного примера невзирая на директиву server_name во втором блоке.

      server {
          listen 192.168.1.10;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      

      В случае равного уровня соответствия нескольких серверных блоков следующим шагом будет проверка директивы server_name.

      Проверка директивы “server_name” для выбора совпадения

      Для оценки запросов с равноценным уровнем соответствия директив listen Nginx проверяет заголовок “Host” запроса. Это значение соответствует домену или IP-адресу, к которым клиент пытается подключиться.

      Nginx пытается подобрать наилучшее значение на основе директивы server_name в каждом из серверных блоков, которые являются наилучшим соответствием. Nginx оценивает их по следующей формуле:

      • Nginx сначала попытается найти серверный блок со значением server_name, точно соответствующим значению в заголовке запроса “Host”. Если такой элемент найден, для обслуживания запроса будет использован соответствующий блок. Если найдется несколько точных совпадений, используется первый вариант.
      • Если точных совпадений найдено не будет, Nginx будет пытаться найти серверный блок с директивой server_name, соответствующей первому подстановочному символу (обозначается символом * в начале названия в конфигурации). Если такой блок будет найден, он будет использоваться для обслуживания запроса. Если будут найдены несколько совпадений, для обслуживания запроса будет использоваться самое длинное из них.
      • Если совпадений с начальным подстановочным символом не будет, Nginx начнет искать серверный блок со значением server_name, соответствующим конечному подстановочному символу (указывается именем сервера с символом * в конфигурации). Если такой блок будет найден, он будет использоваться для обслуживания запроса. Если будут найдены несколько совпадений, для обслуживания запроса будет использоваться самое длинное из них.
      • Если совпадений с использованием конечного подстановочного символа найдено не будет, Nginx будет оценивать серверные блоки, определяющие server_name, с помощью регулярных выражений (обозначаются символом ~ перед названием). Для выполнения запроса будет использоваться первая директива server_name с регулярным выражением, соответствующим заголовку “Host”.
      • Если совпадений с регулярными выражениями не будет, Nginx выберет серверный блок по умолчанию для этого IP-адреса и порта.

      Каждая комбинация IP-адреса и порта имеет серверный блок, который будет по умолчанию использоваться, если с помощью вышеописанных методов не удастся принять решение. Для комбинации IP-адреса и порта это будет первый блок в конфигурации или блок, содержащий опцию default_server в директиве listen (имеет приоритет перед алгоритмом на основе первого найденного). Для каждой комбинации IP-адреса и порта может существовать только одна декларация default_server.

      Примеры

      Если будет определена директива server_name, которая точно соответствует значению заголовка “Host”, для обработки запроса будет выбран соответствующий серверный блок.

      В этом примере, если для запроса задать заголовку “Host” значение “host1.example.com”, будет выбран второй сервер:

      server {
          listen 80;
          server_name *.example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name host1.example.com;
      
          . . .
      
      }
      

      Если точного совпадения найдено не будет, Nginx проверяет наличие параметра server_name с подходящим начальным подстановочным символом. Для выполнения запроса будет выбрано самое длинное совпадение, начинающееся с подстановочного символа.

      В этом примере, если заголовок “Host” запроса будет иметь значение “www.example.org”, будет выбран второй серверный блок:

      server {
          listen 80;
          server_name www.example.*;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name *.example.org;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name *.org;
      
          . . .
      
      }
      

      Если не будет найдено совпадения с начальным подстановочным символом, Nginx проверит наличие совпадения с подстановочным символом в конце выражения. На этом шаге для обслуживания запроса выбирается наиболее длинное совпадение, заканчивающееся подстановочным символом.

      Например, если заголовок “Host” запроса имеет значение “www.example.com”, будет выбран третий серверный блок:

      server {
          listen 80;
          server_name host1.example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name www.example.*;
      
          . . .
      
      }
      

      Если совпадений с подстановочными символами найдено не будет, Nginx попытается подобрать директивы server_name, использующие регулярные выражения. Первое совпадающее регулярное выражение будет выбрано для реагирования на запрос.

      Например, если заголовок “Host” будет иметь значение “www.example.com”, для выполнения запроса будет выбран второй серверный блок:

      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name ~^(www|host1).*.example.com$;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name ~^(subdomain|set|www|host1).*.example.com$;
      
          . . .
      
      }
      

      Если никакие из вышеуказанных шагов не обеспечат выполнение запроса, запрос будет передан серверу по умолчанию для соответствующей комбинации IP-адреса и порта.

      Совпадающие блоки расположения

      Аналогично процессу, который Nginx использует для выбора серверного блока для обработки запроса, Nginx также имеет стабильный алгоритм для определения блока расположения сервера, который будет использоваться для обработки запросов.

      Синтаксис блока расположения

      Прежде чем рассказывать о том, как Nginx определяет, какой блок расположения использовать для обработки запросов, давайте посмотрим синтаксис, который можно увидеть в определениях блоков расположения. Блоки расположения находятся в серверных блоках (или других блоках расположения) и используются, чтобы решить, как обрабатывать URI запроса (часть запроса после доменного имени или IP-адрес/порта).

      Блоки расположения обычно принимают следующую форму:

      location optional_modifier location_match {
      
          . . .
      
      }
      

      location_match выше определяет, что Nginx следует проверять в отношении URI запроса. Наличие или отсутствие модификатора в примере выше влияет на то, как Nginx пытается подобрать соответствие блока расположения. Далее перечислены модификаторы, используемые для интерпретации блока расположения:

      • (нет): если модификаторов нет, расположение определяется как соответствие префикса. Это означает, что расположение будет сверяться с началом URI запроса для определения совпадения.
      • =: если используется знак равенства, блок будет считаться совпадающим, если URI запроса точно соответствует указанному расположению.
      • ~: знак тильды означает, что это расположение будет интерпретироваться как совпадение с регулярным выражением с учетом регистра.
      • ~*: знак тильды со звездочкой означают, что блок расположения будет интерпретироваться как совпадение с регулярным выражением без учета регистра.
      • ^~: знак елочки с тильдой означают, что если этот блок будет выбран как лучшее соответствие без регулярных выражений, сопоставление по регулярным выражением проводиться не будет.

      Примеры, демонстрирующие синтаксис блока расположения

      В качестве примера соответствия префиксов можно выбрать следующий блок расположения для реагирования на URI запроса вида /site, /site/page1/index.html или /site/index.html:

      location /site {
      
          . . .
      
      }
      

      Как пример точного соответствия URI запроса, этот блок всегда будет использоваться для ответа на URI запроса вида /page1. Он не будет использоваться для ответа на URI запроса /page1/index.html. Помните, что если выбран этот блок, и если запрос выполняется с использованием страницы индекса, произойдет внутренняя переадресация на другое расположение, которое фактически и будет обрабатывать запрос:

      location = /page1 {
      
          . . .
      
      }
      

      Как пример расположения, которое следует интерпретировать как регулярное выражение с учетом регистра, этот блок можно использовать для обработки запросов /tortoise.jpg, но не запросов /FLOWER.PNG:

      location ~ .(jpe?g|png|gif|ico)$ {
      
          . . .
      
      }
      

      Ниже показан похожий блок, поддерживающий сопоставление без учета регистра. Этот блок может обрабатывать как запросы /tortoise.jpg, так и запросы /FLOWER.PNG:

      location ~* .(jpe?g|png|gif|ico)$ {
      
          . . .
      
      }
      

      Наконец, этот блок не даст выполнять сопоставление с регулярными выражениями, если будет признан лучшим совпадением без регулярного выражения. Он сможет обрабатывать запросы /costumes/ninja.html:

      location ^~ /costumes {
      
          . . .
      
      }
      

      Как видите, модификаторы показывают, как следует интерпретировать блок расположения. Однако это не говорит нам, какой алгоритм Nginx использует для определения блока расположения, в который будет отправлен запрос. Этот вопрос мы рассмотрим далее.

      Как Nginx выбирает расположение для обработки запросов

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

      Учитывая описанные выше типы деклараций расположения, Nginx оценивает возможные контексты расположения, сравнивая URI запроса с каждым расположением. Для этого используется следующий алгоритм:

      • Для начала Nginx проверяет все совпадения расположения на базе префиксов (все типы расположений без регулярных выражений). Каждое расположение сверяется с полным URI запроса.
      • Во-первых, Nginx ищет точное совпадение. Если блок расположения, использующий модификатор =, будет точно соответствовать URI запроса, этот блок расположения сразу же будет выбран для обслуживания запроса.
      • Если точное соответствие (с модификатором =) блока расположения найдено не будет, Nginx перейдет к оценке неточных префиксов. Он определит самое длинное совпадающее расположение префикса для указанного URI запроса, которое будет оценено следующим образом:
        • Если совпадающее расположение с самым длинным префиксом имеет модификатор ^~, Nginx немедленно прекращает поиск и выбирает это расположение для обслуживания запроса.
        • Если совпадающее расположение с самым длинным префиксом не имеет модификатор ^~, Nginx временно сохраняет его, чтобы можно было сместить фокус поиска.
      • После определения и сохранения совпадающего расположения с самым длинным префиксом Nginx переходит к оценке расположений с регулярными выражениями (с учетом регистра и без учета регистра). Если расположения с регулярными выражениями будут найдены внутри совпадающего расположения с самым длинным префиксом, Nginx переместит их наверх списка расположений с регулярными выражениями для проверки. Затем Nginx попытается подбирать расположения регулярных выражений последовательно. Первое регулярное выражение, соответствующее URI запроса, будет сразу же выбрано для обслуживания запроса.
      • Если не будет найдено никаких расположений регулярных выражений, соответствующих URI запроса, для обслуживания запроса будет выбрано ранее сохраненное расположение префикса.

      Важно понимать, что по умолчанию Nginx будет отдавать совпадениям регулярных выражений приоритет перед совпадениями префиксов. Однако он вначале оценивает расположения префиксов, позволяя администратору переопределить этот приоритет, используя модификаторы = и ^~ при определении расположения.

      Также важно отметить, что хотя расположения префиксов обычно определяются на основе самого длинного и точного совпадения, оценка регулярных выражений останавливается при обнаружении первого совпадения. Это означает, что расположение в конфигурации важно для расположения регулярных выражений.

      Наконец, важно понимать, что совпадения регулярных выражений с самым длинным совпадением префикса будут иметь больший приоритет при оценке регулярных выражений Nginx. Они будут оцениваться по порядку до начала оценки любых других совпадений регулярных выражений. Максим Дунин, разработчик Nginx, дающий очень много полезных советов, объясняет в этом сообщении принципы работы данной части алгоритма выбора.

      Когда оценка блока расположения переходит к другим расположениям?

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

      Хотя это общее правило, позволяющее прогнозируемо проектировать блоки расположения, важно понимать, что иногда определенные директивы в выбранном расположении могут активировать новый поиск расположения. Исключения из правила использования только одного блока расположения могут влиять на фактический процесс обработки запроса и не соответствовать вашим ожиданиям при проектировании блоков расположения.

      Вот некоторые директивы, которые могут активировать подобную внутреннюю переадресацию:

      • index
      • try_files
      • rewrite
      • error_page

      Давайте вкратце рассмотрим их.

      Директива index всегда вызывает внутреннюю переадресацию, если используется для обработки запроса. Точные совпадения расположения часто используются для ускорения процесса выбора с немедленным завершением алгоритма. Однако, если точное совпадение расположения представляет собой каталог, есть вероятность, что запрос будет переадресован для фактической обработки в другое расположение.

      В этом примере первому расположению соответствует URI запроса /exact, но для обработки запроса директива index, унаследованная блоком, активирует внутреннюю переадресацию во второй блок:

      index index.html;
      
      location = /exact {
      
          . . .
      
      }
      
      location / {
      
          . . .
      
      }
      

      Если в примере выше вы захотите ограничить исполнение первым блоком, вам нужно будет подобрать другой метод выполнения запроса каталога. Например, вы можете задать недопустимый index этого блока и включить autoindex:

      location = /exact {
          index nothing_will_match;
          autoindex on;
      }
      
      location  / {
      
          . . .
      
      }
      

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

      Также расположение обработки может переоцениваться при использовании директивы try_files. Эта директива предписывает Nginx проверить существование набора файлов или каталогов с определенным именем. Последним параметром может быть URI, на который Nginx осуществляет внутреннюю переадресацию.

      Рассмотрим следующую конфигурацию:

      root /var/www/main;
      location / {
          try_files $uri $uri.html $uri/ /fallback/index.html;
      }
      
      location /fallback {
          root /var/www/another;
      }
      

      В примере выше, если мы делаем запрос /blahblah, запрос получит первое расположение. Оно попытается найти файл с именем blahblah в каталоге /var/www/main. Если это не получится сделать, будет выполнен поиск файла с именем blahblah.html. Затем будет выполнен поиск каталога blahblah/ в каталоге /var/www/main. Если все эти попытки закончатся неудачно, будет выполнена переадресация на /fallback/index.html. В этом случае будет активирован другой поиск расположения, который будет перехвачен вторым блоком расположения. Он выдаст файл /var/www/another/fallback/index.html.

      Также смена блока расположения возможна при использовании директивы rewrite. При использовании параметра last с директивой rewrite или при ее использовании без каких-либо параметров Nginx выполняет поиск нового подходящего расположения на основе результатов перезаписи.

      Например, если мы изменим последний пример и включим в него директиву rewrite, мы увидим, что запрос будет иногда передаваться во второе расположение без использования директивы try_files:

      root /var/www/main;
      location / {
          rewrite ^/rewriteme/(.*)$ /$1 last;
          try_files $uri $uri.html $uri/ /fallback/index.html;
      }
      
      location /fallback {
          root /var/www/another;
      }
      

      В примере выше запрос /rewriteme/hello будет первоначально обработан первым блоком расположения. Он будет перезаписан в /hello, и будет выполнен поиск расположения. В этом случае совпадением опять будет первое расположение, и будет выполнена обычная обработка try_files, возможно с возвратом к /fallback/index.html, если ничего не будет найдено (посредством внутренней переадресации try_files, как описано выше).

      Однако в случае запроса /rewriteme/fallback/hello первый блок опять будет соответствовать. В этом случае снова будет применена перезапись, в данном случае на /fallback/hello. Затем запрос будет выполнен вторым блоком расположения.

      Похожая ситуация происходит с директивой return при отправке кодов состояния 301 или 302. В данном случае разница заключается в том, чтобы обработать совершенно новый запрос в форме внешней видимой переадресации. Такая же ситуация может возникнуть с директивой rewrite при использовании флагов redirect или permanent. Однако эти поиски расположения не должны быть неожиданными, поскольку внешняя видимая переадресация всегда приводит к созданию нового запроса.

      Директива error_page может вызвать внутреннюю переадресацию, аналогичную созданной try_files. Эта директива используется, чтобы определить, что должно происходить при получении определенных кодов состояния. Она практически никогда не выполняется вместе с try_files, потому что обрабатывает весь жизненный цикл запроса.

      Рассмотрим следующий пример:

      root /var/www/main;
      
      location / {
          error_page 404 /another/whoops.html;
      }
      
      location /another {
          root /var/www;
      }
      

      Каждый запрос, кроме начинающихся с /another, будет обрабатываться первым блоком, который будет выводить файлы из /var/www/main. Однако, если файл не будет найден (статус 404), будет выполнена внутренняя переадресация на /another/whoops.html, в результате чего будет активирован новый поиск расположения, который попадет на второй блок. Файл будет выводиться из /var/www/another/whoops.html.

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

      Заключение

      Понимание способов обработки запросов клиентов в Nginx может значительно упростить работу администратора. Вы сможете понимать, какой серверный блок будет выбирать Nginx в ответ на запрос каждого клиента. Также вы поймете, как определить выбираемый блок расположения на основе URI запроса. Понимание того, как Nginx выбирает разные блоки, позволит вам отслеживать применяемые Nginx контексты для обслуживания каждого запроса.



      Source link

      Использование сервера SMTP Google


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Портативный сервер Google SMTP — один из малоизвестных функциональных компонентов Gmail и Google Apps. Вместо того, чтобы управлять собственным сервером исходящей почты в дроплете DigitalOcean или кластере Kubernetes, вы можете настроить сервер Google SMTP для работы с любым скриптом или с любой программой, которые вы хотите использовать для отправки электронной почты. Для этого потребуется (i) бесплатная учетная запись Gmail или (ii) платная учетная запись G Suite.

      Преимущества

      Вы можете использовать службу Google для сохранения и индексации электронных писем, отправляемых через сервер SMTP, чтобы резервные копии всех отправленных электронных писем хранились на серверах Google, и чтобы в них можно было производить поиск. Если вы решите использовать учетную запись Gmail или G Suite и для получения входящей почты, вся ваша почта будет храниться в одном удобном месте. Кроме того, поскольку сервер Google SMTP не использует порт 25, это уменьшит вероятность того, что провайдер заблокирует ваше электронное письмо или пометит его как спам.

      Настройки

      Для сервера Google SMTP требуется аутентификация, и здесь мы покажем, как настроить ее в почтовом клиенте или приложении:

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

      1. Сервер SMTP (т. е. сервер исходящей почты): smtp.gmail.com
      2. Имя пользователя SMTP: ваш полный адрес электронной почты Gmail или G Suite (например, [email protected] или example@your_domain)
      3. Пароль SMTP: ваш пароль Gmail или G Suite
      4. Порт SMTP: 465
      5. Требуется SMTP TLS/SSL: да

      Чтобы отправленные электронные письма автоматически копировались Google в папку Отправленные, необходимо также убедиться, что для вашей учетной записи включен доступ IMAP.

      Для этого следует перейти в настройки Gmail и открыть вкладку Переадресация и POP/IMAP. Затем следует перейти в раздел Доступ IMAP и убедиться, что доступ IMAP включен для вашей учетной записи.

      Примечание. Google будет автоматически перезаписывать строку От любого электронного письма, отправляемого через сервер SMTP Google, на связанный с учетной записью электронный адрес по умолчанию, если используемый адрес не содержится в списке адресов Отправлять почту как в настройках Gmail или G Suite. Чтобы просмотреть этот список, используйте вкладку Учетные записи и импорт на экране настроек.

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

      Лимиты отправки

      Google ограничивает объем почты, который пользователь может отправить через портативный сервер SMTP. Этот лимит ограничивает количество отправляемых сообщений в день 99 электронными сообщениями. Это ограничение автоматически снимается через 24 часа после достижения предела.

      Заключение

      Теперь у вас есть возможность использовать сервер Google SMTP. Если компактной версии недостаточно, вы можете установить и использовать postfix как сервер SMTP, служащий только для отправки сообщений.



      Source link

      Включение рендеринга на стороне сервера для приложения React


      Введение

      Рендеринг на стороне сервера (SSR) — это популярная методика рендеринга одностраничного клиентского приложения (SPA) на сервере и последующей отправки на клиент полностью отрисованной страницы. Это позволяет использовать динамические компоненты в качестве статической разметки HTML.

      Такой подход может быть полезным для поисковой оптимизации (SEO), когда при индексации код JavaScript не обрабатывается надлежащим образом. Это также может быть полезно в ситуациях, когда загрузка большого блока JavaScript затруднена из-за медленной скорости сети.

      В этом учебном модуле мы инициализируем приложение React с помощью Create React App, а затем изменим проект, чтобы он активировал рендеринг на стороне сервера.

      После прохождения учебного модуля вы получите работающий проект с клиентским приложением React и серверным приложением Express.

      Примечание. Также Next.js позволяет использовать современный подход к созданию статических приложений React и приложений, рендеринг которых выполняется на сервере.

      Предварительные требования

      Для данного обучающего руководства вам потребуется следующее:

      Этот учебный модуль был проверен с версиями Node v14.4.0 и npm v6.14.5.

      Шаг 1 — Создание приложения React и изменение компонента приложения

      Вначале мы используем npx для запуска нового приложения React с помощью последней версии Create React App.

      Назовем наше приложение my-ssr-app:

      Перейдем в новый каталог с помощью команды cd:

      cd my-ssr-app
      

      В заключение мы запустим наше новое приложение на стороне клиента для проверки установки:

      Вы должны увидеть пример приложения React в окне браузера.

      Теперь создадим компонент <Home>:

      Затем добавим следующий код в файл Home.js:

      src/Home.js

      import React from 'react';
      
      export default props => {
        return <h1>Hello {props.name}!</h1>;
      };
      

      При этом будет создан заголовок <h1> с сообщением "Hello“, адресованным имени.

      Далее мы выполним рендеринг <Home> в компоненте <App>. Откройте файл App.js:

      Затем заменим существующие строки кода новыми строками кода:

      src/App.js

      import React from 'react';
      import Home from './Home';
      
      export default () => {
        return <Home name="Sammy" />;
      };
      

      Они передают name в компонент <Home> так, что ожидаемое сообщение будет выглядеть так: "Hello Sammy!".

      В файле index.js нашего приложения мы будем использовать метод ReactDOM hydrate вместо render, чтобы указать блоку рендеринга DOM, чтобы мы восстанавливаем приложение после рендеринга на стороне сервера.

      Откроем файл index.js:

      Замените содержимое файла index.js следующим кодом:

      index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import App from './App';
      
      ReactDOM.hydrate(<App />, document.getElementById('root'));
      

      Мы завершили настройку на стороне клиента и теперь можем перейти к настройке на стороне сервера.

      Шаг 2 — Создание сервера Express и рендеринг компонента приложения

      Теперь наше приложение готово, и мы настроим сервер, который будет отправлять готовую версию после рендеринга. Для сервера мы будем использовать Express. Добавим его в проект, введя следующую команду в окне терминала:

      • npm install express@4.17.1

      Также можно использовать yarn:

      Создайте каталог server рядом с каталогом src нашего приложения:

      Затем создайте новый файл index.js, содержащий код сервера Express:

      Добавим необходимые элементы импорта и определим некоторые константы:

      server/index.js

      import path from 'path';
      import fs from 'fs';
      
      import React from 'react';
      import express from 'express';
      import ReactDOMServer from 'react-dom/server';
      
      import App from '../src/App';
      
      const PORT = process.env.PORT || 3006;
      const app = express();
      

      Затем добавим код сервера с обработкой ошибок:

      server/index.js

      // ...
      
      app.get('/', (req, res) => {
        const app = ReactDOMServer.renderToString(<App />);
      
        const indexFile = path.resolve('./build/index.html');
        fs.readFile(indexFile, 'utf8', (err, data) => {
          if (err) {
            console.error('Something went wrong:', err);
            return res.status(500).send('Oops, better luck next time!');
          }
      
          return res.send(
            data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
          );
        });
      });
      
      app.use(express.static('./build'));
      
      app.listen(PORT, () => {
        console.log(`Server is listening on port ${PORT}`);
      });
      

      Как видите, мы можем импортировать наш компонент <App> из клиентского приложения непосредственно с сервера.

      Здесь происходит три важные вещи:

      • Мы предписываем Express вывести содержимое каталога build в виде статичных файлов.
      • Мы будем использовать метод ReactDOMServer, renderToString, для рендеринга нашего приложения в статичную строку HTML.
      • Затем мы считываем статичный файл index.html из готового клиентского приложения, вставляем статичное содержимое нашего приложения в <div> с id "root", а затем отправляем результат в качестве ответа на запрос.

      Шаг 3 — Настройка webpack, Babel и скриптов npm

      Чтобы наш серверный код работал, нам нужно объединить его в комплект и провести транспиляцию, используя webpack и Babel. Для этого добавим в проект зависимости dev, введя следующую команду в окне терминала:

      • npm install webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --save-dev

      Также можно использовать yarn:

      • yarn add webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --dev

      Примечание. В более ранней версии этого учебного модуля мы устанавливали babel-core, babel-preset-env и babel-preset-react-app. Эти пакеты с тех пор были архивированы, и вместо них используются версии с одним репозиторием.

      Далее мы создадим файл конфигурации Babel:

      После этого добавьте готовые настройки env и react-app:

      .babelrc.json

      {
        "presets": [
          "@babel/preset-env",
          "@babel/preset-react"
        ]
      }
      

      Примечание. В более ранней версии этого учебного модуля мы использовали файл .babelrc (без расширения .json). Это был файл конфигурации Babel 6, однако для Babel 7 он больше не используется.

      Теперь мы создадим конфигурацию webpack для сервера, который использует Babel Loader для транспиляции кода. Начните с создания файла:

      Затем добавьте следующие конфигурации в файл webpack.server.js:

      webpack.server.js

      const path = require('path');
      const nodeExternals = require('webpack-node-externals');
      
      module.exports = {
        entry: './server/index.js',
      
        target: 'node',
      
        externals: [nodeExternals()],
      
        output: {
          path: path.resolve('server-build'),
          filename: 'index.js'
        },
      
        module: {
          rules: [
            {
              test: /.js$/,
              use: 'babel-loader'
            }
          ]
        }
      };
      

      С этой конфигурацией наш транспилированный серверный комплект будет выводиться в папку server-build в файле с именем called index.js.

      Обратите внимание на использование target: 'node' и externals: [nodeExternals()] из webpack-node-externals. При этом опускаются файлы из node_modules в комплекте, сервер сможет получить доступ к этим файлам напрямую.

      Это завершает установку зависимости и конфигурации webpack и Babel.

      Теперь мы снова вернемся к файлу package.json и добавим вспомогательные скрипты npm:

      Мы добавим скрипты dev:build-server, dev:start и dev в файл package.json, чтобы легко выполнять сборку и подачу нашего приложения SSR:

      package.json

      "scripts": {
        "dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",
        "dev:start": "nodemon ./server-build/index.js",
        "dev": "npm-run-all --parallel build dev:*",
        ...
      },
      

      Мы используем nodemon для перезапуска сервера при внесении изменений. Также мы используем npm-run-all для параллельного выполнения нескольких команд.

      Давайте установим эти пакеты, введя следующие команды в окне терминала:

      • npm install nodemon@2.0.4 npm-run-all@4.1.5 --save-dev

      Также можно использовать yarn:

      • yarn add nodemon@2.0.4 npm-run-all@4.1.5 --dev

      Так вы можете запустить следующий код для сборки приложения на стороне клиента, объединения в пакет и транспиляции кода сервера и запуска сервера на порту :3006:

      Также можно использовать yarn:

      Наша конфигурация сервера webpack будет следить за изменениями, и в случае изменений наш сервер перезапустится. Однако для клиентского приложения нам нужно выполнять сборку каждый раз при внесении изменений. Для этого есть открытая проблема.

      Откройте в браузере адрес http://localhost:3006/ и вы увидите приложение после рендеринга на стороне сервера.

      Ранее исходный код показал следующее:

      Output

      <div id="root"></div>

      С внесенными изменениями исходный код показывает:

      Output

      <div id="root"><h1 data-reactroot="">Hello <!-- -->Sammy<!-- -->!</h1></div>

      При рендеринге на стороне сервера компонент <App> был успешно конвертирован в формат HTML.

      Заключение

      В этом учебном модуле мы инициализировали приложение React и активировали рендеринг на стороне сервера.

      Так мы просто оценили доступные возможности. Все становится сложнее, если в приложение после рендеринга на стороне сервера нужно добавить маршрутизацию, доставку данных или Redux.

      Преимущество использования SSR заключается в наличии приложения, содержимое которого может просмотреть даже сборщик, не выполняющий код JavaScript. Это поможет для поисковой оптимизации (SEO) и отправки метаданных на каналы социальных сетей.

      SSR также часто помогает решить проблемы с производительностью, потому что полностью загруженное приложение отправляется с сервера при первом запросе. С необычными приложениями ситуация может меняться, потому что SSR требует довольно сложной настройки и создает более существенную нагрузку на сервер. Использование рендеринга на стороне сервера для приложения React зависит от конкретных потребностей и от того, какие компромиссы имеют смысл для вашего сценария использования.

      Если вы хотите узнать больше о React, почитайте нашу серию «Программирование на React.js» или посмотрите страницу тем React, где вы найдете упражнения и программные проекты.



      Source link