среда, 18 сентября 2019 г.

Балансировщик нагрузки nginx не реагирует на ошибку 502 Bad gateway.

Я поддерживаю сайт, сервисы которого располагаются на 4 серверах. Сервер 1 - балансировщик запросов на основе nginx. Сервер 2 и Сервер 3 - ноды с установленными nginx+php-fpm для обработки веб-запросов. Сервер 4 - база данных Mysql.

Однажды была высокая нагрузка на сайт. Сервер 2 и 3 перестали отвечать, т.к. такое количество запросов php-fpm не мог обработать. В конфиге nginx-балансировщика стали появляться сообщения:

upstream server temporarily disabled while reading response header from upstream

В браузере появлялась ошибка 502 Bad gateway. Пришлось оптимизировать конфиг php-fpm. Для этого я поменял process manager опцию со pm=static на pm = ondemand и увеличил количество pm.max_children до максимального в соответствии с ограничениями ОЗУ. Ноды стали корректно обрабатывать запросы. Я попытался проверить работу балансировщика при выключеннии одной ноды. Выключил сервер 3 и сайт стал выдавать 502 Bad gateway. Выходит, что в случае http-ошибки 502 балансировщик не перенаправляет запрос на работающую ноду. Фрагмент конфига nginx.conf на балансировщике:

   upstream blablasite.ru {  
     least_conn;  
     server 192.168.4.18:80;  
     server 192.168.4.80:80;  
   }  
 ...  
   server {  
     listen 443 ssl;  
     server_name blablasite.ru;  
 ...  
     location / {  
       proxy_pass http://blablasite.ru;  
       proxy_redirect off;  
       proxy_max_temp_file_size 0;  
       proxy_set_header Host $host;  
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
       proxy_set_header X-Real-IP $remote_addr;  
     }  

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

В итоге, добавил в конфиг эту строчку с указанием обрабатывать http-ошибки 500, 502, 503, 403. Обновленный конфиг:

   upstream blablasite.ru {  
     least_conn;  
     server 192.168.4.18:80;  
     server 192.168.4.80:80;  
   }  
 ...  
   server {  
     listen 443 ssl;  
     server_name blablasite.ru;  
 ...  
     location / {  
       proxy_pass http://blablasite.ru;  
       proxy_redirect off;  
       proxy_max_temp_file_size 0;  
       proxy_set_header Host $host;  
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
       proxy_set_header X-Real-IP $remote_addr;  
       proxy_next_upstream error timeout http_500 http_502 http_503 http_403;  
     }   

После этого, балансировщик стал корректно обрабатывать ошибки.

понедельник, 28 января 2019 г.

Распознавание русской речи в ОС Linux

Однажды я набирал длинный текст на клавиатуре. Мне хотелось сделать выписки интересных мыслей из одной книги.
Устав писать, я задался вопросом - а существует ли способ набора текста с помощью распознавания русской  речи  в операционной системе Linux?
Погуглив, не нашел  нативных приложений для этого.
Зато нашел совет, как воспользоваться технологиями  Google для распознавания русской речи.


Необходимо зарегистрировать Google Account, если у вас его нет. Затем:

1. Заходите на docs.google.com 
2. Создаем Google документ - аналог документа Microsoft Word
3. Выполняем команду Инструменты - Голосовой ввод...



4. Слева появится соответствующий значок


5. Нажимаем на значок и наслаждаемся переводом речи в текстовое представление.



понедельник, 4 июня 2018 г.

Персональный конструктор sql-команд


В последнее время много пишу на Javascript. Недавно открыл для себя фреймворк vue.js. Поразило, насколько просто он интегрируется с html-кодом страницы. Я имею ввиду вставку и поддержку таких атрибутов для элементов страницы, как : v-if, v-for, v-show, v-cloak и т.д. На Jquery реализация логики по манипуляции с элементами страницы потребовала бы больше кода. Экспериментируя с Vue.js я написал генератор sql-команд.


Генератор нужен, когда лень писать полный текст команд.
Как сгенерировать нужные команды:
  • Написать название вашей таблицы
  • Выбрать в списке необходимые команды
  • Нажать кнопку "Копировать sql"
После этого необходимые команды окажутся в вашем буфере обмена.

понедельник, 11 декабря 2017 г.

Перенос Linux Mint c HDD большего объема на меньший жесткий диск SSD

Как-то раз на работе стал медленно работать жесткий диск с системой Linux Mint.
Админы поставили новый ssd, однако систему на него не перевели, не хватило квалификации.
Пришлось мне, веб-программисту, заниматься самостоятельно задачей перевода системы,  установленной на hdd Linux Mint на ssd.

Сначала я попробовал скопировать систему на новый ssd с помощью программы dd.
Однако выяснилось, что данная программа копирует диск посекторно, то есть фактически клонирует диск.
Ошибка была в том, что исходный hdd имеет объем 460 ГБ, а диск назначения ssd  -  240 Гб.
Данных на hdd на 70 Гб. Тем не менее, поскольку утилита dd пытается скопировать hdd посекторно на ssd, программа завершается с ошибкой.
Погуглив, понял, что нужно создать загрузочный раздел на ssd диске, а в другой раздел на этом же диске скопировать все файлы с hdd.


1. Устаналиваем редактор разделов жестких дисков:

sudo apt-get install gparted

2. Создаем раздел grub_bios объемом 1 МБ для установки загрузчика GRUB в GPT для BIOS.
(Отличная инструкция приведена вот здесь https://losst.ru/ustanovka-zagruzchika-grub )

Внимание! Если у вас в системе используется вместо BIOS UEFI загрузчик, или вы используете в качестве таблицы разделов не GPT, а MBR, то вам данный способ установки загрузчика не подойдет.

Остальную часть диска я перевел в файловую систему ext4 . Эта часть и будет содержать скопированную с hdd систему Linux Mint.


x
































3. Инсталлируем загрузчик grub в специальный раздел на ssd

 sudo grub-install /dev/sda

4. Монтируем новый раздел ssd и копируем все файлы с hdd на этот раздел (за исключением файлов устройств, процессов, временных файлов — новая система прекрасно загрузится и будет работать без них)


mkdir /mnt/sda2

mount /mnt/sda2 /dev/sda2

sudo rsync -aAXv /* /mnt/sda2/ --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/var/tmp/*,/run/*,/mnt/*,/media/*,/lost+found}


5. После перезагрузки системы видим меню, где можно выбрать с какого раздела загружать Linux Mint. Появился новый раздел - /dev/sda2.
Выбираю загрузиться с данного раздела и grub показывает ошибку.


No such device guid …
нет такого раздела
сначала нужно загрузить ядро

Понятно, что несмотря на копирование системы и создание загрузочного раздела, grub не находит файлы системы в новом разделе.

6. Загружаюсь в старую систему на hdd, выполняю команду

sudo update-grub 

Утилита находит систему в разделе /dev/sda2 и обновляет список загрузки


После перезагрузки Linux Mint на новом разделе ssd /dev/sda2 успешно загрузилась, стала работать быстрее и стабильней.

Надеюсь эта последовательность действий сбережет время и нервы читателю. Поскольку Linux Mint и Ubuntu имеют много общего, думаю этот способ подойдет и для Ubuntu.

вторник, 31 октября 2017 г.

Мысленный эксперимент "Китайская комната"


Автор -  американский философ Джон Серл.

Данный мысленный эксперимент был представлен публике в 1980  году.


Картинки по запросу красная китайская комната



Суть: 
Некий человек сидит в комнате в Китае. 
Китайцы передают ему вопросы, написанные на бумаге. Он не знает китайского, но у него есть все инструкции, чтобы написать  ответы на вопросы (какие-то книги, справочники и т.д.). 
По инструкциям он пишет ответы.
Для китайцев нет сомнений, что человек знает китайский однако в действительности он его не знает, а отвечает только благодаря  инструкциям.

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

Такая программа является примером "слабой" версии искусственного интеллекта. Действий своих она не осознает, однако благодаря различным математическим моделям и данным, способна вести диалог с человеком. И человеку кажется, что он разговаривает с разумным собеседником. К таким программам относится, например, Siri.

"Сильная" версия ИИ - это когда система осознает саму себя и возможно имеет собственные цели.  Подобные системы еще не созданы. Если будет создана "сильная" версия искусственного интеллекта, то это будет признано разумным существом. 

пятница, 4 августа 2017 г.

Настройка монитора для меньшей утомляемости глаз

Увидел совет в одном из видеороликов, как настроить монитор, чтобы меньше утомлялись глаза.
Для этого нужно уменьшить синюю составляющую в изображении на мониторе.
Как это сделать?

  1. Для этого нужно в настройках монитора выбрать цветовую температуру (Color Temperature)
  2. Выбрать пользовательский режим настроек (user mode), а не предустановленные, вроде movie или game.
  3. На экране покажутся 3 полоски (красная, зеленая и синяя)
  4. Выделить синюю полоску и уменьшать количество до приемлемого состояния.

Изображение немного пожелтеет. Проверил по собственным ощущениям при работе на домашнем и рабочем ПК - действительно, глаза меньше устают.
Также нашел у себя на  Xiaomi Redmi Pro особый режим "Режим чтения".
Включаю - и изображение на смартфоне тоже слегка пожелтело, что свидетельствует об уменьшении синего цвета.
Читать с экрана смартфона стало удобнее.

понедельник, 29 августа 2016 г.

Обработка уведомлений с подтверждениями в Битрикс



Всем привет!
В последнее время приходится программировать под Битрикс “Корпоративный портал”. Впечатления весьма неоднозначные. Нет в этой системе той стройности и упорядоченности, как например  в фреймворке Laravel,  в коде компонентов редко используется ООП,  часто шаблоны компонентов пестрят жуткой смесью из PHP, JavaScript и html–кода.  Как сказано в одной замечательной критичной к Битрикс статье, "документация по Битрикс отстает от кода системы на 1 – 1.5 года”. Поэтому приходится залезать в дебри системных классов Битрикс и изучать все самостоятельно.
Недавно мне поступило задание реализовать следующий алгоритм на портале:


  1.  Постановщик создает задачу для ответственного.
  2. Ответственный должен открыть карточку задачи  и отредактировать крайний срок для исполнения данной задачи
  3. Постановщику должно придти сообщение с указанием данного крайнего срока и кнопками “Принять” и “Отменить”
  4. Если постановщик принимает крайний срок по задаче, то данное поле блокируется для редактирования и ответственный начинает решать задачу, иначе  - ответственному приходит сообщение о том, чтобы он предложил другой крайний срок





Алгоритм достаточно простой и понятный. Отсылка сообщений должна происходить через веб-мессенджер портала.   Основная сложность здесь – это отсылка постановщику сообщения для подтверждения или отмены и соответственно обработчик нажатия кнопок “Принять” или “Отменить”. Погуглив, я понял, что не все так просто. Вот эта статья от разработчика Битрикс https://dev.1c-bitrix.ru/community/blogs/hazz/im-post-one.php объясняет, как отсылать такое сообщение, требующее подтверждения. Однако в качестве обработчика нажатия кнопок предлагается использовать код своего самописного модуля. Модуль должен быть зарегистрирован следующей функцией.
 
RegisterModuleDependences("im", "OnBeforeConfirmNotify", 
"yourmodule","CYourModuleEvents", "CYourModuleEventsIMCallback");




Как? Неужели для этого я должен писать свой собственный модуль?
Однако выяснилось, что есть альтернатива – функция AddEventHandler, которая регистрирует обработчик события. Отличие данного регистратора события от RegisterModuleDependences в том, что последний сохраняется в базе данных и работает с событиями в модулях.
Итак, вызов функции для отправки сообщения с подтверждением в коде компонента bitrix:task.task.edit :



$mdeadline=ConvertDateTime($arFields["DEADLINE"], "DD.MM.YYYY", "ru"); 
$notify="Задача № {$arParams['TASK_ID']} будет выполнена до {$mdeadline} включительно. Прошу нажать  'Принять', если согласны, и мы приступим к решению данной задачи";
$buttons=Array(
// 1. название кнопки, 2. значение, 3. шаблон кнопки, 4. переход по адресу после нажатия (не обязательный параметр)
Array('TITLE' => 'Принять', 'VALUE' => 'Y', 'TYPE' => 'accept' /*, 'URL' => 'http://test.ru/?confirm=Y' */),
Array('TITLE' => 'Отказаться', 'VALUE' => 'N', 'TYPE' => 'cancel' /*, 'URL' => 'http://test.ru/?confirm=N' */),
);
CMessagesHelper::SendConfirmNotify($arTask['RESPONSIBLE_ID'],$arTask['CREATED_BY'],
'tasks',"tasks|CONFIRM_DEADLINE|{$arParams['TASK_ID']}|{$arTask['RESPONSIBLE_ID']}|{$arTask['CREATED_BY']}|{$task_path}",
$notify,'',$buttons,'');

Функция класса-хелпера для отсылки сообщения с подтверждением:


public static function SendConfirmNotify($from,$to,$module,$tag,$message,$message_email,$buttons,$email_template)
{
if (IsModuleInstalled("im") && CModule::IncludeModule("im"))
{
 $arMessageFields = array(
     // получатель
     "TO_USER_ID" => $to,
     // отправитель
     "FROM_USER_ID" => $from, 
     // тип уведомления
     "NOTIFY_TYPE" => 1,
     // модуль запросивший отправку уведомления
     "NOTIFY_MODULE" => $module,
     // символьный тэг для группировки (будет выведено только одно сообщение), если это не требуется - не задаем параметр
     "NOTIFY_TAG" => $tag,
     // текст уведомления на сайте (доступен html и бб-коды)
     "NOTIFY_MESSAGE" => $message,
     // текст уведомления для отправки на почту (или XMPP), если различий нет - не задаем параметр
     "NOTIFY_MESSAGE_OUT" => $message_email,
     // массив описывающий кнопки уведомления
     "NOTIFY_BUTTONS" => $buttons,
     // символьный код шаблона отправки письма, если не задавать отправляется шаблоном уведомлений
     "NOTIFY_EMAIL_TEMPLATE" => 
     $email_template,
 );
 return CIMNotify::Add($arMessageFields);
}
}

А вот необходимый обработчик нажатия кнопок в уведомлении, который располагается в файле bitrix/php_interface/init.php:

AddEventHandler("im", "OnAfterConfirmNotify", "OnAnswerNotifyHandler");

function OnAnswerNotifyHandler($module,$tags,$value,$arRes,$resultMessages)
{

if (IsModuleInstalled("im") && CModule::IncludeModule("im") && CModule::IncludeModule("tasks"))
{ 
if($module=='tasks' && !empty($tags))
{
 $tag_ar=explode ('|',$tags);

 if($tag_ar && $tag_ar[1]=='CONFIRM_DEADLINE')
 {
 $task_id=$tag_ar[2];
 if(!$task_id) return;
 
 $responsible_id=$tag_ar[3];
 if(!$responsible_id) return;
 
 $author_id=$tag_ar[4];
 if(!$author_id) return;
 
    $task_path="/company/personal/user/{$responsible_id}/tasks/task/view/{$task_id}/";
    
    function ChangeDeadLineCounter($val,$task_id)
    {
        global $USER;
        $loggedInUserId = (int) $USER->getId();
            //отмечаем, что автор ответил на запрос
        $oTask = CTaskItem::getInstanceFromPool($task_id, $loggedInUserId);
        if(!$oTask) 
            return false;
        $arTask = $oTask->getData();


        if(!$arTask) 
            return false;

        //уменьшаем счетчик, чтобы можно было еще раз поменять крайний срок
        if(!empty($arTask['UF_DEADLINE_COUNTER']))
            $arTask['UF_DEADLINE_COUNTER']+=$val;

        if($arTask['UF_DEADLINE_COUNTER']<0 artask="" otask-="">update(array('UF_DEADLINE_COUNTER'=>$arTask['UF_DEADLINE_COUNTER']));
        return true;
    }
         
 if($value=='Y')
 {
 //инициатор подтвердил крайний срок
   $arMessageFields = array(
            "TO_USER_ID" => $responsible_id,
            "FROM_USER_ID" => $author_id,
            "NOTIFY_TYPE" => IM_NOTIFY_SYSTEM,
            "NOTIFY_MODULE" => "tasks",
            "NOTIFY_TAG" => "",
            "NOTIFY_MESSAGE" => "Уважаемый сотрудник! Инициатор задачи № {$task_id} подтвердил установленный Вами крайний срок. Можете приступать к исполнению.",
            "MESSAGE_TYPE" => "S"
        );

    ChangeDeadLineCounter(1,$task_id);
            //echo mydump($arMessageFields);
 
 }
 else
 {

 //инициатор не подтвердил крайний срок - высылаем уведомление ответственному
      $arMessageFields = array(
            "TO_USER_ID" => $responsible_id,
            "FROM_USER_ID" => $author_id,
            "NOTIFY_TYPE" => IM_NOTIFY_SYSTEM,
            "NOTIFY_MODULE" => "tasks",
            "NOTIFY_TAG" => "",
            "NOTIFY_MESSAGE" => "Уважаемый сотрудник! Инициатор задачи № {$task_id} не подтверждает Ваш крайний срок по данной задаче. 
            Редактирование крайнего срока вновь доступно в карточке задачи. Скорректируйте крайний срок еще раз.",
            "MESSAGE_TYPE" => "S"
        );
 
    ChangeDeadLineCounter(-1,$task_id);
 }
    $notifyID = CIMNotify::Add($arMessageFields);

 }
}
}

В обработчике в зависимости от ответа постановщика задачи происходит изменение поля UF_DEADLINE_COUNTER указанной задачи и также отсылается уведомление ответственному по задаче.
Эту статью я решил написать, чтобы другим программистам было проще с реализацией подобных функций. Это необходимо, т.к. из-за скудости документации по Битриксу приходится много времени тратить на поиски решений подобных задач.
Каталог блогов Blogolist