Портрет 4X_Pro
Был в Сети 27 авг. 2025 г., 05:32
Мультиблог
4X_Pro
Кратко о себе: Web-разработчик. Пишу на PHP, Python, JavaScript. Знаю Ruby и Go, со студенческих времён более-менее помню C и asm. Специализируюсь на ускорении загрузки сайтов и разработке ботов для Telegram. Linuxоид (использую Debian+LXDE). Сторонник IndieWeb, slow lifer.

Социальные сети


Новости сайта в Telegram

t.me/4x_pro

Лог жизни

Лог моей жизни, где я фиксирую наиболее эмоционально значимые для меня события и текущее настроение. Является продолжением блога, который я вел в ЖЖ с ноября 2004 по апрель 2018 года.


Переписал SiteKnockerBot на Go

4X_Pro

Вчера вечером выполнил еще один пункт из своего списка дел на период загородной жизни: дописал новую версию SiteKnockerBot на языке Go, что позволило выполнять проверки сайтов параллельно. Но закончил весьма поздно, поэтому тестирование на боевом сервере решил отложить на сегодня. Причем, думал, что управлюсь за час-полтора, а остаток дня потрачу на отдых и компьютерные игры. Но увы, я был бы не я, если бы не растянул намеченное дело на всё доступное время. Началось всё с того, что при первом запуске бот просто упал. Причина нашлась довольно быстро: в одной ситуации я поставил defer resp.Close до проверки на то, что нет ошибок. Перекомпилировал, загрузил на сервер, запустил снова. Вроде работает, но то и дело появляются ошибки — невозможно открыть сокет из-за нехватки количества файлов. Пришлось срочно разбираться, как увеличить количество открытых файлов, так как прежде я с этим лимитом никогда не сталкивался. Ещё один запуск: вроде работает, но появляется подозрительно много таймаутов. Уменьшил число goroutines, вроде стало лучше, но опять таймауты есть. Какое-то время пытался играться с числом goroutines и интервалом, через который они порождаются, но увы, таймауты никуда не девались. Пробовал смотреть эти сайты через curl на локальном компьютере — вроде всё нормально, выдаются 301 или 302 редиректы. Думал, что в Go они как-то некорректно обрабатываются, но нет. Разгадка пришла тогда, когда попытался открыть соответствующие адреса в броузере. Оказывается, в таких случаях происходят редиректы на другой адрес, который не отвечает.
И вообще, в базе обнаружилось на удивление много «битых» и подозрительных адресов. Такое ощущение, что чуть ли не четверть пользователей использовало бота для каких-то сайтов-однодневок и подозрительных целей…
После того, как разобрался с таймаутами, снова стал подбирать число goroutines и интервалы. Изначально план был таким: 500 параллельных запросов каждые 150 миллисекунд, что давало 1 миллион проверок за пятиминутный цикл. Но при этом время ответа многих сайтов оказывалось гораздо больше, чем на самом деле. Например, даже 2seconds, где вообще лежит статичная страница, открывался чуть больше 1 секунды. Я стал пытаться уменьшить интервалы до 20—30 мс, уменьшив также и количество параллельных запросов, но увы, либо это не помогало, либо приходилось ставить такие значения, что число проверок за цикл получалось бы равным 200—300 тыс, а не миллиону, как я задумывал изначально. В итоге уже почти смирился с тем, что придётся тремястами тысячами и ограничиться. Но потом решил пойти на крайние меры: интервал между запросами снизил всего до 6 мс, а их число — до 10. Сделав это, я обнаружил, что время снизилось до почти приемлемого: тот же 2seconds загружается за 400 мс, а в среднем время ответа менее на 100 мс больше, чем на старой версии бота. Но увы, даже так получается всего полмиллиона запросов за цикл.
На этом я и решил остановиться. Потом еще какое-то время провозился с настройкой systemd. Почему-то из командной строки новый бот запускался нормально, а через него — нет. Вскоре выяснилось, что systemd запускает старую версию бота, не видя изменений в unit-файле. Так до конца и не понял, почему, но в итоге добился нормальной работы. Потом стал искать, нельзя ли как-то оптимизировать работу HTTP-клиента в Go, наткнулся на проект fasthttp, где обещают гораздо более высокую скорость запросов. Но увы, переписывать бота ещё раз уже нет ресурса — выгорел полностью.
Но в любом случае, я впервые написал что-то достаточно серьёзное и законченное на относительно новом для себя языке — Go. До этого я использовал его только для решения задач на CodinGame, и то очень мало. Впечатления остались очень противоречивые. Понравилась лёгкость распараллеливания, легкость компиляции под другую архитектуру (я сначала собирал бота под x86, забыв, что у меня и на втором VDS стоит 64-битная OS) и то, что он делает полностью статичные бинарники бе всяких so. Правда, у этого есть и обратная сторона: большая прожорливость памяти. PHP-шная версия бота занимала вроде около 30 Мб, а новая — 65, а когда параллельных запросов было больше, то и все 110. Но что реально не нравится, так это использование всяких Readers/Writers для работы с HTTP-ответом (и не только с ним, они там на каждом шагу используются). Ну почему его нельзя получить просто в строку/массив байтов, как в PHP или JavaScript? Зачем эти лишние (и труднодоступные для понимания) сущности? (И тут я подумал, что надо было вообще использовать node.js, а не Go.)
А еще завершение бота — это ещё один выполненный пункт моего списка дел. Причем восьмой из шестнадцати — ровно половина сделана. Хех, как всегда, живу в 3—4 раза медленнее: планировал сделать 16 пунктов за месяц, сделал половину почти за два. Но, пожалуй, теперь стоит устроить несколько дней отдыха, чтобы прийти в себя после всего этого!

О FUSE для WAD-файлов

4X_Pro

Вчера вдруг вспомнил о своей давней идее освоить FUSE (библиотеку для работы с файловыми системами из userspace). Когда-то я разбирал основы и тогда задумался над тем, чтобы написать модуль для монтирования WAD-файлов, которые использовались в старом добром Doom и ему подобных играх, так как у них довольно простая внутренняя структура.
И вот вчера внезапно произошло то самое складывание в голове всего в единую цельную картину. И это сразу же дало мотивацию сесть и начать писать. Сначала я сделал простую утилиту для извлечения отдельного элемента из WAD-файла для тестирования. Потом пришлось прерваться — мы с Tellой договорились прогуляться по городу. Прогулка прошла хорошо, если не считать, что чуть было не попали под дождь. Но своевременно это поняли и добрались до моста Богдана Хмельницкого буквально в момент начала дождя, где и спрятались. А перед дождем удалось сделать несколько весьма атмосферных фото.
Вернулся я достаточно поздно и занялся переделкой написанной утилиты для работы с FUSE. И тут меня ждала первая неприятность: я долго не мог понять, как правильно передавать имя файла, который требуется смонтировать. Потом выяснилось, что у FUSE есть собственные инструменты для разбора параметров командной строки, и нужно использовать их. Но с этим я разобрался только сегодня, и то далеко не сразу. И вот наконец, первый запуск программы, не приведший к ошибке. Захожу в подкаталог монтирования и с радостью вижу там имена элементов из WAD-файла! Правда, сначала они выводились криво из-за того, что я забыл добавить нулевой байт. Но это я исправил быстро. А вот с другой ошибкой — тем, что список файлов получить удалось, а прочитать какой-либо из них — нет, провозился долго. А ошибка там оказалась довольно примитивная. Я давно не писал на C и благополучно забыл, что функция fread возвращает не число прочитанных байт, а число прочитанных блоков указанного размера. Причем сначала я думал, что проблемы в поиске нужного элемента в WAD или определении его размера. Но сделал отладочный вывод — там было все нормально. И только когда вывел вообще все что можно, и обнаружил, что fread возвращает единицу, понял, откуда берется проблема.
После этого все заработало. Но не обошлось без last-minute bug. Решил сделать по-нормальному: вынести некоторые процедуры из h-файла в c-файл, а в заголовочном оставить только их объявления. Но ошибочно в спешке одну из процедур принял за структуру (ввело в заблуждение слово struct в начале), и оставил ее в h-файле. А потом долго не мог понять, почему линковщик ругается на дважды определенный символ, который к тому же вообще экспортироваться не должен. Потом еще какое-то время ушло на то, чтобы выгрузить получившееся на GitHub и написать краткое описание.
Но все это было не зря: снова удалось почувствовать себя Настоящим Компьютерщиком, который может творить то, что не каждому доступно!
А еще обнаружил, что кто-то поставил звездочку (GitHubовский аналог like) моей другой разработке — JavaScript-библиотеке CondiLoader для асинхронной загрузки JavaScript и CSS. Что ж, хоть кто-то ее заметил!


Задать вопрос

Здесь можно задать мне вопрос или спросить совета по любой теме, затронутой в блогах или на форуме. После того, как я отвечу, вопрос и ответ появятся в соответствующем разделе. Но не забываем, что я — сторонник slow life, поэтому каких-либо сроков ответов не обещаю. Самые интересные вопросы станут основой для новых тем на форуме или записей в блоге.
Сразу предупреждаю: глупости, провокации, троллинг и тому подобное летит прямо в /dev/null.