Эта неделя знаменует собой запуск моего продолжительного проекта выходных дней: scalabitz.com . Он пытается раскрыть интересный контент Scala через API-интерфейс сокращения ссылок Bit.ly. Сайт был построен с использованием Scala и Play (и немного Akka) со слоем хранения MongoDB и может быть найден на GitHub .
Прежде чем читать, возможно, вы захотите взглянуть на scalabitz.com, чтобы понять, о чем остальная часть этого поста. Вы также можете следить за Скалабитцем в Твиттере .
Поиск контента
Одна из самых сложных проблем в Интернете — как найти соответствующий контент. Агрегаторы ссылок, такие как Reddit и Hacker News, решают часть этой проблемы с помощью краудсорсинговых ссылок. Твиттер и Фейсбук могут быть более адаптированы, но работают так же хорошо, как люди, на которых вы подписаны. Стартапы, такие как Prismatic, создают более персонализированное решение с использованием машинного обучения, и существует множество решений, сочетающих разные подходы. Короче говоря, это не решенная проблема. Некоторое время назад я наткнулся на API Bit.ly и подумал, может ли он предоставить альтернативу. Можем ли мы найти интересный контент по данным службы сокращения ссылок? Отсюда и начало моего эксперимента. Пока что я считаю, что ответ осторожен: да, поскольку люди стремятся сократить ссылки, которые им небезразличны.
Но актуально ли это?
Bit.ly API предлагает конечную точку поиска . Если вы начнете использовать его в качестве ключевого слова «Scala», неожиданно появится много интересных ссылок, связанных с Scala. Но это также дает много ложных срабатываний. Например, оказывается, что есть много клубов и ресторанов, называемых Scala, которые объявляют о событиях через ссылки Bit.ly. Вы можете использовать «Программирование Scala» в качестве поисковой фразы, но тогда вы пропустите много контента.
Для scalabitz.com я использовал просто «Scala» в качестве поисковой фразы. Это означает, что я должен что-то делать с ложными срабатываниями. В настоящее время результаты на главной странице проверяются вручную. Существует небольшая эвристика соответствия ключевых слов для контента, которая помогает мне быстро увидеть, является ли контент релевантным:
(частичный скриншот страницы администратора, зеленый — это хорошо, красный — плохо…)
К счастью, объем новых ссылок, обнаруженных с помощью API Bit.ly, на данный момент является управляемым. Во избежание переполнения главной страницы после вашей модерирующей сессии, одобренные ссылки публикуются один за другим по времени. Отсюда действие «предпубликация» на снимке экрана выше, которое помещает ссылку в очередь публикации. Очевидно, это все далеко от совершенства. Моя цель — сделать процесс курирования полностью автоматическим. В качестве следующего шага я попытаюсь реализовать контролируемый алгоритм машинного обучения, когда у меня будет большой объем обучающих данных с правильными решениями публикации / отклонения.
Кроме того, другая конечная точка Bit.ly предоставляет данные о количестве переходов по ссылкам. Этот счетчик периодически обновляется бэкэндом Scalabitz и отображается на главной странице.
Зачем играть?
Одна из причин, по которой я выбрал инфраструктуру Play, заключалась в том, что я хотел изучить создание полностью асинхронного и неблокирующего веб-приложения. Play помогает вам создавать реактивные приложения на всех уровнях: от обработки HTTP-запросов до выполнения внутренних REST-вызовов и запросов к базе данных. Первые два являются встроенными функциями Play, а для последних вы можете использовать драйвер ReactiveMongo . Несмотря на то, что у водителя нет версии 1.0, пользоваться им приятно.
Основным строительным блоком для этого приложения является scala.concurrent.Future
. Представленный в Scala 2.10, Future
API предоставляет очень хороший способ асинхронного программирования. Он был принят как Play, так и Akka, поэтому все части отлично сочетаются друг с другом. Чтобы понять, насколько легко вы можете выполнять неблокирующее и асинхронное программирование с помощью Scala и Play, рассмотрите следующий фрагмент:
ScalabitzService.getPublishedArticles(0, Some(100)).flatMap { articles => val clickFutures = for(article <-articles) yield clickWSCall(article) Future.sequence(clickFutures) }
(см. код в контексте на GitHub)
getPublishedArticles
Метод возвращает Future[List[ScalabitzArticle]]
, по существу , обещая , что этот список статей будут доступны из Монго в какой — то момент в будущем. Далее мы должны указать, что должно произойти с результатом внутри, Future
когда он успешно завершен. Мы можем сделать это, определив преобразование, в данном случае используя flatMap
метод. Фактически, flatMap
это операция монадического связывания, делающая ее общим механизмом для создания действий с другими, Future
как мы собираемся сделать здесь.
На фактическое преобразование. Для каждой из статей мы хотим получить количество кликов. Поскольку нет единой конечной точки Bit.ly, принимающей несколько идентификаторов одновременно, нам нужно сделать один вызов Bit.ly для каждой статьи. Очевидно, что мы не хотим делать эту блокировку и последовательно. Таким образом, для понимания создается на List[Future[(String, Int)]]
основе articles
мы получили от внешнего вызова базы данных. Метод clickWSCall
использует неблокирующие WS API от Play для выполнения этих вызовов, в конечном итоге возвращая a Future
. Теперь переменная clickFutures
представляет результат всех этих вызовов в какой-то момент в будущем. Тем не менее, нет никаких блокировок или заняты ожиданием любого вызова WS. Затем мы кормим полученный список типа List[Future[(String, Int)]]
к Future.sequence
методу. Et вуаля, это создает новыйFuture[List[(String,Int)]]
(обратите внимание на инверсию в типе?), в конечном итоге содержащую все количество кликов, связанных с их идентификатором в кортеже. Он асинхронно завершается, когда все отдельные фьючерсы завершаются автоматически.
Тема реактивных приложений заслуживает отдельного поста в блоге, который обязательно последует … После некоторых корректировок парадигма кажется почти такой же естественной, как если бы она делала это «по-старому». Почти, за исключением обработки ошибок, которую я еще не очень элегантно решил. Еще одной областью улучшения является нахождение хорошей модели для внедрения служб в зависимости (может быть, это шаблон Cake ?), В настоящее время есть только одноэлементные объекты, соединенные напрямую друг с другом.
развертывание
За последние годы у меня было много игрушечных проектов, но ни один из них не был сделан из комфорта моей собственной машины. Поэтому я все еще ищу идеальную производственную среду. Heroku является текущей платформой развертывания для Scalabitz, потому что она изначально поддерживает приложения Play, имеет низкое трение и бесплатна. Развертывание приложения так же просто, как сделать толчок Git в Heroku, что приятно. Поскольку приложение перестраивается на Heroku до его развертывания и запуска, оно работает немного медленнее, чем идеально. Кроме того, я не уверен, как долго бесплатный уровень будет работать для этого приложения. Я думаю, я скоро узнаю.
Будущие разработки
Далее автоматически добавляется в твиттер-аккаунт @scalabitz опубликованные ссылки. Таким образом, чтобы получить хороший канал статей Scala! С другой стороны, следующим приоритетом является более точная классификация входящих ссылок. К счастью, в прошлом году я улучшил свои навыки машинного обучения, так что это должно быть хорошим испытанием. Еще одна интересная идея — обобщить эту «платформу», поскольку то же самое можно сделать не только для Scala, но и для ряда тем.
Короче говоря, у меня есть масса идей для дальнейшего развития Скалабица. Но самый важный вопрос: что вы думаете? Перспективный? Бессмысленно? Дайте мне знать в комментариях или в Twitter!