HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Представим, что мы запустили проект, в котором серверная часть написана на Golang. Все идет хорошо: количество пользователей растет и бизнес развивается. Но в какой-то момент мы замечаем, что наше веб-приложение выключается без причины. Пользователи сталкиваются с ошибками, спрашивают о проблеме, но мы не можем определить причину.</p>
1 <p>Представим, что мы запустили проект, в котором серверная часть написана на Golang. Все идет хорошо: количество пользователей растет и бизнес развивается. Но в какой-то момент мы замечаем, что наше веб-приложение выключается без причины. Пользователи сталкиваются с ошибками, спрашивают о проблеме, но мы не можем определить причину.</p>
2 <p>В этом уроке мы рассмотрим самые популярные проблемы Go веб-приложений и способы их решения. Также узнаем, можно ли заранее обезопасить себя от неожиданных сбоев.</p>
2 <p>В этом уроке мы рассмотрим самые популярные проблемы Go веб-приложений и способы их решения. Также узнаем, можно ли заранее обезопасить себя от неожиданных сбоев.</p>
3 <h2>Пишем устойчивое веб-приложение</h2>
3 <h2>Пишем устойчивое веб-приложение</h2>
4 <p>Допустим, у нас есть веб-приложение, которое позволяет рассчитать время между двумя датами. Код приложения выглядит следующим образом:</p>
4 <p>Допустим, у нас есть веб-приложение, которое позволяет рассчитать время между двумя датами. Код приложения выглядит следующим образом:</p>
5 <p>Все работает отлично, пока в один день мы не внесли изменение в код приложения, которое отправляет HTTP-запросы. Из-за небольшой ошибки в коде, приложение стало отправлять некорректный JSON-формат в теле HTTP-запроса.</p>
5 <p>Все работает отлично, пока в один день мы не внесли изменение в код приложения, которое отправляет HTTP-запросы. Из-за небольшой ошибки в коде, приложение стало отправлять некорректный JSON-формат в теле HTTP-запроса.</p>
6 <p>Воспроизведем такую ситуацию. Запускаем веб-приложение и отправляем HTTP-запрос с некорректным JSON-форматом в теле:</p>
6 <p>Воспроизведем такую ситуацию. Запускаем веб-приложение и отправляем HTTP-запрос с некорректным JSON-форматом в теле:</p>
7 <p>Получаем сообщение, что ответа не было:</p>
7 <p>Получаем сообщение, что ответа не было:</p>
8 <p>Ошибка в формате тела HTTP-запроса привела к тому, что наше веб-приложение перестало работать. В логах веб-приложения мы видим сообщение о панике:</p>
8 <p>Ошибка в формате тела HTTP-запроса привела к тому, что наше веб-приложение перестало работать. В логах веб-приложения мы видим сообщение о панике:</p>
9 <h2>Восстановление после паники</h2>
9 <h2>Восстановление после паники</h2>
10 <p><strong>Паника</strong>- это критическая ошибка, которая останавливает работу всего Go-приложения. Она возникает в запущенном приложении, поэтому ее нельзя обнаружить во время компиляции.</p>
10 <p><strong>Паника</strong>- это критическая ошибка, которая останавливает работу всего Go-приложения. Она возникает в запущенном приложении, поэтому ее нельзя обнаружить во время компиляции.</p>
11 <p>Из-за того, что паника возникает в работающем приложении и приводит к полной остановке, следует максимально подготовить наше приложение к таким ситуациям. Для этого в Go есть механизм<em>восстановление от паники</em>.</p>
11 <p>Из-за того, что паника возникает в работающем приложении и приводит к полной остановке, следует максимально подготовить наше приложение к таким ситуациям. Для этого в Go есть механизм<em>восстановление от паники</em>.</p>
12 <p>В веб-приложениях принято устанавливать посредника для восстановления после паник. Добавим такого посредника в наше веб-приложение:</p>
12 <p>В веб-приложениях принято устанавливать посредника для восстановления после паник. Добавим такого посредника в наше веб-приложение:</p>
13 <p>Запускаем веб-приложение и пытаемся снова воспроизвести ситуацию с некорректным JSON-телом в HTTP-запросе. На этот раз получаем следующий ответ:</p>
13 <p>Запускаем веб-приложение и пытаемся снова воспроизвести ситуацию с некорректным JSON-телом в HTTP-запросе. На этот раз получаем следующий ответ:</p>
14 <p>Мы получили сообщение об ошибке, но веб-приложение продолжило работу. Это произошло благодаря посреднику recover.New(), который восстановил веб-приложение после паники. Если мы попробуем отправить еще десять таких запросов, то веб-приложение все равно продолжит работать.</p>
14 <p>Мы получили сообщение об ошибке, но веб-приложение продолжило работу. Это произошло благодаря посреднику recover.New(), который восстановил веб-приложение после паники. Если мы попробуем отправить еще десять таких запросов, то веб-приложение все равно продолжит работать.</p>
15 <p>Наше веб-приложение стало устойчиво к паникам в коде, однако пользователи до сих пор получают ошибки с непонятным текстом. При этом мы ничего не видим в своих логах. Исправим обработку ошибок в приложении.</p>
15 <p>Наше веб-приложение стало устойчиво к паникам в коде, однако пользователи до сих пор получают ошибки с непонятным текстом. При этом мы ничего не видим в своих логах. Исправим обработку ошибок в приложении.</p>
16 <h2>Обработка ошибок</h2>
16 <h2>Обработка ошибок</h2>
17 <p>В любом работающем веб-приложении всегда могут возникнуть ошибки. Это обусловлено тем, что веб-приложения - это сложные системы, в которых невозможно учесть все возможные сценарии. Поэтому Go-разработчикам важно предусмотреть возникновение как можно большего количества ошибок.</p>
17 <p>В любом работающем веб-приложении всегда могут возникнуть ошибки. Это обусловлено тем, что веб-приложения - это сложные системы, в которых невозможно учесть все возможные сценарии. Поэтому Go-разработчикам важно предусмотреть возникновение как можно большего количества ошибок.</p>
18 <p>В Go ошибки представлены явным типом error, и золотое правило Go-разработчика звучит как:<strong>Обрабатываем абсолютно все ошибки в коде приложения</strong>. Даже если кажется, что ошибка в какой-то части кода никогда не возникнет, мы все равно должны ее обработать. Код приложения будет часто меняться, и то, что невозможно сегодня, может произойти завтра.</p>
18 <p>В Go ошибки представлены явным типом error, и золотое правило Go-разработчика звучит как:<strong>Обрабатываем абсолютно все ошибки в коде приложения</strong>. Даже если кажется, что ошибка в какой-то части кода никогда не возникнет, мы все равно должны ее обработать. Код приложения будет часто меняться, и то, что невозможно сегодня, может произойти завтра.</p>
19 <p>Добавим обработку всех ошибок в нашем веб-приложении:</p>
19 <p>Добавим обработку всех ошибок в нашем веб-приложении:</p>
20 <p>Запускаем веб-приложение и отправляем HTTP-запрос с невалидным JSON в теле:</p>
20 <p>Запускаем веб-приложение и отправляем HTTP-запрос с невалидным JSON в теле:</p>
21 <p>В ответ получаем сообщение об ошибке:</p>
21 <p>В ответ получаем сообщение об ошибке:</p>
22 <p>Теперь отправителю HTTP-запроса понятно, в чем заключается ошибка.</p>
22 <p>Теперь отправителю HTTP-запроса понятно, в чем заключается ошибка.</p>
23 <p>Попробуем отправить корректный JSON в теле HTTP-запроса, но передать невалидные даты:</p>
23 <p>Попробуем отправить корректный JSON в теле HTTP-запроса, но передать невалидные даты:</p>
24 <p>Получаем в ответ сообщение:</p>
24 <p>Получаем в ответ сообщение:</p>
25 <p>Отправитель HTTP-запроса поймет, что неправильно передал значение в поле<em>from</em>.</p>
25 <p>Отправитель HTTP-запроса поймет, что неправильно передал значение в поле<em>from</em>.</p>
26 <p>Проверим, что наше веб-приложение работает при корректном запросе:</p>
26 <p>Проверим, что наше веб-приложение работает при корректном запросе:</p>
27 <p>В ответ приходит:</p>
27 <p>В ответ приходит:</p>
28 <p>Мы сделали веб-приложение более устойчивым к ошибкам в коде и неправильным запросам. В случае паники, веб-приложение будет само восстанавливаться и продолжать работу. При неправильном HTTP-запросе отправитель получит понятное сообщение об ошибке, а мы увидим в логах подробную информацию о том, что произошло.</p>
28 <p>Мы сделали веб-приложение более устойчивым к ошибкам в коде и неправильным запросам. В случае паники, веб-приложение будет само восстанавливаться и продолжать работу. При неправильном HTTP-запросе отправитель получит понятное сообщение об ошибке, а мы увидим в логах подробную информацию о том, что произошло.</p>
29 <p>Мы уже сделали много работы, но осталась еще одна потенциальная проблема, которую стоит учитывать при разработке веб-приложений. У нас отсутствуют таймауты при обработке HTTP-запросов.</p>
29 <p>Мы уже сделали много работы, но осталась еще одна потенциальная проблема, которую стоит учитывать при разработке веб-приложений. У нас отсутствуют таймауты при обработке HTTP-запросов.</p>
30 <h2>Таймаут при обработке HTTP-запроса</h2>
30 <h2>Таймаут при обработке HTTP-запроса</h2>
31 <p>Представим, что наш конкурент решил воспользоваться противозаконным методом и нанял команду хакеров, чтобы они остановили наше веб-приложение. Допустим, они узнали, что у нас нет таймаутов на обработку HTTP-запросов. Тогда они написали программу, которая отправляла в веб-приложение HTTP-запрос, но делало это очень медленно - по одному байту в секунду.</p>
31 <p>Представим, что наш конкурент решил воспользоваться противозаконным методом и нанял команду хакеров, чтобы они остановили наше веб-приложение. Допустим, они узнали, что у нас нет таймаутов на обработку HTTP-запросов. Тогда они написали программу, которая отправляла в веб-приложение HTTP-запрос, но делало это очень медленно - по одному байту в секунду.</p>
32 <p>Получилась ситуация, когда количество соединений к нашему веб-приложению бесконечно растет, а обработчики не могут сработать, потому что тело HTTP-запроса получено не полностью. Спустя какое-то время, наше приложение перестает отвечать на HTTP-запросы, и весь сервер может выйти из строя.</p>
32 <p>Получилась ситуация, когда количество соединений к нашему веб-приложению бесконечно растет, а обработчики не могут сработать, потому что тело HTTP-запроса получено не полностью. Спустя какое-то время, наше приложение перестает отвечать на HTTP-запросы, и весь сервер может выйти из строя.</p>
33 <p>Чтобы избежать такой ситуации, нам стоит настроить таймауты при обработке HTTP-запросов. Эта настройка укажет веб-приложению закрывать соединения спустя заданное время, если оно не получило полностью тело HTTP-запроса.</p>
33 <p>Чтобы избежать такой ситуации, нам стоит настроить таймауты при обработке HTTP-запросов. Эта настройка укажет веб-приложению закрывать соединения спустя заданное время, если оно не получило полностью тело HTTP-запроса.</p>
34 <p>Добавим настройку таймаутов в наш код:</p>
34 <p>Добавим настройку таймаутов в наш код:</p>
35 <p>Таймауты настраиваются через передачу параметров в конструктор fiber.New(). Мы указали таймаут на чтение HTTP-запросов и запись HTTP-ответов в три секунды. Если спустя заданное время не произойдет ни одного действия, то соединение будет закрыто.</p>
35 <p>Таймауты настраиваются через передачу параметров в конструктор fiber.New(). Мы указали таймаут на чтение HTTP-запросов и запись HTTP-ответов в три секунды. Если спустя заданное время не произойдет ни одного действия, то соединение будет закрыто.</p>
36 <p>С помощью настройки таймаутов на обработку HTTP-запросов мы обезопасили веб-приложение от еще одной уязвимости, которая может вывести веб-приложение из строя.</p>
36 <p>С помощью настройки таймаутов на обработку HTTP-запросов мы обезопасили веб-приложение от еще одной уязвимости, которая может вывести веб-приложение из строя.</p>
37 <h2>Выводы</h2>
37 <h2>Выводы</h2>
38 <ul><li>В Go веб-приложениях может возникать состояние критической ошибки - паника, и важно всегда иметь посредника, который будет восстанавливать веб-приложение</li>
38 <ul><li>В Go веб-приложениях может возникать состояние критической ошибки - паника, и важно всегда иметь посредника, который будет восстанавливать веб-приложение</li>
39 <li>Все ошибки, которые возвращаются из функций, должны быть обработаны. Так разработчики и пользователи будут понимать, что пошло не так</li>
39 <li>Все ошибки, которые возвращаются из функций, должны быть обработаны. Так разработчики и пользователи будут понимать, что пошло не так</li>
40 <li>В Go веб-приложениях нужно настроить таймауты на обработку HTTP-запросов. Это поможет избежать уязвимости, которая может вывести веб-приложение из строя</li>
40 <li>В Go веб-приложениях нужно настроить таймауты на обработку HTTP-запросов. Это поможет избежать уязвимости, которая может вывести веб-приложение из строя</li>
41 </ul>
41 </ul>