Мой первый опыт знакомства с Go только по документации и статьям получился довольно неудачным, язык казался страшным и неудобным по большинству параметров. Но раз так много и упорно о нём в последнее время говорят, значит что-то в Go есть?! В общем я решил познакомиться с ним поближе. И по моему глубокому убеждению, проекты типа "hello world" для этого очень плохо подходят. Нужна большая интересная задача, в режиме "хотя бы пара месяцев по вечерам после работы". В общем я начал писать свой маленьких поисковик.
Задача из категории невыполнимых. В том плане, что на "взрослые" поисковые системы потратили сотни человеко-лет работы очень и очень умных людей. Это значит, что соревноваться с ними не возможно в принципе и никакого тебе уязвлённого самолюбия, что не смог сделать законченный проект. А следовательно можно не заморачиваться, а просто выбирать наиболее интересные задачи и для фана их реализовывать. Как только надоест - забрасываем проект безо всяких угрызений совести. В общем идеальная задача для изучения нового языка, ну и технологий поиска заодно.
Если сразу ринуться в бой и начать тестировать возможности Go по парсингу HTML онлайн и выдиранию из него ссылок, да ещё и параллельно проверять как эффективно работают горутины - для ускорения процесса. То окажется, что сайты отнюдь не горят желанием поддерживать тебя в этом благородном начинании. Многие считают, что десяток-другой запросов в секунду с одного IP это DDoS атака и перестают отдавать мне контент. Некоторые, например habrahabr, поступали особо жестоко - он начинал отдавать мне маленькую страницу с ошибкой 503 (Service Unavailable). И пока я не добавил анализ кода ответа - очень радовался, что удалось написать код, который так быстро разбирает страницы.
В общем довольно скоро назрела необходимость в написании выделенного, относительно умного краулера с возможностью сохранять всё закаченное в БД. Одним из самых главных требований, которые я предъявлял к базе - она должна быть встроенной. Очень не хотелось, что бы для запуска моего приложения требовалось сначала поставить и настроить ещё десяток сторонних. Для Go таких баз уже написали довольно много, но почти все самые популярные - это NoSQL.
Чаще всего встречалось упоминание BoltDB. Поэтому с неё я и начал. Довольно аскетичная база - классическая key-value, оба значения бинарные, что снимает любые ограничения на хранимые данные. По ключу естественно автоматически строится индекс. В качестве плюса можно отметить реализованные транзакций и даже целых 2 типа: на "чтение" и на "чтение-запись". Скорость, традиционно для NoSQL - на высоте, по крайней мере если соблюдать несколько несложных правил - можно добиться отличных результатов.
Как вы будете переводить свои данные в бинарный формат для сохранения в базу, авторов совершенно не волнует. Возможно это и правильно, с той точки зрения, что подобного рода базы не должна навязывать какие-то ограничения в этом плане. Тем более, что для Go сериализаторов довольно много. Вот тут можно найти большую сводную таблицу с производительностью самых популярных из них. В итоге по соотношению скорость\удобство работы\кроссплатформенность был выбран MessagePack. Скорость последнего обеспечивается за счёт того, что для каждой конкретной структуры, при помощи "go generate", генерируется специфический код для сериализации\десериализации. Это в лучшую сторону отличает библиотеку от нативного пакета binary, который работает через рефлексию.
На первый взгляд всё довольно неплохо. Но после примерно 2 недель реального использования, оказалось, что плюсы у такой связки отсутствуют. Скорость работы - нивелируется тем, что между запросами к сайту нужно делать минимум 1 секунду задержки, что бы они не банили, а парсить больше 10 сайтов одновременно я особого смысла сейчас не вижу. В итоге для сохранения 10 страниц в секунду особой производительности от базы не требуется. Второй типичный для nosql решений плюс - возможность в одной таблице хранить разнородные данные, я так и не смог реализовать. В таблицы данные писались в сугубо однородном виде, страницы, что характерно, разные сайты отдавали в стандартизированном виде.
А вот минусов оказалось много:
В итоге выбор с БД у меня сократился до SQLite. С кучей сторонних приложений для работы с базой. И как бонус с возможностью относительно легко переделать код для работы с другими реляционными БД. А с задачей абстрагирования от БД мне успешно помог ORM Gorm. В последнем, кстати, я смог даже решить такую интересную задачу как - абсолютно прозрачное сжатие через zlib контента страницы при сохранении в БД, и разархивация при обращении к этому полю. Вообще, очень интересный пакет, сильно рекомендую посмотреть на него, упрощает работу с БД в разы.
На сегодня пожалуй хватит и так довольно много получилось.
Задача из категории невыполнимых. В том плане, что на "взрослые" поисковые системы потратили сотни человеко-лет работы очень и очень умных людей. Это значит, что соревноваться с ними не возможно в принципе и никакого тебе уязвлённого самолюбия, что не смог сделать законченный проект. А следовательно можно не заморачиваться, а просто выбирать наиболее интересные задачи и для фана их реализовывать. Как только надоест - забрасываем проект безо всяких угрызений совести. В общем идеальная задача для изучения нового языка, ну и технологий поиска заодно.
Если сразу ринуться в бой и начать тестировать возможности Go по парсингу HTML онлайн и выдиранию из него ссылок, да ещё и параллельно проверять как эффективно работают горутины - для ускорения процесса. То окажется, что сайты отнюдь не горят желанием поддерживать тебя в этом благородном начинании. Многие считают, что десяток-другой запросов в секунду с одного IP это DDoS атака и перестают отдавать мне контент. Некоторые, например habrahabr, поступали особо жестоко - он начинал отдавать мне маленькую страницу с ошибкой 503 (Service Unavailable). И пока я не добавил анализ кода ответа - очень радовался, что удалось написать код, который так быстро разбирает страницы.
В общем довольно скоро назрела необходимость в написании выделенного, относительно умного краулера с возможностью сохранять всё закаченное в БД. Одним из самых главных требований, которые я предъявлял к базе - она должна быть встроенной. Очень не хотелось, что бы для запуска моего приложения требовалось сначала поставить и настроить ещё десяток сторонних. Для Go таких баз уже написали довольно много, но почти все самые популярные - это NoSQL.
Чаще всего встречалось упоминание BoltDB. Поэтому с неё я и начал. Довольно аскетичная база - классическая key-value, оба значения бинарные, что снимает любые ограничения на хранимые данные. По ключу естественно автоматически строится индекс. В качестве плюса можно отметить реализованные транзакций и даже целых 2 типа: на "чтение" и на "чтение-запись". Скорость, традиционно для NoSQL - на высоте, по крайней мере если соблюдать несколько несложных правил - можно добиться отличных результатов.
Как вы будете переводить свои данные в бинарный формат для сохранения в базу, авторов совершенно не волнует. Возможно это и правильно, с той точки зрения, что подобного рода базы не должна навязывать какие-то ограничения в этом плане. Тем более, что для Go сериализаторов довольно много. Вот тут можно найти большую сводную таблицу с производительностью самых популярных из них. В итоге по соотношению скорость\удобство работы\кроссплатформенность был выбран MessagePack. Скорость последнего обеспечивается за счёт того, что для каждой конкретной структуры, при помощи "go generate", генерируется специфический код для сериализации\десериализации. Это в лучшую сторону отличает библиотеку от нативного пакета binary, который работает через рефлексию.
На первый взгляд всё довольно неплохо. Но после примерно 2 недель реального использования, оказалось, что плюсы у такой связки отсутствуют. Скорость работы - нивелируется тем, что между запросами к сайту нужно делать минимум 1 секунду задержки, что бы они не банили, а парсить больше 10 сайтов одновременно я особого смысла сейчас не вижу. В итоге для сохранения 10 страниц в секунду особой производительности от базы не требуется. Второй типичный для nosql решений плюс - возможность в одной таблице хранить разнородные данные, я так и не смог реализовать. В таблицы данные писались в сугубо однородном виде, страницы, что характерно, разные сайты отдавали в стандартизированном виде.
А вот минусов оказалось много:
- Отсутствие инструментов для просмотра содержимого базы - приводило к тому, что просто для того, что бы посмотреть, что реально записалось в базу приходилось писать код, пусть по 10-20 строк, но всё же приходилось.
- Обновление структуры базы, например добавление нового столбца, опять же приводит к тому, что я пишу код, порой довольно много кода.
- Без индексов по полям сохраняемого документа, как оказалось, жить просто не возможно. Можно конечно написать свои собственные, на основе того, что предоставляет база, но тогда пришлось бы в каждом месте, где я сохраняю данные в базу, сразу писать код для обновления индексов. Но писать фактически свою БД в мои планы не входило.
- Отсутствие макросов, шаблонов и нормального наследования в Go, привело к тому, что мне не удалось спрятать всю эту сериализацию\десерриализацию, обновление индексов, прочие особенности работы с BoltDB в отдельный пакет. В вызывающем коде приходится постоянно учитывать подобную специфику. Вероятно, если сильнее углубиться в возможности рефлексии Go, то эта проблема решается, но опять же это получается я буду писать свой фреймворк для работы с базой, а не поиск.
В итоге выбор с БД у меня сократился до SQLite. С кучей сторонних приложений для работы с базой. И как бонус с возможностью относительно легко переделать код для работы с другими реляционными БД. А с задачей абстрагирования от БД мне успешно помог ORM Gorm. В последнем, кстати, я смог даже решить такую интересную задачу как - абсолютно прозрачное сжатие через zlib контента страницы при сохранении в БД, и разархивация при обращении к этому полю. Вообще, очень интересный пакет, сильно рекомендую посмотреть на него, упрощает работу с БД в разы.
На сегодня пожалуй хватит и так довольно много получилось.
Комментариев нет:
Отправить комментарий