Страницы

Поиск по вопросам

Показаны сообщения с ярлыком веб-сервер. Показать все сообщения
Показаны сообщения с ярлыком веб-сервер. Показать все сообщения

воскресенье, 29 марта 2020 г.

Как устранить ошибку 403?

#linux #htaccess #битрикс #веб_сервер #хостинг


Есть выделенный сервер на CentOS 7 на котором хостится муниципальный сайт на битриксе.
Проблема в том, что сайт (в основном, по ночам) становится не доступен и судя по логам
Nginx выдаёт ошибку 403. "Ложится" он на время от 5 до 50 минут и потом сам "поднимается".
Доступ для пользователей к директории с битрексом есть на чтение и запись. htaccess
(на сколько я могу судить) стандартный битрексовский. Наименование файлов корректное.
Думал, что это может быть из-за бэкапов, но "ложится" сайт по времени рандомно. Подскажите,
пожалуйста, что может быть?
    


Ответы

Ответ 1



Требуется для начала исследовать кто выдает ошибку 403: Nginx/Apache или PHP-код. Найти это можно в логах nginx/apache и по заголовкам ответа (X-Powered-By). Далее требуется разбиратся с логами конкретного сервера nginx/apache (и искать в логе ошибок причину ошибки 403) либо при выдаче этого кодом php - разбиратся в нём

Ответ 2



Нашёл причину (точнее, помог коллега). Оказалось, всё было в банальном фишинге сайта (использовали rk.php для перенаправления на свои сайты). По этой причине, сервер временно блокировал балансировщик пересылающий эти запросы серверу. Решается это всё в настройках Битрикса. Нужно зайти в интерфейс Битрикса, в раздел меню "Проактивная защита", дальше в "Защита редиректов" и включить защиту редиректов. Битрикс будет подписывать ссылки через rk.php.

воскресенье, 8 марта 2020 г.

rails puma постоянный рост используемой памяти

#ruby_on_rails #веб_сервер


Имеется небольшое приложение на rails(4.2.6), в качестве веб-сервера используется
nginx+puma(3.4.0). Сервер Debian 8.1 64bit. 
Заметил одну странную вещь, с каждым запросом, используемое процессами puma количество
памяти растет, и никогда не снижается(во время отсутствия каких либо запросов). Это
приводит к тому, что  ОЗУ на сервере (512 мб) заканчивается, и останавливается служба
postgres(9.4). Пробовал разное количество воркеров, нитей, работа в кластерном режиме
и в обычном - результат всегда один и тот же. После нескольких тяжелых запросов ОЗУ
заканчивается.
Неужели так и должно быть? Какие есть пути устранения такой проблемы? По информации,
которую я нашел, Puma - это один из лучших выборов для избежания проблемы медленных
клиентов и долгих запросов.
Текущий конфиг такой:

#!/usr/bin/env puma

...

threads 2,4

...
workers 1

preload_app!

on_restart do
  puts 'Refreshing Gemfile'
  ENV["BUNDLE_GEMFILE"] = "/home/.../current/Gemfile"
end

on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

    


Ответы

Ответ 1



Описанное в ответе реально не совсем правда. GC может освобождать память, но темпы освобождения могут быть меньше темпов роста. Вне синтетических тестов обычно так и есть. Ответ подлежит переработке с учётом этого факта. Да, это из-за особенностей работы GC в самом Ruby. Он держит собственный пул памяти, увеличивая его при необходимости, но никогда не уменьшая. И тому есть причина. Объекты, "подметённые" сборщиком мусора, освобождают память с точки зрения интерпретатора, но не с точки зрения ОС. Посему, только тот факт, что память занята процессом, не означает, что в ней действительно есть что-то ценное для программы, скорее всего, это просто "запас", в котором интерпретатор будет размещать новые объекты сам, не дёргая аллокатор ОС. Соответственно, в каждый момент времени процесс будет занимать максимум того, что ему было нужно за всё время его жизни. Размер пула будет с небольшим запасом равен пиковому потреблению памяти. Беда с большими объектами в том, что они требуют большие последовательные области памяти. И если такой блок в пуле не находится, то... пул ещё увеличивается на такую величину, чтобы большой объект влез! Эту проблему можно было б частично победить, используя сборщик мусора с "уплотнением" (compaction), когда GC в процессе работы "перекладывает" мелкие объекты поближе друг к другу, тем самым образуя более крупные последовательные области свободной памяти. Это же могло бы позволить отдавать крупные "хвосты" незанятой памяти обратно в ОС. Но это здорово усложняет работу С-шных расширений, которые запоминают, где был объект, непосредственно по адресу в памяти. Нельзя просто сказать им "я вон тот объект подвинул, имей в виду". Сделать можно много чего. Пол-гигабайта на целое рельсовое приложение и хранилища данных это... крайне немного. Есть смысл добавить, если не физической ОЗУ, то хотя бы подкачки. Подкрутить garbage collector на более осторожное расширение пула и частые срабатывания. Это вряд ли поможет, но может немножко отсрочить неизбежное. Это довольно обширная и опасная тема, требующая тщательного стресс-тестирования на каждое изменение. (Самый богатый на приключения) Реально снизить пиковое потребление памяти, отдавая большие ответы по кусочкам, чтобы GC успевал подчищать то, что уже отослано клиенту. Есть ActionController::Live, с помощью которого можно писать ответ в поток по кусочкам, не загружая все исходные данные для него в память.

Что включать в Docker-образ

#администрирование #веб_сервер #docker #сборка


Имеется веб-сайт на Django, на боевом сервере для запуска использую связку uWSGI/Nginx,
локальная разработка - virtualenv/dev-сервер Django

Из некоторых вопросов, которые задал на SO:


Распространение Docker-образов 
Запуск Docker-образов на боевом сервере
Запуск сайта из Docker-образа VS запуск c традиционными средствами (uWSGI, Nginx, Apache)


появилось еще пара.

Что включать в образ Docker?

Мы имеем production-версию и developer-версию. Как понимаю, в репозиториях распространяют
production. 

Имеет ли смысл создавать docker-образ для developer-версии (применительно ко мне,
код проекта и виртуальное окружение virtualenv)? Или разработчику достаточно только
кода из репозитория, чтобы начать разработку?

Где создают docker-образ production-версии?

На боевом сервере имеется проект, который работает на указанной связке - uwsgi/nginx/упаковщик
с production-настройками. Собирать образ я должен на боевом сервере?
    


Ответы

Ответ 1



Не должно быть понятия production и non-production версия образа. Разработчики, тестировщики, эксплуатационники и все остальные должны использовать одну и туже версию образа. Процесс разработки может быть устроен совершенно по разному, но если используется docker-образ (например, сервер разрабатываемый другой группой) в качестве внешней зависимости, то он должен браться из того же источника, что и для других нужд - тестирование, эксплуатация и т.п. Как, где и чем создают docker-образы? Процесс изготовления образа должен быть полностью автоматизирован, что бы избежать ошибок и сделать процесс повторяемым. Приблизительно процесс изготовления образа выглядит так (я опускаю некоторые шаги): Разработчик дописал код и протестировал его локально, в том числе и сборку образа. Разработчик заливает код в систему контроля версий. Робот собирает проект и создает артефакты для развертывания (мнифицирует все, объединяет в общий пакет и т.п.). Необязательный шаг - артефакты помещаются в хранилище. Робот собирает docker-образ. Необязательный шаг - робот подписывает образ. Робот заливает образ в реестр. Если нет специфических требовани, то для управления процессом создания можно использовать любой Continuous Integration сервер - Jenkins, TeamCity, Bamboo и т.д. У них у всех есть соответсвующие плагины или можно написать простые шел-скрипты и создавать образы стандартной командой docker build. Что включать в docker-образ? Сложно дать однозначный ответ на этот вопрос, так как многое зависит от типа образа и личных предпочтений. Я напишу как бы я поступил с сервером на Django. Я мало работал с Django, так что поправьте меня, если я говорю что-то несоответствующее действительности. Если проект только начинается и нагрузка на сервис будет маленькая, то я бы поместил все (кроме БД) в один образ. Т.е. образ будет содержать: Python фиксированной версии установленный как системный (без virtualenv и пр) nginx/Apache фиксированных версий с нужными настройками собственно ваше приложение, взятое как артефакт для развертывания и развернутое внутри образа БД либо в отдельный образ, либо на отдельный сервер без использования контейнерезации. Если БД идёт отдельным образом, то важно позаботится о сохранении данных на внешний (по отношению к контейнеру) раздел диска. В противном случае данные будут утеряны при перезапуске контейнера. Если нагрузка будет значительная и есть много статических страниц, то я бы сделал несколько образов: Образ со статическими страницами nginx/Apache статическая часть вашего приложения Образ с динамической частью Python часть вашего приложения (Django) Образ балансировщиком (необязательный) nginx/HAProxy/Varnish/etc Если проект очень большой, то возможно статическую и динамическую часть делают разные команды и в этом случае они и буду отвечать за подготовку Dockerfile к своей части проекта.

суббота, 15 февраля 2020 г.

В чем отличие между HTTP методами HEAD и OPTIONS?

#веб_программирование #сеть #http #веб_сервер


В чем отличие между HTTP методами HEAD и OPTIONS? я знаю лишь что в ответ на OPTIONS
сервер должен отдать Allow со списком поддерживаемых методов. Есть ли еще какие либо
концептуальные/технические отличия?
    


Ответы

Ответ 1



У этих запросов разное назначение: HEAD - служит для проверки существования ресурса, он полностью аналогичен GET, но без возврата тела ответа OPTIONS - служит для получения параметров для ресурса или для сервера в целом и при этом сам ресурс ни как не затрагивается (то есть это более дешевая операция по сравнению с HEAD) OPTIONS возвращает параметры в заголовке. Список параметров зависит о ресурса и/или сервера. Обычно это заголовок Allow, который описывает какие методы доступны для ресурса.

Ответ 2



HEAD Данный метод по своей сути похож на GET, но сервер отвечает на запрос одним лишь заголовком. (Отсюда и название метода.) Применяется, например, чтобы узнать, существует ли в сети тот или иной URL и не произошло ли каких-нибудь изменений. OPTIONS Метод представляет запрос информации об опциях соединения, доступных в цепочке запросов/ответов, идентифицируемой запрашиваемым URI (Request-URI). Этот метод позволяет клиенту определять опции и/или требования, связанные с ресурсом, или возможностями сервера, но не производя никаких действий над ресурсом и не инициируя его загрузку.

Ответ 3



Отличие этих методов - в том, что HEAD запрашивает информацию о ресурсе, а OPTIONS запрашивает информацию о методах доступа к ресурсу. Более подробно. Заголовки, возвращаемые методом HEAD, обязаны соответствовать заголовкам, возвращаемым методом GET. При этом, метод HEAD не имеет никакого отношения к методам POST, PUT, DELETE и прочим. В то же время, настройки доступа, возвращаемые методом OPTIONS, имеют отношение сразу ко всем методом ресурса - GET, POST, PUT, DELETE и пр. Если говорить о применении, то метод HEAD может использоваться чтобы получить информацию о странице без скачивания самой страницы. Метод OPTIONS же используется, в основном, механизмом Preflight request в CORS или для обнаружения поддерживаемых сервером фич в WebDAV.

четверг, 13 февраля 2020 г.

Как узнать ip адрес

#php #веб_сервер


Необходимо узнать IP адрес, с которого пришел запрос к моему серверу.
    


Ответы

Ответ 1



$_SERVER['REMOTE_ADDR']

среда, 12 февраля 2020 г.

libcurl - как парсить HTTP заголовок?

#cpp #http #curl #парсер #веб_сервер


Учусь писать HTTP веб-сервер. Сам сервер есть. Запускаю сервак, посылаю HTTP-запрос
curl -D dumpbin 127.0.0.1:2000. На сервере сохраняю запрос в строковую переменную.
Теперь (и я не знаю, как это сделать) мне надо распарсить весь полученный запрос, составить
ответ и отправить обратно. С отправкой я справлюсь, а вот с парсингом и с составлением
ответа возникли проблемы. 
Сказали, что следует использовать libcurl, но я не понимаю, как им можно парсить
готовые строки и можно ли.
Помогите, пожалуйста, примером такого использования этой либы, если он возможен.
Посоветуйте, какие можно использовать библиотеки еще, чтобы решить эту задачу.

//Вид запроса (это то, что вернул мой эхо-сервер при использовании утилиты curl): 

    request = "GET /index.html HTTP/1.1\r\n"
              "Host: 127.0.0.1:5991\r\n"
              "User-Agent: curl/7.47.0\r\n"
              "Accept: */*\r\n"
              "\r\n";

    //Вид ответа (это я прочитал в комментах к заданию на степике):
    answer = "HTTP/1.0 200 OK\r\n"
             "Content-length: %d\r\n"
             "Connection: close\r\n"
             "Content-Type: text/html\r\n"
             "\r\n"
             "%s";


Суть в том, что есть данные, которые разделены \r\n и заканчиваются двойным переводом
каретки и строки \r\n\r\n
    


Ответы

Ответ 1



tl;dr см. конец ответа для реализации парсера заголовков licurl используют, как правило, для написания клиентов, смотрите FAQ: 5.17 Can I write a server with libcurl? No. libcurl offers no functions or building blocks to build any kind of internet protocol server. libcurl is only a client-side library. For server libraries, you need to continue your search elsewhere but there exist many good open source ones out there for most protocols you could possibly want a server for. And there are really good stand-alone ones that have been tested and proven for many years. There's no need for you to reinvent them! В вольном переводе: 5.17 Могу ли я использовать libcurl для написания сервера? Нет. libcurl не предлагает функций или других строительных блоков для создания какого либо IP сервера. libcurl это только клиентская библиотека. Вам нужно продолжить ваши поиски библиотеки для построения сервера. Существует большое количество таких библиотек с открытым исходным кодом для большинства протоколов. Также существует большое кол-во проверенных годами stand-alone приложений. Вам не стоит изобретать их заново. Хотя для построения клиента libcurl действительно очень удобная вещь, как упомянул в своем ответе @Pink Tux. В том числе заголовки вы получаете уже разбитые по одному. Для построения сервера, как один из вариантов, можно использовать mongoose (на c, в виде пары .c/.h, добавляемой к вашему проекту, либо одну из многочисленных c++ оберток). Пример минимального сервера достаточно громоздкий, поэтому приводить здесь не буду. Можно посмотреть здесь. Еще один вариант - библиотека cpp-netlib которая изначально планировалась как часть boost (но потом планы разработчиков поменялись). В простейшем виде http сервер выглядит так: namespace http = boost::network::http; struct handler; typedef http::server http_server; struct handler { void operator() (http_server::request const &request, http_server::response &response) { response = http_server::response::stock_reply( http_server::response::ok, "Hello, world!"); } void log(http_server::string_type const &info) { std::cerr << "ERROR: " << info << '\n'; } }; int main(int arg, char * argv[]) { handler handler_; http_server::options options(handler_); http_server server_( options.address("0.0.0.0") .port("8000")); server_.run(); } Если вам нужно что-то еще более высокоуровневое (с поддержкой шаблонов, json, middlewares), можно взять crow, которая подключается к вашему проекту одним заголовочным файлом. По духу это напоминает Python Flask. Пример сервера: #include "crow_all.h" int main() { crow::SimpleApp app; CROW_ROUTE(app, "/")([](){ return "Hello world"; }); app.port(18080).multithreaded().run(); } Если все-таки интересно самому разобрать заголовки, то можно например таким кодом: std::istringstream rawstream(raw); std::map headers; std::string header; while (std::getline(rawstream, header) && header != "\r") { std::string::size_type index = header.find(':'); if (index != std::string::npos) { headers.insert(std::make_pair( boost::algorithm::trim_copy(header.substr(0, index)), boost::algorithm::trim_copy(header.substr(index + 1)) )); } } В raw типа std::string помещаете строку с вашими заголовками (либо даже целиком запрос клиента), в headers получаете map из имен/значений заголовков. Используете к примеру так: std::cout << headers["Content-Type"] << std::endl; Обратите внимание, что по стандарту имя заголовка может иметь любой регистр. Поэтому CONTENT-TYPE, content-type и Content-Type это все один заголовок, и для удобства можно перед добавлением в map привести имя заголовка в нижний регистр: headers.insert(std::make_pair( boost::algorithm::to_lower_copy(boost::algorithm::trim_copy(header.substr(0, index))), boost::algorithm::trim_copy(header.substr(index + 1)) )); Действующий пример на Ideone.com. P.S. Для тестирования вместо curl можно использовать postman в котором можно задавать заголовки, тело страниц (в том числе json), сохранять наборы запросов и т.д.

Ответ 2



libcurl предоставляет возможность задавать обработчик для разных данных. Например, чтобы "поймать" заголовки ответа, можно воспользоваться следующим подходом: static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { /* * Эта функция будет вызываться на каждый возвращаемый заголовок. */ return nitems * size; } int main() { CURL *curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "http://yahoo.com/"); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); curl_easy_perform(curl); } return 0; } Подробней - см. в документации по libcurl. Кроме того, для C++ есть обёртки, например, curlcpp.

воскресенье, 9 февраля 2020 г.

Как посмотреть по http наличие файла? (node js)

#javascript #nodejs #http #веб_сервер


Как на ноде проверить наличие файла в дерриктории http? Мне не нужно его скачивать,
просматривать, а просто знать - есть он или нет.
По сути мне нужно отправить http запрос например на  урл http://example.org/file.txt
и получить ответ, распарсить и если 404 не наблюдаю то гуд иначе не гуд... 
    


Ответы

Ответ 1



Для определения наличия ресурса можно использовать метод HEAD. В ответ на HEAD запрос не будет возвращаться содержимое файла. При этом потребуется проверить код ответа: 200 - ресурс есть. Вы можете использовать, например, модуль request, который дочтаточно прост и содержит функцию head: const request = require("request"); request.head("http://www.cfcl.com/vlb/Cuute/f/a-few-bricks.txt").on("response", res => { global.console.log(res.statusCode); }); или использовать модуль http самой ноды и функцию request const http = require("http"); const req = http.request( { hostname: "www.cfcl.com", path: "/vlb/Cuute/f/a-few-bricks.txt", method: "HEAD", }, res => { global.console.log(res.statusCode); }, ); req.end();

среда, 29 января 2020 г.

Как понять нагрузку на сервер?

#linux #сервер #centos #веб_сервер #статистика


может кто-то объяснить как понять вот эти цифры?

то что они показывают нагрузку на 1 минуту, 5 минут и 15 минут - знаю

а что означает например 0.67?

у нас 2 процессора по 12 ядер каждый

до какого значения должно быть максимум нагрузка чтобы чтобы сервер работал нормально?
    


Ответы

Ответ 1



Показатель Load Average показывает, сколько в среднем процессов в системе стояло в очереди на выполнение и выполнялось в каждом из трёх указанных промежутков времени. Здесь «стоит в очереди на выполнение» означает, что процесс не находится в состоянии "sleep" и не ожидает завершения операции ввода-вывода, но и не является выполняющимся в данный квант процеесорного времени. То есть ему не мешает стать выполняющимся ничего, кроме того факта, что процессор в данный момент занят выполнением другого процесса. Например, если на иначе ничем не занятой одноядерной системе запустить три процесса, занимающихся активными вычислениями чего-либо (то есть не задействующих ввод-вывод), то показатель LA будет находиться в районе чуть выше 3.00. Что́ есть «сервер работает нормально» обычно точно не известно, поэтому, как уже отметили в комментарии, надо проводить нагрузочное тестирование. Иногда и при достаточно низких показателях LA, но при сильной нагрузке на диск производительность сервера начинает проваливаться заметно раньше теоретического «LA / кол-во ядер > 1», но я обычно рассматриваю именно это значение (то есть когда LA больше, чем количество ядер/HT-потоков) как предельное, при достижении которого процессор сервера явно нуждается в апгрейде (или ПО — в оптимизации). Или, по-другому выражаясь, неизвестно, будете ли вы отмечать тормоза при LA < 24, но то, что при LA > 24 вы их точно будете отмечать — это к бабке не ходи.

Ответ 2



htop + F2 (Load +) Средние значения нагрузки в Linux — это «средние значения нагрузки системы», показывающие потребность в исполняемых потоках (задачах) в виде усреднённого количества исполняемых и ожидающих потоков. Это мера нагрузки, которая может превышать обрабатываемую системой в данный момент а что означает например 0.67? (нагрузку ,была на 15 мин назад ) Load Average — среднее значение загруженности системы за период времени (в дальнейшем LA). Три значения показывают усреднённую нагрузку за последние 1, 5 и 15 минут. LA является одним из самых спорных показателей. Можно найти множество противоречивых статей, какое значение считать нормальным. Обычно принимается, что значение 0 это простой ядра, а значение 1 это полная нагрузка ядра. Оценить показатель средней нагрузки можно только зная количество ядер в системе. Узнать сколько ядер доступно можно командой: dmidecode -t processor | grep "Core Enabled:" Core Enabled: 6 Core Enabled: 6 Видим, что на данной системе находится 12 физических ядер (6+6). Соответственно, нормальный показатель LA должен быть менее 12. Смотри atop

суббота, 11 января 2020 г.

Node.js приложение запустить на хостинге

#nodejs #веб_сервер #хостинг


Всем привет!
У меня проблемка. Перерыл google и satackoverflow но не могу найти решения.
Написал сайт на Node+react+mongo. Соответственно все делал на локале (Запуск сервера
и коддинг). Теперь взял пробное место на хостинге Timeweb. Залил туда проект, установил
на сервере Node + все зависимости проекта. Код работает если вызываю команду node app.js,
который запускает этот код 



const express = require('express');
const MongoClient = require('mongodb').MongoClient;
// const path = require('path');
// const logger = require('morgan');
// const multer = require('multer');
const bodyParser = require('body-parser');
const db = require('./config/db');
const app = express();
const port = 8000;

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.static(`${__dirname}/public`));

const dirname = __dirname;

MongoClient.connect(db.url, (err, database) => {

	if (err) return console.log(err)
	require('./server/routes')(app, database, dirname);

app.listen(process.env.PORT || port, () => { console.log(process.env.PORT || port) })

})




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

Можно хотябы ссылку на какуюто информаию или туториал.

Извините а такой банальный вопрос.
Всем спасибо заранее!)
    


Ответы

Ответ 1



Зависит от того, что у вас там за сервер. Для nginx делать так: Для начала сам nginx установить sudo apt-get -y install nginx. Открыть конфиг файл sudo nano /etc/nginx/sites-available/default и написать там: server { listen 80; server_name имя_вашего_домена; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; } } Перезапустить nginx sudo service nginx restart. Остаётся только в настройках DNS вашего домена направить его на ip этого сервера.

PHPStorm - автоматический Upload отредактированных файлов на Remote Host

#phpstorm #веб_сервер #jetbrains


В PHPStorm 2017 можно произвести настройки, при которых отредактированные файлы будут
автоматически сохраняться при потере фокуса с PHPStorm.

Также есть возможность редактировать файлы на Remote Host, при этом над каждым редактируемым
файлом есть панель с кнопками "Compare", "Upload", при этом необходимо каждый отредактированный
файл отправлять на Remote Host через нажатие кнопки "Upload".

Можно ли автоматически делать "Upload" отредактированных файлов, при потере фокуса
с PHPStorm?
    


Ответы

Ответ 1



Да, можно. Разрешение на автоматическую загрузку файлов заходишь в верхнем меню - Tools -> Deployment -> Automatic Upload (always) Разрешить загрузку внешних файлов Еще, для удобства, зайди в Tools -> Deployment -> Options.. и галочкой отметь Upload external changes , чтобы при добавлении в папку файлов (не через программу PHPStorm), программа сама находила те файлы и перекидывала бы на сервак. Видео-урок настройки remote host YouTube

вторник, 24 декабря 2019 г.

Web сервер с функциями отладки

#сеть #веб_сервер #тестирование


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

Сейчас используется два подхода - с применением утилит на подобии clumsy, которые
вносят сбои на уровне сетевого адаптера, и ручное снятие процесса веб сервера flask
(для симуляции обрыва соединения).

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

Из видимых решений - писать свой web сервер, взяв например за основу mongoose.

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


Ответы

Ответ 1



я бы воспользовался вот этим http://www.haka-security.org/ или прокси-сервером/скриптом. Не думаю, что вам будет сложно наваять прокси-скрипт, который пробрасывает соединения, обрывает их, когда нужно, либо возвращает нужную вам ошибку, спит время от времени (или по настройкам вашего теста - для детерминированности), и уж во всяком случае, это проще и надежней, чем кастомный веб-сервер

воскресенье, 15 декабря 2019 г.

Как чистить оперативную память сервера после выполнения php скрипта?

#php #linux #сервер #веб_сервер #memory


Проблема собственно вот в чем: мои скрипты php после отработки засоряют оперативную
память сервера,но не все конечно. Системный администратор после изучения этой темы
говорит что память засоряется после моих скриптов и они являются активными процессами(
как я понял спящими по букве S)

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

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

Или может добавлять какуюто строку кода в php скрипт чтобы сервер убивал соединение?

Да кстати в циклах использую

break;


Также использовал

ini_set('memory_limit', '1024M');


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

unset($some_param);


Откуда начинать копать? Статьи по оптимизации кода читал, применяю их на практике,
но как известно в этом направлении можно далеко уйти и писать на Ассемблере

Добавлено минутой позже:

Также использую в коде 

exit();


в том случае если код не имеет смысла дальше выполнять - или вместо него необходимо
использовать 

die();


Добавлено 5 минутами позже:

Это веб сервер.
Пользователь загружает фаил эксель для парсинга и получается вышеописанное
NGINX 1.6.2 PHP5-FPM без APACHE
5.6 PHP версия

Добавлено через 9 часов:

на форуме говорится о команде 

strace -p 


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

На ресурсе сказано что данный режим может быть вызван недостатком мощностей процессора(но
по сути какой бы мощный процессор не был, нагрузить его на 100% можно), также сказано
что такое поведение наблюдается когда в коде php есть команда

sleep($count_of_seconds);


Да - данная команда ввергает процесс в состояние Sleep, но здесь нет зависимости
от количества потребляемой памяти.

Как мне объяснил системный администратор - сколько пользователей на сайте, столько
будет спящих www-data, следовательно состояние sleep вполне нормально, ненормально
количество удерживаемой для этого процесса памяти.

Добавлено 22,02,2017

Вот что происходит когда остаются 3 спящих процесса


    


Ответы

Ответ 1



Моя история похожа. Конфигурация: nginx+php-fpm Тяжелый запрос на бекенде и при заходе на него 3-5 раз подряд, память забивалась и вылетала ошибка: 502 bad gateway При этом память сама не отчищалась. Мой конфиг до проблемы: pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 25 pm.max_requests = 500 Конфиг после: pm = dynamic pm.max_children = 10 pm.start_servers = 3 pm.min_spare_servers = 2 pm.max_spare_servers = 5 pm.max_requests = 500 Попробуйте так.

Ответ 2



Считаю 374 мегабайта RES совершенно нормальным значением для php-fpm. То есть, на мой взгляд, у вас проблема совершенно не в вашем коде, а либо в использовании PHP как такового, либо просто в неадекватной задачам конфигурации сервера (мало RAM для запущенного набора процессов).

Ответ 3



Тут оказывается не только дело в скриптах но и в настройках php-fpm а именно надо поиграться с настройками /etc/php5/fpm/pool.d/www.conf а именно pm.max_requests request_terminate_timeout request_terminate_timeout устанавливает максимальное время выполнения дочернего процесса, прежде чем он будет уничтожен. Это позволяет избегать долгих запросов, если по какой-либо причине было изменено значение max_execution_time в настройках интерпретатора. Значение стоит установить исходя из логики обрабатываемых приложений, скажем 60s (1 минута).

четверг, 5 декабря 2019 г.

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

#php #nodejs #многопоточность #веб_сервер #процесс


Я, допустим, написал одно веб-приложение на php, которое располагается на моем сервере.
То есть имеется один код. Но клиентов ведь много.

Допустим, сразу 10 человек подключаются к моему серверу. И как это один единственный
код обрабатывает запросы всех человек сразу? Он ведь это делает одновременно.

Я слышал, это связано с потоками и процессами, какими-то экземплярами. В общем. объясните
это, пожалуйста.

А ещё объясните, чем архитектура php приложения отличается от архитектуры node.js
приложения. Слышал, разница в каких-то потоках, то есть в их количестве.
    


Ответы

Ответ 1



Есть HTTP. Он работает по схеме "клиент послал запрос @ сервер ответил". В случае с PHP обычно есть вебсервер. Многопоточностью занимается обычно именно он. Он принимает запрос и передаёт его интерпретатору PHP. Если вебсервер использует CGI, то получив запрос, он запускает новый процесс интерпретатора PHP, передаёт ему параметры запроса, передаёт выведенные интерпретатором данные обратно клиенту и дожидается, пока процесс закончит работу. Параллельная обработка может происходить за счёт того, что вебсервер контролирует несколько процессов интерпретатора PHP. Запуск каждый раз нового процесса потребляет оперативную память, разумеется. И время. Припоминаю случаи, когда слишком много параллельных клиентов приводили к слишком большому потреблению ОЗУ и слабопредсказуемым последствиям. Если вебсервер использует FastCGI, то он заранее, при запуске, поднимает набор процессов интерпретатора PHP в особом режиме. В наборе каждый интерпретатор может быть занят или свободен. Вебсервер, получив запрос, берёт из своего набора свободный процесс интерпретатора (делая его занятым), сообщает ему (с помощью сокетов) параметры запроса, и выдаваемый ответ передаёт пользователю, после чего освобождает интерпретатор. По сравнению с CGI, процессы больше не завершаются/запускаются на каждый запрос, что уже неплохо. И процессов конечное количество. Если каждый из них ограничить по памяти, они будут потреблять предсказуемое количество ресурсов. Но осталась другая проблема, актуальная и для CGI: код приложения обычно перед непосредственно обработкой запроса выполняет некоторую подготовительную работу: загружает библиотеки, настройки, создаёт системные объекты. Это довольно внушительная часть кода, которая для разных запросов приводит к одним и тем же результатам. Но эти результаты не сохраняются, а выбрасываются в никуда после каждого запроса. Не круто. На этой ноте выходим из мира PHP, пропуская экзотические способы заставить PHP обслуживать запросы, и переходим к более интересным темам. Про что вы там спрашивали... NodeJS! Окей. JavaScript исторически достаточно сильно привязан к циклу событий. Сам по себе он не многопоточен, программы на JS принято разделять на небольшие порции, между которыми делать всё равно нечего и можно дать поработать кому-то ещё. Поэтому очень часто код на NodeJS "общается с внешним миром" асинхронно: он не просто говорит "сделай Х", а "сделай Х, и когда получишь результат, сообщи в Y, а я пойду отдыхать". Действий, которые полезно выполнять асинхронно в мире веб-сайтов довольно много: ожидание поступления данных от клиента, ожидание результатов от базы данных, ожидание записи в файл, и прочая, прочая. Вебсервер в Node.js собственный, встроенный. И поскольку он управляется непосредственно из интерпретатора, с контролем над тем, что выполнить до и после, можно заранее один раз подготовить объекты и выполнять запросы, не конструируя их по новой каждый раз. Внутри Node.js крутится "цикл событий" вокруг "очереди событий" (упрощённо). Цикл событий: {берёт очередное событие из очереди, выполняет связанное с ним действие} и так по кругу, причём действия могут добавлять новые записи в очередь событий. Когда запросы поступают по очереди, очередь событий не раздувается более чем на 1 запись и порядок выполнения кода в целом похож на обычный: приходит запрос (в очередь!) цикл событий обнаружиает пришедший запрос (из очереди!) вызвал обработчик f для запроса обработчик f попросил выполнить запрос в БД (в очередь!) а результат передать в обработчик g запрос в БД выполняется, ожидание, очередь событий пуста но есть обработчики, поэтому процесс ещё не закончен в цикл событий пришло уведомление о завершении запроса (из очереди!) вызвал обработчик g для результатов запроса обработчик g на основе результатов запроса составил страницу и отправил пользователю Теперь вопрос: как Node.js выполняет несколько запросов параллельно? Ответ: так только кажется. Просто из-за асинхронности код запроса разбит на отдельные небольшие кусочки, между которыми допускается ожидание. Если запросов мало, то это реально ожидание, а если много, то во время ожидания можно делать что-то полезное. Выше описан типичный запрос простенького веб-приложения, где асинхронных действий всего два: получение запроса от вебсервера и запрос к БД. На практике их обычно больше. В то время, как БД выполняет запрос для первого клиента, может прийти запрос от второго. Если событие о приходе второго запроса придёт раньше результатов из БД для первого, то Node.js: сначала обработает второй запрос (2.II) отправит второй запрос в БД (3.II) получит данные из БД к первому запросу (5.I), сформирует и запишет ответ первому (если запросы в БД более-менее одинаковые и новых запросов нет) получит данные из БД ко второму запросу (5.II), сформирует и запишет ответ второму И выходит, что в каждый момент времени выполняется только одно действие, но при этом один процесс в каждый момент времени может обслуживать несколько клиентов. И допускаются долгие запросы отдельных клиентов. К примеру, не по HTTP а, скажем, Websockets. В CGI и FastCGI, теоретически обслуживание Websocket-соединения занимает целый процесс и не даёт ему заняться ничем другим. N процессов, создаём N соединений и ба-бах -- на запросы отвечать больше некому. Вебсервер из Node.js и другие событийные вебсерверы этим не страдают. Конечно, этого может быть недостаточно. С многоядерными процессорами даже процессы Node.js может иметь смысл поднимать не поодиночке, а сразу наборами. Но какая-то единая точка должна будет раскидывать по ним запросы от клиентов. Балансировщик нагрузки. nginx, HAProxy, Varnish или что-то ещё. Встроенный в язык вебсервер может быть и менее умным: к примеру, есть язык Ruby, в нём принято использовать интерфейс Rack (в котором обработчик запроса -- одна здоровенная функция из кучи частей). Этот интерфейс реализуется вебсервером Unicorn. Получив запрос, он вызывает обработчик-функцию и отдаёт возвращённое ею значение. Разумеется, каждый процесс Unicorn может обслуживать только один запрос. И если он торчит "наружу" (клиенты общаются непосредственно с ним), может возникнуть проблема, если клиент медленный: Unicorn будет терпеливо ждать, пока клиент примет ответ, сколько бы это времени ни заняло, пока клиент кажется живым. N процессов, N медленных клиентов и вуаля -- запросы обслуживать больше некому. Поэтому Unicorn всегда размещают "наружу" за балансировщиком нагрузки, потому что у балансировщика есть собственный буфер в который Unicorn может быстро выкинуть ответ и приступить к следующему запросу, пока балансировщик своим эффективным вводом/выводом (асинхронном событийным) вдалбливает ответы даже в медленных клиентов, освободив от неблагодарной работы Unicorn. ...так, сколько килобайт я уже тут понаписал... Почти 7! Ну... думаю, вы поняли, что это довольно обширная тема.

понедельник, 15 июля 2019 г.

Повторный запрос к серверу

Клиент запрашивает с помощью HTTP страницу от сервера (+ заголовки и прочее) и получает её в ответ, но говорят, что если там есть картинки или другие файлы, то он делает ещё один запрос? Это правда так? Когда он делает повторный запрос на сервер? При наличии в html-коде отданой странички ссылки на файлы, которые размещены на сервере?


Ответ

Это так. Клиент может получать от сервера разные ответы. И если это html, браузеру нужно вывести этот документ на экран, проходя по нему и строя дерево DOM. Если браузер обнаруживает в документе ссылки на ресурсы - он загружает их.
Каждый загруженный ресурс также обрабатывается. И, например, если в css браузер обнаруживает ссылки на другие ресурсы - он начинает загружать и их тоже. А если в них также попадаются ссылки на другие ресурсы - браузер запросит и их тоже.
Вы можете пронаблюдать за этим сами - откройте веб-инспектор в вашем браузере, например в Chrome, на вкладке Network:

Первая строка - запрос html-документа. Последующие - запросы ресурсов из документа, и затем ресурсов из ресурсов. Все по схеме, описанной выше.
В ряде строк можно заметить надпись "Повторный-запрос-к-серверу". Фраза такая же, как название вашего вопроса. Возможно, вы хотели узнать именно об этом? Такая фраза в инспекторе (вместе со статусом 304) значит, что данный ресурс с сервера уже был закачан ранее, и с момента последней закачки не изменился. Значит смысла снова качать его - нет, и можно взять этот ресурс из кеша браузера.
Это называется http-кеширование, и реализуется за счет передачи в http-запросе заголовка ETag. При отдаче ресурса в первый раз, с ним передается некий ключ Etag (например, такой - 6d82cbb050ddc7fa9cbb659014546e59), он формируется на сервере на основе содержимого запрашиваемого файла и даты его изменения. Сема такая:
Во время первого запроса браузер сохраняет файл и ETag в кеше Во время повторного запроса браузер отсылает на сервер значение ETag Если файл за это время не изменился, ETag у него будет такой же, как и в прошлый раз. Сервер сравнивает это значение ETag с пришедшим от браузера, и, если они одинаковые - не отправляет файл браузеру, а отправляет короткий статус 304. По статусу 304 браузер понимает, что файл с прошлого запроса не был изменен, значит можно смело использовать этот файл из кеша.
Подробнее об этом вы можете прочесть в этой статье от google

пятница, 12 июля 2019 г.

Varnish port 80, не может общаться с NGINX port 81

VPS
OS: Debian
Из софта только:
Varnish port 80
NGINX port 81
php-fpm
MariaDB
Drupal 7
Файервола нет.
Установка основывалась на официальном руководстве
Varnish status:
Лог: tail /var/log/varnish/varnishncsa.log
[19/Jun/2018:13:41:52 +0300] "GET http://185.75.90.197:6081/misc/favicon.ico HTTP/1.1" 200 5430 "http://185.75.90.197:6081/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36"
конфиг: /etc/default/varnish
DAEMON_OPTS="-a :80 \ -T localhost:6082 \ -f /etc/varnish/default.vcl \ -S /etc/varnish/secret \ -s malloc,256m"
конфиг: /etc/varnish/default.vcl
backend default { .host = "127.0.0.1"; .port = "81"; }
nginx:
server { listen 81; }
Если Nginx поставить на порт 80, то все нормально работает, может чего не донастроил на своем VPS?


Ответ

Varnish отказывался менять порт с 6081 по умолчанию, на 80 т.к. руководство немного отстает от версии varnish 5.0.0, в котором используется systemd вместо init.d как init system.
Необходимо переопределить сценарий инициализации.
# cp /lib/systemd/system/varnish.service /etc/systemd/system/ # nano /etc/systemd/system/varnish.service
изменить порт
ExecStart=/usr/sbin/varnishd -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m
Перезагружаем:
# systemctl reload varnish.service
возникает ошибка
Failed to reload varnish.service: Job type reload is not applicable for unit varnish.service. See system logs and 'systemctl status varnish.service' for details.
Сначала:
/usr/share/varnish/reload-vcl
Затем:
systemctl daemon-reload
Затем:
systemctl restart varnish
Теперь работает.
curl -I http://localhost:80
HTTP/1.1 200 OK Server: nginx/1.13.3 Date: Wed, 20 Jun 2018 07:42:57 GMT Content-Type: text/html; charset=utf-8 Vary: Accept-Encoding Expires: Sun, 19 Nov 1978 05:00:00 GMT Cache-Control: no-cache, must-revalidate X-Content-Type-Options: nosniff Content-Language: en X-Frame-Options: SAMEORIGIN X-UA-Compatible: IE=edge X-Generator: Drupal 7 (http://drupal.org) Content-Encoding: gzip X-Varnish: 2 Age: 0 Via: 1.1 varnish (Varnish/5.0) Connection: keep-alive
Решение взято отсюда: https://konkretor.com/2017/05/22/varnish-nginx-with-ssl-install-under-ubuntu-16-04/ и https://www.varnish-software.com/wiki/content/tutorials/varnish/varnish_ubuntu.html

Как настроить nginx?

log nginx2012/11/28 17:55:30 [error] 3090#0: *5 directory index of "/var/www/" is forbidden, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", host: "a.localhost1"вывод 403 forbidden/etc/apache2/ports.confNameVirtualHost :8080Listen :8080/etc/apache2/sites-available/aVirtualHost *:8080>В чём ошибка и как исправить?без nginx все работает.


Ответ

*5 directory index of "/var/www/" is forbidden,Ну значит права на папку стоят только для апача, если он тоже стоит, если не стоит, то поставить на папку www права пользователя от которого запущен nginx

понедельник, 8 июля 2019 г.

Nginx | Странное поведение location

Конфигурация сервера:
server { listen 80; server_name example.com;
root /home/Web/www/$server_name/; index index.php index.html;
charset utf-8;
location ~* \.php$ { return 403; }
location / { } }
Индексный файл - index.php. При загрузке example.com/ Nginx отдает именно его (т.к. указано index index.php index.html;), срабатывает location ~* \.php$ и выдает 403 ошибку, все ОК, как и должно.
Но если я внесу такое изменение в блок:
location / { proxy_pass http://backend; }
То при загрузке example.com/ будет сразу проксировать на Backend, хотя index стоит, и обрабатываемый его location тоже.
Я не понимаю, почему если блок location / пустой, то будет обрабатываться подходящий location ~* \.php$, если же поставить проксирование, то запрос начинает обрабатывать location /
Почему так? Не срабатывает настройка index index.php index.html; или что? Как это починить? Спасибо.


Ответ

Вообще ваш пример разобран в документации на nginx
Обработка запроса “/” более сложная. Ему соответствует только префиксный location “/”, поэтому запрос обрабатывается в нём. Затем директива index проверяет существование индексных файлов согласно своих параметров
Кроме этого, весьма рекомендую многократно, до полного просветления перечитывать раздел "порядок применения location" (например раз и два).
Конкретно ваш пример разобранный до самой мелочи.
Как разбирается URI / в вашем конфиге:
А) для случая пустого location /
Для запроса / подходит только один location -- location /. Этот location пустой, но наследует root и index от блока server. Применяется внутреннее перенаправление (директива index) c / на /index.php. Начинается обработка НОВОГО запроса. Для запроса /index.php подходят как location ~* \.php$, так и location \, будет использоваться (см. порядок приоритетов location) location заданный регуляркой, он отдаст 403 и закончит обработку запроса.
Для наглядности эквивалентный конфиг сервера:
server { listen 80; server_name example.com;
root /home/Web/www/$server_name/;
charset utf-8;
location ~* \.php$ { return 403; }
location / { index index.php index.html; } }
Так наглядно видно, что ваш пример полностью соответствует разобранному в документации.
Б) для случая location с прокси-сервером
Для запроса / подходит только один location -- location /. Этот location передаёт обработку бекенду, на этом обработка запроса nginx'ом закончена.
Это что касается запроса, заданного в самом вопросе. В комментариях был ещё один запрос:
Если ввести example.com/index.php, то location ~* .php$ {} срабатывает, в независимости, есть ли что-то в блоке location /{} или нет.
Для запроса /index.php подходит какой location? Правильно -- оба, но первым будет применяться location ~* .php$ (см. приоритет location), а в нём у вас обработка заканчивается выдачей 403.
Как это починить?
Выше я описывал то, как работает ваш конфиг. С вашей точки зрения он работает "неправильно". А вот как правильно -- вы не пишете, поэтому тут нужно, чтобы вы чётко поставили задачу -- что вы, собственно, хотите от nginx? Я полностью согласен с тем комментарием Что «и подобное»? -- это неконкретно и непонятно.
Подозреваю, что вам нужно location = / {...} но не уверен. Возможно, стоит задать новый вопрос, в котором сослаться на текущий и чётко пояснить, какие вы хотите видеть location и что куда хотите отправлять. И лучше не пытайтесь "для примера" заменять одно действие другим: хотите отдать файл -- так и пишите, а не "отдать файл, но вместо этого для примера 403". Это только запутывает и вас как спрашивающего и отвечающих.

воскресенье, 7 июля 2019 г.

PHPStorm - автоматический Upload отредактированных файлов на Remote Host

В PHPStorm 2017 можно произвести настройки, при которых отредактированные файлы будут автоматически сохраняться при потере фокуса с PHPStorm.
Также есть возможность редактировать файлы на Remote Host, при этом над каждым редактируемым файлом есть панель с кнопками "Compare", "Upload", при этом необходимо каждый отредактированный файл отправлять на Remote Host через нажатие кнопки "Upload"
Можно ли автоматически делать "Upload" отредактированных файлов, при потере фокуса с PHPStorm?


Ответ

Да, можно.
Разрешение на автоматическую загрузку файлов
заходишь в верхнем меню - Tools -> Deployment -> Automatic Upload (always)
Разрешить загрузку внешних файлов
Еще, для удобства, зайди в Tools -> Deployment -> Options.. и галочкой отметь Upload external changes , чтобы при добавлении в папку файлов (не через программу PHPStorm), программа сама находила те файлы и перекидывала бы на сервак.
Видео-урок настройки remote host
YouTube

NodeJs Как правильно настроить роутинг

Хочу, чтобы в адресной строке, отображалось название контента, который на данный момент отображается на странице. Прочитал кучу всего, но увы, node js мне пока не подается...
На данный момент у меня Server выглядит так:
var express = require('express'), bodyParser = require('body-parser'), http = require('http'), path = require('path'), fs = require('fs'), id3 = require('id3js'), router = require('routes'), webSocketServer = require('ws').Server, wss = new webSocketServer({ port: 8001 }) getFilesName = require('./myModule/getFilesName'), mongoose = require('mongoose'), async = require('async');
var app = express();
var server = http.createServer(app);
app.use(express.static(path.join(__dirname, '/public')));
app.use(bodyParser.json());
Это мой ajax: файл ajax.js
function ajax(url, callback, data, request) { try { request = new(this.XMLHttpRequest || ActiveXObject)('MSXML2.XMLHTTP.3.0'); request.open(data ? 'POST' : 'GET', url, 1); request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); request.setRequestHeader('Content-type', 'application/json'); request.onreadystatechange = function () { request.readyState > 3 && callback && callback(request.responseText, request); }; request.send(data) } catch (e) { window.console && console.log(e); } };
И допустим есть пара кнопок, меню, и при нажатии на одну из кнопок я в подргужаю что-то на страницу, допустим это slider. Вот так я подгружаю slider на страницу
файл main.js
document.getElementById('menuSlader').addEventListener('click', function() { ajax('../pages/slider/index.html', function(res) { document.getElementById('main').innerHTML = res; })
})
И тут вопрос - как мне изменить состояние адресной строки и получить такой результат http://localhost:8008/slader, если на странице на данный момент отображается slader


Ответ

Главное что нужно помнить меняя URL - это общее правило: любой URL пользователь может скопировать и кому-нибудь передать. Поэтому если вы решили поменять URL - должна быть возможность этот URL открыть минуя главную страницу.
Для того чтобы избежать дублирования, в SPA-приложение вводится отдельный инфраструктурный компонент: роутер, который получает на вход адрес текущей страницы, а на выходе показывает нужные данные.
Тут выделяется два основных подхода: клиентская маршрутизация и изоморфная маршрутизация (работающая вместе с изоморфным рендером).
Клиентская маршрутизация (client side router)
Идея этого способа - все делаем только на клиенте, сервер даже не знает что URL поменялся. Тут все очень просто: маршрут записывается в поле хэша в URL (эта часть URL не передается на сервер). Ссылки на части SPA оформляются вот так, без всякого кода:
...
Если же нужно программное переключение на нужную страницу - это делается вот так:
location.hash = '/foo';
Далее подобные переходы нужно где-то обрабатывать. Для этого существует событие hashchange:
window.addEventListener('hashchange', function (e) { var url = e.newURL; navigate(url.substring(url.indexOf('#')+1)); }); navigate(location.hash); // Не забываем обработать открытие страницы тоже!
Осталось реализовать функцию navigate - она должна в зависимости от переданного ей маршрута загрузить с сервера нужные данные и отобразить их.
Изоморфная маршрутизация
Способ выше индексируется не всеми роботами, а также требует обязательного использования javascript, поэтому от него периодически пытаются отказаться. Вместо этого придумываются способы заставить сервер при необходимости отображать страницу точно так же как вы это делаете на клиенте.
Для этого можно попытаться заставить сервер и клиент исполнять строго один и тот же код - в таком случае то что получилось называется изоморфным рендером. Для изоморфного рендера обычно используют фреймворки которые его поддерживают: React, Vue, Angular.
Ну или можно скостылить что-нибудь вручную чтобы результат получался примерно одинаковым - тогда это будет изоморфная маршрутизация.
На клиенте переходы между страницами должны быть вот такими (иначе им нельзя будет воспользоваться с отключенным JS):

Разумеется, обрабатывать нажатия тут нужно по-особому:
document.addEventListener('click', function (e) { for (var target = e.target; target; target = target.parentElement) { if (target.tagName === 'A') { break; } }
if (target && target.href[0] === '/') { go(target.href); e.preventDefault(); } });
function go(url) { // также эту функцию можно использовать для программной смены текущей страницы navigate(url); history.pushState(null, document.title, url); }
Также можно придумать аналогичный обработчик для события submit у форм - в таком случае формы ввода тоже смогут работать без javascript.
При использовании HistoryAPI нельзя забывать про обработку события popstate:
window.addEventListener("popstate", function (e) { navigate(location.pathname); });
Наконец, серверная часть. На сервере вам нужно выслать измененную страницу со всеми нужными данными. Например, с использованием cheerio:
const cheerio = require('cheerio');
app.get('/foo', function(req, res, next) { fs.readFile(path.join(__dirname, '/public/index.html'), function(err, content) { if (err) { next(err); return; }
var $ = cheerio.load(content); // $('#main').html(...); navigate($, '/foo', function (err) { if (err) { next(err); return; } res.send($.html()); }); }); });
Осталось реализовать функцию navigate два раза - на клиенте и на сервере.

четверг, 30 мая 2019 г.

rails puma постоянный рост используемой памяти

Имеется небольшое приложение на rails(4.2.6), в качестве веб-сервера используется nginx+puma(3.4.0). Сервер Debian 8.1 64bit. Заметил одну странную вещь, с каждым запросом, используемое процессами puma количество памяти растет, и никогда не снижается(во время отсутствия каких либо запросов). Это приводит к тому, что ОЗУ на сервере (512 мб) заканчивается, и останавливается служба postgres(9.4). Пробовал разное количество воркеров, нитей, работа в кластерном режиме и в обычном - результат всегда один и тот же. После нескольких тяжелых запросов ОЗУ заканчивается. Неужели так и должно быть? Какие есть пути устранения такой проблемы? По информации, которую я нашел, Puma - это один из лучших выборов для избежания проблемы медленных клиентов и долгих запросов. Текущий конфиг такой:
#!/usr/bin/env puma
...
threads 2,4
... workers 1
preload_app!
on_restart do puts 'Refreshing Gemfile' ENV["BUNDLE_GEMFILE"] = "/home/.../current/Gemfile" end
on_worker_boot do ActiveSupport.on_load(:active_record) do ActiveRecord::Base.establish_connection end end


Ответ

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

Да, это из-за особенностей работы GC в самом Ruby. Он держит собственный пул памяти, увеличивая его при необходимости, но никогда не уменьшая. И тому есть причина.
Объекты, "подметённые" сборщиком мусора, освобождают память с точки зрения интерпретатора, но не с точки зрения ОС. Посему, только тот факт, что память занята процессом, не означает, что в ней действительно есть что-то ценное для программы, скорее всего, это просто "запас", в котором интерпретатор будет размещать новые объекты сам, не дёргая аллокатор ОС.
Соответственно, в каждый момент времени процесс будет занимать максимум того, что ему было нужно за всё время его жизни. Размер пула будет с небольшим запасом равен пиковому потреблению памяти.

Беда с большими объектами в том, что они требуют большие последовательные области памяти. И если такой блок в пуле не находится, то... пул ещё увеличивается на такую величину, чтобы большой объект влез!
Эту проблему можно было б частично победить, используя сборщик мусора с "уплотнением" (compaction), когда GC в процессе работы "перекладывает" мелкие объекты поближе друг к другу, тем самым образуя более крупные последовательные области свободной памяти. Это же могло бы позволить отдавать крупные "хвосты" незанятой памяти обратно в ОС. Но это здорово усложняет работу С-шных расширений, которые запоминают, где был объект, непосредственно по адресу в памяти. Нельзя просто сказать им "я вон тот объект подвинул, имей в виду".

Сделать можно много чего.
Пол-гигабайта на целое рельсовое приложение и хранилища данных это... крайне немного. Есть смысл добавить, если не физической ОЗУ, то хотя бы подкачки. Подкрутить garbage collector на более осторожное расширение пула и частые срабатывания. Это вряд ли поможет, но может немножко отсрочить неизбежное. Это довольно обширная и опасная тема, требующая тщательного стресс-тестирования на каждое изменение. (Самый богатый на приключения) Реально снизить пиковое потребление памяти, отдавая большие ответы по кусочкам, чтобы GC успевал подчищать то, что уже отослано клиенту. Есть ActionController::Live, с помощью которого можно писать ответ в поток по кусочкам, не загружая все исходные данные для него в память.