Как запустить Wordpress в Azure

Казалось бы, самый простой сценарий по запуску блога Wordpress в облаке. Однако, даже тут есть подводный камень и ошибка в официальной инструкции, которая опубликована на сайтах Микрософта. В этой инструкции предлагается развернуть отдельно сервак с Apache+Php и отдельно Mysql DB. Проблема последнего в том, что Mysql не является базовой возможностью Azure и оно заводит базу данных предоставленную внешним сервисом с тарификацией по дополнительному тарифу. То есть шанс, что будут списывать бабки с карты втихую.

Правильное решение - это в том же окне поиска обзара диска для разворачивания, найти диск Wordpress от Bitnami. При этом база данных mysql будет поднята на той же машине, что и Apache/PHP. Что дешево (даже бесплатно) и работает без сбоев, вызванных передачей по сети.


Метки: ,   Категории:Blogs | microsoft


Про жизнь в Южной Осетии

Недавно Путин встречался с президентом Южной Осетии, к которой лично у меня был и остается ряд вопросов. В сети появилось развернутое интервью об этой встрече одного из участников. По сути говорится, что Россия тратит на регион 2,5 млрд.р. в год, а нужно 3 млрд. на газификацию региона. Я ничего против не имею, чтобы "кормить" таких достойных людей, как осетин. Но открытым остается вопрос, что Россия получает взамен и почему этот географический регион до сих пор не в составе большой страны? И неужели план газификации нельзя было утвердить за 7 лет, прошедшие с 2008 года? Или же руководство нашей страны ждало 7 лет, пока что-то не случится, чтобы дать ход развитию сотрудничества? 


Метки:   Категории:Blogs


Донецк в репортаже первого канала

Вчера в репортаже первого канала про Донецк во всю показывали одну из центральных частей города. Где, как оказалось, и находится основной покерный клуб города со звучным названием "Третий рим"(ссылка на панораму гугла). Напротив большое здание - "национальный академический театр оперы и балета". С такой подачи первого канала, версия о том, что Донецк будет взят украинской армией к 24 августа, к годовщине падения Рима - кажется не такой уж и фантастичной. И тогда установится длительное перемирие.

На фоне этого, обе противоборствующие стороны активно занимаются созданием по Шекспиру "Кориоланов" - героев войны. Которые в независимости от того, что было на фронте, вернуться домой как новые вожаки и будут посажены рулить стадом. По аналогии того, как генерала Лебедева после чеченской войны хотели в президенты продвинуть.


Метки: , , ,   Категории:Blogs | news


Самый успешный стартап Рунета

Незаметно в российском интернете загорелась новая информационная звезда - простой блог на Друпале "Русская Весна", за три месяца поднявший посещаемость с нуля до 3 млн. просмотров в сутки. Небывалый успех, простимулированный активными репостами в социальных сетях. Следовательно, успех блога определяется прежде всего уникальностью контента и связями, насколько верхи заинтересованы его раскрутке.

Этот блог имеет зону .SU, а не .RU, как и другие подозрительные ресурсы занимающиеся повышением вражды между родственниками на постсоветском пространстве. Например, anna-news.info, eot.su, oplot.info, interbrigada.org. Видимо не хотят, чтобы запросы к ним затрагивали российский корневой DNS.

С определением хостинга (месторасположения сайта), в отличии от германского annanews(сейчас они переехали на московский хостинг) немного сложнее. Так как блог находится за CDN-серверами CloudFlare, а на сайте нет дельных контактов. Но, точно такую же структуру имеют все сайты-блоги, которые делает Харьковская веб-студия и сервера которой, неприкрытые CDN, находятся в Харькове. Есть основания полагать, что если бы действительно боролись с ополченцами, а не пытались заставить Россию ввести войска, то эти бы информационные ресурсы давно бы прикрыли.

И для увеличения ненависти в посте отмечу про немецкую разведчицу, которая теперь работает в штабе ополчения называя себя журналисткой. До этого в том же штабе долго сидели немецкие наблюдатели ОБСЕ, называя себя заложниками, о которых теперь уже известно, что они работали разведчиками. То есть Германская заинтересованность в делении Украины на 2 части и в организации Новоевроссии видна везде.


Метки:   Категории:Blogs | idea


Рейтинг Яндекса перестал работать официально

Голландская компания Яндекс решила закрыть рейтинг блогеров. Официальный текст ниже в цитате. По сути они говорят, что писатели в блогах никому не нужны, а нужны лишь потребители информации в соц.сетях. Также они выпилили мой комментарий к их посту. Отмечу, что из-за действий Яндекса перестает работать информация о посещаемости тех или иных постов, а это может очень негативно сказаться на рейтингах типа t30p, но посмотрим через неделю.

Сегодня Яндекс закрывает часть сервиса Поиск по блогам ( http://webmaster.ya.ru/replies.xml?item_no=18640 ) - с главной страницы сервиса пропадают рейтинги блогеров и записей, а на страницах блогов перестают работать кнопки, показывающие количество читателей. При этом поисковая функциональность сервиса продолжает работать. В связи с этим с 25 апреля мы изменяем соответствующую функциональность и в API Поиска по блогам.

Станут недоступны RSS-поток рейтинга блогеров и информация о посетителях записей в API популярных записей.

Поисковая часть API продолжает работать в прежнем режиме.


Метки:   Категории:Blogs | Yandex


Комментарии в новостям

Сборник свежих новостей, которые захотелось собрать в одном месте, чтобы сохранить ссылки, да и комментарии добавить.

100 тыс. русских гагаузов провели референдум и, как я писал ранее, выбрали сторону Таможенного союза, а не Европы. Так решило абсолютное большинство ~90%. Правда референдум проведен с запаздыванием в 3 месяца, что странно.

Вторая новость, США начали набор совестливых блогеров для ускоренного обучения блогингу в Америке этим летом.Попроситься чтоли на поездочку, так как она бесплатна, боюсь, что там все места расписаны.

Далее, после некоторого ожидания, согласно матрице кинофильмов вышел русский трейлер Трансформеров. Толком ничего не говорится, кроме как "Приготовьтесь к истреблению в июле 2014".


Фильмы фильмами, а американские корабли вошли в черное море. Паникерский пост где все расписано. Можно предположить, что на этот раз они выгрузят оружие в Одессе.

К военной агрессии готовится и Румыния, как пишет их издание adevarul.ro (Правда) в виде PDF-файла. Понравилось, что у восточной Украины уже нарисовали логичный флаг и герб.

На русский язык переводят какие-то РосВести и получают примерно следующее
Руководство Румынии ввело состояние максимальной готовности к реализации оперативного плана «Романия Маре». В соответствии с ним должна произойти интервенция румынских вооружённых сил и спецподразделений на территорию Украины. План будет задействован румынскими властями при нарастании хаоса на Украине. В соответствии с планом «Романия Маре», предусматривается интервенция румынских вооружённых сил и спецподразделений на территорию Черновицкой области Украины и в часть Одесской области. Первый эшелон введения основных сил для контроля за территорией части Одесской и Черновицкой областей составят механизированные соединения румынской армии. Это будут 1-я механизированная бригада «Argedava» и бригада «Rovine».

И в завершение, такие же публикации идут в венгерской прессе.

UPD: Кстати, НАТО уже доставляет солдат на Украину, как сообщил будущий ее президент Царев, для проведения совместных учений в марте.

Метки:   Категории:Blogs | news


Темы дня Яндекса

Случайно обнаружил, что Яндекс обновил "темы дня", сделав их в стиле вымирающего pinme-абстракционизма (картинка ниже). Подобная тема оформления популярна у женской аудитории, в то время как (ну или мне так казалось), поиск по блогам Яндекса более популярен у мужской части. Да и самое главное, что стоит отметить. Какие-то слухи, да еще и какой-то поп-дивы, явно не вписываются в русскоязычную информационную повестку сегодняшнего дня.


Метки:   Категории:Blogs | Yandex


Блоги@Mail.ru умер

Тихой сапою закрывается, точнее принудительно переносят все блоги в "моймир" на майру. Чем это грозит и как теперь их индексировать - ума не приложу. Уж лучше бы закрылись с концами и все тут. А пока системное сообщение выглядит следующим образом
С 23 декабря 2013 года остановлена возможность создавать новые посты и писать комментарии в Блоги@Mail.ru. С 3 марта 2014 года будет закрыт доступ ко всем страницам проекта Блоги@Mail.ru.

Метки:   Категории:Blogs | news


Будущее блогов

Давно не было новостей о том, что блоги куда-то развиваются. Есть хабр, который прибыльный, так как им платит щедро микрософт. Есть всякие дёти и лепра, которые в роли идейных маньяков. Есть жж - чисто политическая конструкция. Лиру и бабиру - женско-хомячковый спам.

Но стоило появиться мобильным приложениям вида Инстаграм или Wordpress для мобильного, так казалось бы ничего нового и не ждать. Но вот прогер из Wordpress вышел из компании и создал новую площадку - Node.js-based blog-platform Ghost, а также заявил, что будущее блогов - это операционная система. То есть, когда есть человек и ноутбук, то у него обязательно есть блог. И мне кажется, что Микрософт думает в том же направлении. Если посмотреть последние нововведение в windows 8.1, то стоит обратить внимание на 2 вещи. У всех пользователей появился SkyDrive - сетевой диск. Появился Store - установка приложений на комп, по принципу мобильных приложений. То есть для установки программ на компьютер пользователя скоро более не нужен будет браузер. Он оказался уж слишком сложной прокладкой для взаимодействия с интернетом. Скоро будет простое десктопное приложение "БЛОГ", установив которое пользователь получает возможность публикации своих материалов и мыслей в широкие слои интернета. Уверен, что wordpress уже работает над этим.

Но пока у нас нету государственного контроля за "диким социальным интернетом" и кому-то еще выгодно, чтобы один компьютер позволял вести несколько социально-активных блогов, то привычные нам веб-блоги будут продолжать жить и как-то влить на "общественное мнение" в СМи.

Метки:   Категории:Blogs | trend


К вопросу об осознанности распространения информации в соц.сетях

Один ужасный случай привлек мое внимание. 4-ре недели назад пропал бизнесмен, ехавший из Москвы в Подольск. Родственники искали его путем размещения объявлений в соц.сетях. Собрали десятки тысяч лайков и перепостов. Страничка дочки стала самой обсуждаемой Вконтакте. В итоге ЛизаАлерт сообщила, что человек погиб. Однако ничего не понятно о причинах трагедии. Очевидно, что это была не рядовая авария, которую быстро бы установили. Но никто из социальных сетей не пытается или не может узнать, что стало причиной смерти. Либо сеть не приспособлена для связывания двух временных событий между собою, либо психика у пользователей окончательно деградировала, что их не интересует то, что они распространяют и лайкают.

Вывод: пользователям хотя бы должны приходить уведомления об изменении текста, который они распространили или лайкнули.

Метки:   Категории:Blogs


Рейтинг одноклассников

Недавно составил общий рейтинг всех групп из одноклассников - t30p.ru/ok. В целом, сразу видно, что люди там только и делают, что делятся рецептами и смеются. Жрут и Ржут. Даже неудобно добавлять социальный авторитет для них. Пока же только рейтинг. Параметр "активность" выражает число нажатий "Класс" в последних постах группы.


Метки:   Категории:Blogs | Analytics | t30p


Золотые пистолеты и Полевые лагеря (16 штук)

Поражает идиотизм, который развернулся в либеральных СМИ по поводу тендера золотых пистолетов. Это навело меня на мысль, что на самом деле сейчас во всю идут другие тендеры, о которых нам сознательно не сообщают. Ведь одна из задач журналистов - замалчивание информации, и когда говорят, что вот на сайте гос-закупок что-то нашли, то вряд ли даже кто-то открывал этот сайт. По аналогии с Америкой, Russua Today не стыдясь говорит, что в США во всю закупают палатки, дубинки и гробы на фоне бюджетного кризиса. Так почему же у нас может быть по другому, у нас же тоже дыра? И покопавшись в гос-закупках оказалось, что через день после золотых пистолетов Министерство обороны заказало 16 автономных полевых лагерей на сумму 6,8 млрд. рублей. Очевидно, что это не рядовой случай и преследует какие-то цели в следующем году. последний раз подобный заказ проводился полтора года назад согласно истории, но содержал лишь 6 лагерей.
Но я в делах мин.обороны не спец, кроме удивления более ничего высказать не могу.

Зато напишу про Мониторинг соц.сетей на госзакупках. Тут появился веселый заказик на печать книжки - "Как продвигать библиотеку в Твиттер" (facepalm). У меня несколько вопросов! Кто-нить видел ведение "библиотеки" в Твитторе? Кто-нить сидит в Твитторе из библиотеки? Или может это тендер на публикацию библиотеки в Твиттор? Да и кому нужна эта библиотека в Твитторе, когда у сервиса коротких сообщений другая функция - развращение и коверканье языка у молодежи. А печать книжки - это такое стандартное решение библиотекарей на любую проблему. Если есть проблема - то нужно печатать книжку. Если меня прочтут авторы этих книжек, то я не откажусь от электронной версии на почту.

А теперь серьезно, Префектура СВАО озаботилась мониторингом себя на следующий год, денег дают 2,5 млн., судя по ТЗ планируют выиграть через фирму прокладку, так как избыточные детали по-вырезаны, а ряд абзацев, скажем про Silverlight, 2 часа простоя в неделю и регулярное резервное копирование каждый час - сразу выдают исполнителя. В любом случае, у людей грамотный подход, а жители СВАО смело могут писать в социальные сети всякие отзывы про префектуру.

PS: Кажется меня опередили, в топах тоже пишут про спец.заказ МВД на бронеавтомобили по разгону митингов.

Метки:   Категории:Blogs | Government


Рейтинг фильмов

Сегодня Яндекс купил кинопоиск, как шутят на хабре, что теперь появится 9-ти бальная шкала пробок в кинотеатры.
По случаю починил свой автоматический рейтинг упоминаемости фильмов, и оказалось, что действительно Сталинград сейчас самый обсуждаемый фильм.


Метки:   Категории:Blogs | t30p


Топ фейсбука русскоязычный

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

По случаю того, что т30п индексирует русскоязычный фб, я решил посмотреть, что же такого ценного есть в фейсбуке, что кто-то боится его потерять. И составил стандартный топ фейсбука http://www.t30p.ru/F с некоторыми фильтрами против спама. Как видим ничего интересного кроме котиков и перепостов либеральных СМИ там нет. То есть с уверенностью можно сказать, что социальная сеть успешно решает задачу недопущения коллективного общественного мнения в интернете.

Метки:   Категории:Blogs | Facebook


Медиа как социальный сервис

Выяснилось, что не все понимают, что СМИ являются социальным сервисом. Да и вообще сочетание социальный сервис давно ассоциируется с бомжами и раздачей пищи или религией. Поэтому ниже немного общей теории на заметку. Всего у СМИ существует 6 основных функций. В книжках пишут, что их 7, но "Социализацию" и "воспитание" я объединяю, так как сегодня, когда мировоззрение человека формируется по принципу "веселый зомби", никакого воспитания уже нет и не надо. Издания, типа "наука для молодежи" - уже не читают. Я смело объединил эти две функции в одну - "социализация", которая вещает "делай как они", вот и все воспитание. Таким образом ниже у нас функции в колонках применительно к существующим СМИ.

Информирование Общественное мнение Социализация Развлекательная Объединение общества История современности
О чем это? Передача информации от власти к народу Передача информации от народа к власти Рассказать как надо жить (образы поведения) Убить свободное время индивида Сформировать круг общения человека (коммуникативно-интеграционная на территории распространения) Ленин говорил, что журналистика пишет историю современности.
Телевидение + , все работает - , очевидно не работает + , это образ дома2 + , футбол и прочее + , объединяются даже по принципу "несмотрения" телевизора - , есть видео-архивы, но обычно просто переснимают, то есть история не поднимается.
Газеты +, все они продались - , не работает ~ , слабо + , хорошо - , сомневаюсь, что еще бумага объединяет + , часто есть отсылки на газеты советского времени
Интернет-СМИ +, все они продались ~ , не работает + , да, в виде инструкций, обзоров. - , нет, не читают интернет новости ради развлечения + , люди часто обсуждают один и тот же новостной сайт. + , даже лента.вру запустила спец-проект по написанию истории современности.
Блоги, соц.сети ~ , слабо, есть проплаченные блогеры ~ , слабо, ведь редактора решают какой сюжет сделать новостью. + , других почитал и стал повторять. + , да. + , присутствие в определенной сети определяет классовость в жизни. - , никто не думает об истории
Топ блогосферы (автоматизированное СМИ) + , слабо, но так как людям нечего писать, то они пишут о том, что делает власть. + , что стало общественно-популярным, то и попадает в топ. ~ , слабо, но популярные темы учат других как становиться популярными. + , хорошо, развлекательное часто бывает популярным + , да - , нет, то что было в топе поза-позавчера уже никому не интересно.

Исходя из этой таблицы меня и беспокоит, что блоги и соц.сети не пишут историю современности. Единственное, что указывает на достижения в прошлом - это социальных авторитет и история топов.

Метки:   Категории:Blogs | Analytics


Поисковый виджет

Хочу обратить внимание, что на т30п давно работает возможность создать виджет с упоминаниями любого слова и скопировать его в любой блог или сайт. Панель создания виджета пока очень простая. В списке в хронологическом порядке показаны последние упоминания по всей русскоязычной блогосфере. К сожалению, код виджета не работает под IE, так как под ним вылетает ошибка jQuery, если кто знает как подправить, то пишите. Дизайн тупо скопирован со старого твиттеровского. Как всегда все в бете, но вроде работает стабильно.
UPDATE: Ошибка в IE была исправлена путем обновления jQuery


Метки:   Категории:Blogs | Code


Мониторинг соц.сетей на госзакупках - 6

Пару месяцев назад о том, что следственный комитет объявил тендер на гос.закупках на мониторинг социальных сетей написали все СМИ. Поэтому я тогда ничего не писал в общем потоке. Но неделю назад были подведены результаты и оказалось, что тендер провалился. Провалился впервые среди всех тендеров в этой области. Хотя казалось бы Следственный комитет РФ - это как раз та организация, которой мониторинг нужнее всего. Они ведь и должны следить за тем, что происходит, в отличии от Верховного Арбитражного Суда, деятельность которого на мой взгляд никоим образом не должна зависеть от общественного мнения. Но ничего, это недоразумение будет ликвидировано Путиным осенью этого года и никакой мониторинг его не спасет.

Итак, тендер провалился ввиду того, что был один лишь участник, у которого вроде все идеально подходило по ТЗ. Если почитать события тендера, то в последний момент был выдвинут какой-то фиктивный второй участник, у которого даже сайт мертв, однако, что-то пошло не так и мертвого участника не допустили к конкурсу. Других участников не было, толи СКРФ все боятся, либо денег маловато дают.


Метки:   Категории:Blogs | Government | Analytics


Лена Миро купила еще 2000 подписчиков

Занятная история, жил был на Лиру простой журнал одной девочки, которая кроме всего прочего была популярной среди женской аудитории. Но потом бросила вести дневник. И его купили некто пропагандирующие здоровый образ жизни под брендом ЛеныМиро, просто переписав всех старых подписчиков, переименовав блог. То есть купив 2000 читателей к очередной копипасте. В теории такие выходки должны блокировать как денежные операции противоречащие правилам сервиса. Но не исключено, что блог был просто подарен. Вот что пишет сама владелица по ссылке выше:
Вести блог нет ни малейшего желания, ещё меньше на это времени, уж простите. В конце концов, ЖЖ рулит и доставляет значительно больше лулзлов. Ко мне тут постучались и попросили отдать свою лирушечку на благое дело – дело похудения и приведения девчачьх народных масс в красавиц. Я подумала: не, ну а чо? И отдала. Теперь этим блогом будет пользоваться Лена Миро, она же главный фитнес-блогер страны miss-tramell.

Метки:   Категории:Blogs


КНДР - что там происходит

Одна желтая газетенка написала сегодня о подготовке ядерных ударов где-то в районе Северной Кореи. Можно было бы пропустить это, списав на привычную манеру составления заголовков. Однако, если развернуть все новости по Северной Корее в нормальном виде. То ситуация там действительно развивается стремительно.
  • В начале марта, как упоминалось ранее, там прошли совместные учения США и Южной Кореи. План учений назывался как "Отработка действий по оккупации Северной Кореи." И 21 марта эти учения завершились.
  • США заблокировали денежные операции с Северной Кореей.
  • Северная Корея разорвала перемирие по результатам войны 50-53гг. и произвела испытания ракет малой дальности.
  • В рамках учений США провели имитацию ядерной бомбардировки.
  • Операции с недвижимостью в Южной Корее упали до минимума с 2006 года, когда были проведены первые ядерные испытания.
  • После имитации бомбардировок - Ким Чен Ын распорядился готовиться к войне (в эти выходные), к нанесению ракетных ударов на все вражеские объекты захватчика США на материке, на Гавайях и на Гуаме.

Метки:   Категории:Blogs | news


Контекстный поиск с русской и английской морфологией на основе проектов с открытым исходным кодом и облачных сервисов Microsoft Azure

keywords: контекстный поиск, русская морфология, поточное индексирование, облачные сервисы
abstract: В статье дается общее представление об архитектуре поискового сервиса, построенного на проектах с открытым исходным кодом и рассматриваются базовые проблемы реализации безотказной его работы в условиях поточных данных и минимальных системных ресурсах. Реализация опирается на следующие проекты:

На скриншотах присутствует программа для работы с BlobStorage - AzureStorageExplorer, которая также доступна с открытым кодом - http://azurestorageexplorer.codeplex.com/. Сам пост написан и хостится в облаке Azure с использованием BlogEngine - http://blogengine.codeplex.com/

 

Общее

Разложим задачу создания поискового сервиса на пять составляющих: 1. Сбор данных. 2. Фильтрация, токенизация, стемминг, синонимизация данных. 3. Помещение в поисковый индекс. 4. Обработка поисковых запросов. 5. Распознавание что хотел пользователь и предоставление нативного языка запросов.

В сегодняшней статье не будут разобраны пункты 2 и 5. Так как токенизация и стемминг осуществляется при помощи библиотеки Lemmatizer.dll, разработанной сотрудниками Яндекса, и рассматривалась мною 4 года назад. За прошедшее время статью открывали более 20 тыс. раз. Сама же библиотека Lemmatizer.dll перестала быть платной и с 2011 года доступна всем желающим в исходниках. Последний пункт - создание человеческого языка, запросов требует большую наработку данных уже от пользователей производящих запросы, и может дорабатываться в процессе работы поискового сервиса. На сегодняшний момент языком запросов будет являться стандартный синтаксис Lucene, который достаточно гибкий. Так например, орфографические ошибки в запросах можно исправлять с помощью нечеткого поиска "word~",

Сбор данных

В поточном режиме при помощи Twitterizer.NET подключимся к Twitter.Streaming API. Вытаскиваем все русскоязычные твиты, фотографии instagram, видео с youtube (+Vimeo), посты на стене Вконтакте, фотографии Facebook, посты GooglePlus. Для разбора данных воспользуемся библиотеками BlogsAPI, GoogleGData, FacebookSDK соответственно. Вытаскиваем из соц.сетей все данные, которые не требуют access_token - то есть присутствия пользователя. Примеры извлечения этих данных есть в примерах самих библиотек. Также важно понимать, что большинство ссылок на информацию пропущено через сервисы сокращения ссылок. Для разворачивания таких ссылок есть простая функция в BlogsAPI для большинства известных сокращателей:

Copy Source | Copy HTML
  1. Shortener shrt = Common.GetShortenerByLink(url.ExpandedUrl) as Shortener;
  2. if (shrt != null)
  3. {
  4.         fulllink = shrt.ConvertDataTo(url.ExpandedUrl, ItemType.ShortUrl, ItemType.FullUrl);
  5. }

После извлечения - данные группируются в пакеты (Batch) и отправляются на WCF-сервис индексирования. Для сложных данных, требующих дополнительных запросов используется Azure BlobQueue(примеры работы) для отложенной индексации или, как в случае Facebook и Vkontakte, группового извлечения данных, когда в одном запросе к социальной сети запрашивается сразу несколько постов.

Так как много данных не бывает, то в тотже индекс мы добавим все посты (если быть точным - большую часть) livejournal.com, liveinternet.ru, qip.ru,juick.com, blogi.mail.ru и т.д. Отдельно отмечу, что такой интернет гигант как Яндекс не выдает информации по своим блогам wow.ya.ru и видеоблогам на video.yandex.ru через API.

Индексирование

Благодаря облачным технологиям Микрософта у нас появляется замечательный объект BlobStorage, который предоставляет до 100 ТБ под хранение индекса. Чтобы разместить в нем наш поисковый индекс формируемый Lucene, достаточно воспользоваться проектом AzureDirectory. Основной индекс будет храниться в BlobStorage, а на локальном инстансе WebRole создается специальная промежуточная директория - LocalStorage, где производятся все операции с индексом по поиску и изменению его. Важно понимать, что инстансов WebRole у нас должно быть 2 и более, так как к конечный момент времени один из них заблокировал индекс на запись (добавление объектов), а с остальных идет непрерывное чтение (поисковые команды). При этом чтение на инстансе, где идет запись - невозможно. Модель "один писатель, много читателей". При завершении записи данные с локального LocalStorage копируются в центральный BlobStorage откуда синхронизируются на остальные машины. Ниже приводится класс, который реализует связь Lucene и AzureDirectory, функции работы с Lucene не приводятся, примеры этого есть в интернете.

  1. public abstract class IndexerBase
  2. {
  3.     protected IndexerBase()
  4.     {
  5.     }
  6.     /// <summary>
  7.     /// Constructor
  8.     /// </summary>
  9.     protected IndexerBase(string sDir):base()
  10.     {
  11.         IndexDir = sDir;
  12.     }
  13.     /// <summary>
  14.     /// Объект блокировки, переопределяемый дочерними классами
  15.     /// </summary>
  16.     protected static object oWriting = new object();
  17.     /// <summary>
  18.     /// Название папки с индексом
  19.     /// </summary>
  20.     protected string IndexDir = "IndexBase";
  21.     public string cachepath
  22.     {
  23.         get
  24.         {
  25. #if DEBUG
  26.             return Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), IndexDir);
  27. #else
  28.             return Path.Combine(RoleEnvironment.GetLocalResource("MyStorage").RootPath,IndexDir);
  29. #endif
  30.         }
  31.     }
  32.     private AzureDirectory _azureDirectory;
  33.     /// <summary>
  34.     /// Директория
  35.     /// </summary>
  36.     protected AzureDirectory azureDirectory
  37.     {
  38.         get
  39.         {
  40.             if (_azureDirectory != null) return _azureDirectory;
  41.             _azureDirectory = new AzureDirectory(CloudStorageAccount.Parse(
  42.                         RoleEnvironment.GetConfigurationSettingValue("SearchConnectionString")), IndexDir,
  43.                         new SimpleFSDirectory(new DirectoryInfo(cachepath)));
  44.             return _azureDirectory;
  45.         }
  46.     }
  47.     /// <summary>
  48.     /// Указывает на первое время запуска и открытия поискового индекса
  49.     /// </summary>
  50.     protected static Boolean firsttime = true;
  51.     /// <summary>
  52.     /// Статический поток занимающийся перезаписью сегментов
  53.     /// </summary>
  54.     protected static ConcurrentMergeScheduler Merger = new ConcurrentMergeScheduler();
  55.     /// <summary>
  56.     /// То, как писать
  57.     /// </summary>
  58.     private IndexWriter _MyWriter = null;
  59.     protected IndexWriter MyWriter
  60.     {
  61.         get
  62.         {
  63.             if (_MyWriter != null) return _MyWriter;
  64.             lock (oWriting)
  65.             {
  66.                 if (_MyWriter != null) return _MyWriter;
  67.                 CreateMyWriter();
  68.             }
  69.             return _MyWriter;
  70.         }
  71.         set { _MyWriter = value; }
  72.     }
  73.     private void CreateMyWriter()
  74.     {
  75.         try
  76.         {
  77.             _MyWriter = new IndexWriter(azureDirectory, MyAnalyser, false);
  78.             //важны атомарные операции, так как несмотря на lock(oWriting) в случае нескольких дочерних классов индексирования, возникнуть может многопоточность
  79.             Interlocked.Exchange(ref LockFailsCounter,  0); //успешно открылось на запись, сбросили счетчик неудач
  80.         }
  81.         catch (LockObtainFailedException e0)
  82.         {
  83.             //если уже много ошибок подряд, около часа
  84.             if (LockFailsCounter > 30)
  85.             {
  86.                 //принудительно удаляем блокировку =(
  87.                 azureDirectory.ClearLock("write.lock");
  88.                 //Trace.Write(e0);
  89.                 Trace.Write("Принудительное снятие старой блокировки на запись.");
  90.                 Thread.Sleep(60 * 1000); //1 min for unlock
  91.                 _MyWriter = new IndexWriter(azureDirectory, MyAnalyser, false);
  92.                 Interlocked.Exchange(ref LockFailsCounter,  0);//успешно открылось на запись, сбросили счетчик неудач
  93.             }
  94.             else
  95.             {
  96.                 Interlocked.Increment(ref LockFailsCounter);
  97.                 throw;
  98.                 //вызовем ошибку, чтобы запрос на индексирование был повторен через некоторое время, когда файл разблокируется
  99.             }
  100.         }
  101.         catch (Exception err)
  102.         {
  103.             Trace.WriteLine("Не удалось открыть старый индекс: " + err.Message);
  104.             if (System.IO.Directory.Exists(cachepath))
  105.             {
  106.                 System.IO.Directory.Delete(cachepath, true);
  107.                 //удаляем возможный старый хлам помешавший открытию
  108.                 System.IO.Directory.CreateDirectory(cachepath);
  109.             }
  110.             //создадим новый еще раз!
  111.             _MyWriter = new IndexWriter(azureDirectory, MyAnalyser, true);
  112.         }
  113.         _MyWriter.SetMergeScheduler(Merger);
  114.         _MyWriter.SetMergeFactor(10);
  115.         //_MyWriter.SetUseCompoundFile(false);
  116.     }
  117.     /// <summary>
  118.     /// Время открытия поискового индекса
  119.     /// </summary>
  120.     protected DateTime? SyncTime
  121.     {
  122.         get { return HttpContext.Current.Application[GetType() + "synctime"] as DateTime?; }
  123.         set { HttpContext.Current.Application[GetType() + "synctime"] = value; }
  124.     }
  125.     /// <summary>
  126.     /// Количество неудачных попыток открыть индекс на запись
  127.     /// </summary>
  128.     /// <remarks>Важно, что это один объект на все дочерние классы индексирования</remarks>
  129.     private static int LockFailsCounter =  0;
  130.     /// <summary>
  131.     /// То, как писать
  132.     /// </summary>
  133.     protected IndexSearcher _MySearcher
  134.     {
  135.         get { return HttpContext.Current.Application[GetType() + "searcher"] as IndexSearcher; }
  136.         set { HttpContext.Current.Application[GetType() + "searcher"] = value; }
  137.     }
  138.     public IndexSearcher MySearcher
  139.     {
  140.         get
  141.         {
  142.             if (SyncTime.HasValue && SyncTime.Value.AddHours(1)<DateTime.Now)//более ~  с момента последней синхронизации(!)
  143.             {
  144.                 //делаем запись только в том, случае, если индекс уже был успешно открыт ранее на запись, и сделана синхронизация с основным
  145.                 //иначе может произойти потеря
  146.                 IndexCommit();
  147.             }
  148.             if (_MySearcher != null) return _MySearcher;
  149.             lock (oWriting)
  150.             {
  151.                 if (_MySearcher != null) return _MySearcher;
  152.                 var myPerf = new PerformanceTimer();
  153.                 myPerf.StartTimer();
  154.                 try
  155.                 {
  156.                     _MySearcher = new IndexSearcher(azureDirectory, true);
  157.                 }
  158.                 catch (CorruptIndexException)
  159.                 {
  160.                     //требуется починка индекса
  161.                     FixIndex(RoleEnvironment.DeploymentId);
  162.                 }
  163.                 catch (Exception e1) //если у нас нету еще директории индекса или сбой соединения
  164.                 {
  165.                     Trace.Write(e1);
  166.                     if (_MyWriter != null)
  167.                     {
  168.                         Trace.Write("Warning: have to close MyWriter to open MySearcher!");
  169.                         _MyWriter.Commit();
  170.                         _MyWriter.Close();
  171.                         _MyWriter = null;
  172.                     }
  173.                     if (System.IO.Directory.Exists(cachepath))
  174.                     {
  175.                         System.IO.Directory.Delete(cachepath, true); //удаляем возможный старый хлам помешавший открытию
  176.                         System.IO.Directory.CreateDirectory(cachepath);
  177.                     }
  178.                     try
  179.                    &nbssearcherp;{
  180.   nbsp;запись                      //важно для всех случаев когда индекс открывается в отсутствующую папку.
  181.                         var openindex = MyWriter;
  182.                     }catch(LockObtainFailedException)
  183.                     {
  184.                         //Папка с индексом успешно открыта и создана, однако другая роль уже пишет индекс
  185.                     }
  186.                     try
  187.                     {
  188.                         _MySearcher = new IndexSearcher(azureDirectory, true);
  189.                     }catch(FileNotFoundException foi)
  190.                     {
  191.                         //индекс скопированный с мастера не содержит нужного сегмента
  192.                         if(foi.InnerException!=null && foi.InnerException.GetType() == typeof(StorageClientException))
  193.                         {
  194.                             //вызываем утилиту исправления ошибок
  195.                             FixIndex(RoleEnvironment.DeploymentId);
  196.                         }
  197.                     }
  198.                     Trace.Write(String.Format("BadMode: _MySearcher {0} loaded in {1}", GetType().Name, myPerf.StopTimer()));
  199.                 }
  200.                 if(firsttime)
  201.                 {
  202.                     firsttime = false;
  203.                     Trace.Write(String.Format("GoodMode: _MySearcher {0} loaded in {1}", GetType().Name, myPerf.StopTimer()));
  204.                 }
  205.             }
  206.             return _MySearcher;
  207.         }
  208.     }
  209.     /// <summary>
  210.     /// Морфологический анализатор
  211.     /// </summary>
  212.     internal static MorphologyAnalyzer _MyAnalyser;
  213.     protected MorphologyAnalyzer MyAnalyser
  214.     {
  215.         get
  216.         {
  217.             if (_MyAnalyser != null && _MyAnalyser.Morph != null && _MyAnalyser.Morph.isLoaded) return _MyAnalyser;
  218.             Trace.Write("Заново загружаем MorphologyAnalyzer");
  219.             _MyAnalyser = new MorphologyAnalyzer(null, false);
  220.             return _MyAnalyser;
  221.         }
  222.     }
  223.     static IndexerBase()
  224.     {
  225.         RoleEnvironment.Stopping += delegate
  226.         {
  227.             try
  228.             {
  229.                 //дожидаемся завершения записи
  230.                 lock (oWriting)
  231.                 {
  232.                     //останавливаем поток слияния
  233.                     Merger.Close();
  234.                     Merger = null;
  235.                 }
  236.             }catch(Exception e)
  237.             {
  238.                 Trace.Write(e);
  239.             }
  240.         };
  241.     }
  242.     /// <summary>
  243.     /// Запись индекса на диск и переоткрытие поискового механизма
  244.     /// </summary>
  245.     protected void IndexCommit()
  246.     {
  247.         SyncTime = DateTime.Now;
  248.         lock (oWriting)
  249.         {
  250.             if (_MyWriter != null)
  251.             {
  252.                 //закрыли текущий коммит
  253.                 try
  254.                 {
  255.                     _MyWriter.Commit();
  256.                 }catch(CorruptIndexException)
  257.                 {
  258.                     FixIndex(RoleEnvironment.DeploymentId);
  259.                 }
  260.                 _MyWriter.Close();
  261.                 _MyWriter = null;
  262.             }
  263.             if (_MySearcher != null)
  264.             {
  265.                 //закрыли тукущий поисковик
  266.                 _MySearcher.Close();
  267.                 _MySearcher = null;
  268.             }
  269.         }
  270.         //открыли новый поисковик
  271.         var opensearcher = MySearcher;
  272.     }
  273.     /// <summary>
  274.     /// Серьезный процесс по востановлению индекса 
  275.     /// </summary>
  276.     /// <param name="DeploymentId"></param>
  277.     /// <returns></returns>
  278.     public string FixIndex(string DeploymentId)
  279.     {
  280.         var myPerf = new PerformanceTimer();
  281.         myPerf.StartTimer();
  282.         Trace.WriteLine(String.Format("Вызов проверки статуса и исправления индекса со значением  {0} / {1}", DeploymentId, GetType().Name));
  283.         string lockfile = GetType().Name + "_" + DeploymentId + ".lock";
  284.         Lock indexLock = azureDirectory.MakeLock(lockfile);
  285.         if (indexLock.Obtain())//блокировка основного индекса
  286.         {
  287.             try
  288.             {
  289.                 //устроим проверку индекса!
  290.                 CheckIndex fixer = new CheckIndex(azureDirectory);
  291.                 CheckIndex.Status oStatus = fixer.CheckIndex_Renamed_Method();
  292.                 Trace.WriteLine(GetType().Name+":status=" + oStatus.numBadSegments + ":" + oStatus.totLoseDocCount);
  293.                 if (oStatus.numBadSegments != oStatus.numSegments && oStatus.numBadSegments >  0)
  294.                 {
  295.                     fixer.FixIndex(oStatus); //опасная функция перезаписи
  296.                 }
  297.                 Trace.WriteLine(GetType().Name + ":FixIndex finished");
  298.                 //убедимся, что разблокировали индекс после правки
  299.                 azureDirectory.ClearLock("write.lock");
  300.             }
  301.             catch (Exception e1)
  302.             {
  303.                 Trace.Write(e1);
  304.             }
  305.             finally
  306.             {
  307.                 indexLock.Release();
  308.                 azureDirectory.DeleteFile(lockfile);
  309.             }
  310.             //////////******************////////////
  311.         }
  312.         return myPerf.StopTimer().ToString();
  313.     }
  314. }


Поясню основные моменты:
1. Класс абстрактный, так как от него наследуется WCF сервис, который может содержать свой индекс. То есть один инстанс может работать с несколькими индексами Lucene которые хранятся в одном BlobStorage в разных папках.
2. В Azure любой инстанс может быть выключен в любую секунду, поэтому важно обрабатывать событие остановки и корректно завершать работу отдельного потока ConcurrentMergeScheduler занимающегося оптимизацией индекса.
3. Объект чтения MySearcher у нас открыт всегда, чтобы минимизировать время поиска. Объект записи MyWriter создается только при получении данных на запись и соответственно закрывает и блокирует чтение для текущего индекса на инстансе.
4. Очень важна отказоустойчивость, поэтому такая детальная обработка ошибок LockObtainFailedException - когда пришли данные на запись, а основной индекс в BlogStorage заблокирован. Например, другим инстансом, который после этого падает по OutOfMemoryException и не снимает блокировки. Соответственно через несколько таких ошибок блокировка снимается автоматически и буфера поставщиков данных не успевают переполниться и потери данных не происходит.
5. Другая стандартная ошибка - CorruptIndexException - вполне может быть следствием сбоем сети при передачи больших файлов. При этом последняя операция записи отменяется, однако это занимает значительное время и блокирует другие операции записи. Ниже, как это выглядит в логах.




Еще раз перечислю основные проблемы, к которым нужно быть готовыми: одновременный приход запросов на индексирование, произвольное выключение инстанса, нечитаемость индекса, нехватка памяти, невозможность эксклюзивно заблокировать индекс, очищение LocalStorage. Последние подразумевает то, что согласно документации, подключаемый к инстансу диск несмотря на настройку cleanOnRecycle="false" всеравно не является величиной постоянной и может быть очищен в любой момент. Последнее означает повторную синхронизацию с центральным BlobStorage и открытие объектов чтения и записи займет дольше обычного (2-3 сек) на несколько минут. В итоге все работает стабильно и загрузка одного индексирующего инстанса показана ниже. К слову, мы используем всего-лишь два инстанса с минимальными мощностями, так как сильно ограничены в ресурсах. Пикам по 100% соответствует обработка пакета на индексацию. С уверенностью можно сказать, что за простой ресурсов мы не платим.


Вторая картинка приведена для сравнения, и формируется на системном портале Azure как среднее по WebRoles в Endpoint. Очевидно, что она не дает представления о том, что твориться с инстансами.

Выполнение поисковых запросов

В условиях, когда мы не знаем в каком состоянии находится инстанс, пишет он или нет, единственный правильный вариант - отправлять поисковый запрос на все инстансы, и брать первый ненулевой ответ. При наличии ресурсов можно было бы выделить отдельную Endpoint и WebRole, которая работала бы только на чтение. Но мы не ищем простых путей, поэтому на каждой webrole с индексом имеется следующая поисковая функция, выполняющаяся только если инстанс не пишет в индекс

Copy Source | Copy HTML
  1. /// <summary>
  2. /// Контекстный поиск с проверкой на поток записи, если открыта запись, то возвращаем NULL
  3. /// </summary>
  4. /// <param name="opts">Слова и параметры поиска</param>
  5. /// <param name="ignorelock">Не проверять идет ли запись</param>
  6. /// <returns>найденные результаты, CAN BE NULL</returns>
  7. public IndexedDocument[] SearchForAdvanced(SearchForOptions opts, bool ignorelock)
  8. {
  9.     //без проверки на блокировку
  10.     if (ignorelock) return SearchFor(opts);
  11.     bool isNotLocked = false;
  12.     try
  13.     {
  14.         isNotLocked = Monitor.TryEnter(oWriting);
  15.     }
  16.     finally
  17.     {
  18.         if (isNotLocked)
  19.         {
  20.             //важно освободить блокировку на поиск как можно скорее
  21.             Monitor.Exit(oWriting);
  22.         }
  23.     }
  24.     if (isNotLocked)
  25.     {
  26.         return SearchFor(opts);
  27.     }
  28.     //заблокировано
  29.     return null;
  30. }

И соответственно на поисковом клиенте реализуется многопоточная отправка с ожидаем быстрейшего ненулевого результата через ThreadPool.QueueUserWorkItem(state => SearchForAdvancedAsync(oData));

Copy Source | Copy HTML
  1. /// <summary>
  2. /// Объет параллельного запроса
  3. /// </summary>
  4. public class SearchAsyncObject
  5. {
  6.     /// <summary>
  7.     /// Число параллельных запросов
  8.     /// </summary>
  9.     public readonly int Count = 3;
  10.     /// <summary>
  11.     /// Число обработанных вызовов
  12.     /// </summary>
  13.     public int Finished =  0;
  14.     /// <summary>
  15.     /// Поисковые параметры
  16.     /// </summary>
  17.     public SearchForOptions opts = null;
  18.     /// <summary>
  19.     /// Результат поиска
  20.     /// </summary>
  21.     public IndexedDocument[] Founds = null;
  22.     /// <summary>
  23.     /// Mutex
  24.     /// </summary>
  25.     public ManualResetEvent mre = new ManualResetEvent(false);
  26. }
  27. /// <summary>
  28. /// Функция завершения поиска
  29. /// </summary>
  30. private static void SearchForAdvancedAsync(SearchAsyncObject data)
  31. {
  32.     try
  33.     {
  34.         IndexClient service = Indexer.GetIndexClient();
  35.         IndexedDocument[] founds = service.SearchForAdvanced(data.opts, false);
  36.         if (founds != null && data.Founds == null) //найден результат(!)
  37.         {
  38.             data.Founds = founds;
  39.             data.mre.Set();
  40.         }else if (Interlocked.Increment(ref data.Finished) >= data.Count){
  41.             data.mre.Set();//все потоки завершили работу
  42.         }
  43.     }
  44.     catch (Exception e1)
  45.     {
  46.         Trace.Write(e1);
  47.     }
  48. }

Функции добавления документов в индекс и выполнение операций поиска уже на конкретной машине выполняются как описано в документации Lucene.

 

Результат

Построенный поисковый сервис используется для выявления наиболее интересной информации в русскоязычных социальных сетях и ранжирования этой информации для формирования топа событий на сайте http://t30p.ru. Полученное таким образом самостоятельное независимое СМИ сильно снижает стоимость и увеличивает качество интернет журналистики. В поисковый индекс можно добавлять новые индексные поля и обновлять код без нарушения работы сервиса или потери данных (проверено). Сейчас в индексе более 200 млн. объектов от 2,5 млн. социальных аккаунтов; примерно 2,5 млн. новых объектов в сутки; размер индекса в пределах 100 GB; время выполнения запросов - 0,5 сек; к индексу выполняется примерно 1000 запросов в час, в том числе внутре-технические. Ожидаемое время запаздывания между нахождением данных и появлением их в поисковой выдаче - 5-15 мин. (в частности, задержку дает "кэширующий" CDN на поисковой странице).

Проблемы и решения

Более int.MaxValue объектов
Поисковый индекс Lucene ограничен примерно в 2 млрд. объектов. Поэтому для работы с большим числом объектов на помощь приходит MultiSeacher, который еще не портирован в .Net версию. Он позволяет производить поиск по конечному набору поисковых индексов при этом теряется скорость.
Нехватка места или превышение 100ТБ
При использовании MultiSeacher для поиска по нескольким поисковым индексам каждый индекс может быть привязан к своему диску или BlobStorage, что решает проблему с ограничением места.
Дублирование информации
Несмотря на обработку поточных данных от самих социальных сервисов, в них часто происходят сбои и разрывы соединений, что приводит к разного рода ошибкам. Так возможно повторное индексирование постов. Поэтому на поисковом клиенте производится проверка уникальности ссылки на пост в результате. В теории эту задачу можно переложить на фильтр DuplicateFilter
Lucene 4.0 имеет классы встроенной русскоязычной морфологии
Какой бы не была встроенная морфология, я бы рекомендовал использовать более тяжеловесный Lemmatizer, библиотеки которого в архиве zip занимают 100МБ, что говорит о серьезности подхода к составлению словаря.
И куда потом все эти собранные данные?
Например, можно создать сервис в Azure Marketplace и продавать доступ к ним по фиксированной цене. Там же есть интересный платный сервис перевода текстов на многие языки, что позволит в автоматическом режиме перевести всю собранную информацию на любой язык и сделать международный топ русскоязычных социальных новостей.


Метки: , , , , , ,   Категории:Blogs | Csharp | Yandex | microsoft | Code


Кто я?

Программист. Я слежу за блогосферой и знаю, как будет развиваться интернет. Когда у меня есть время я даже прилагаю для этого усилия. Подробнее

Последние комментарии

Не отображать

Topbot at FeedsBurner

Мои Твиты

Twitter июня 18, 16:03
Где искать инопланетян или как зародилась жизнь http://dlvr.it/QXjCQB https://twitter.com/f1ashr/status/1008741906925277184/photo/1

Twitter июня 18, 09:02
Выборы в Америке и будущее запада http://dlvr.it/QXfv8m

Twitter июня 18, 09:02
Памятник крестителю Владимиру http://dlvr.it/QXfttG https://twitter.com/f1ashr/status/1008635961939378177/photo/1

Twitter июня 18, 09:02
YouTube рекламирует свой ВидеоТоп http://dlvr.it/QXftqv

Twitter июня 18, 08:29
Цукенберг встретился с Папой http://dlvr.it/QXflWH https://twitter.com/f1ashr/status/1008627785961635840/photo/1

Twitter июня 18, 08:29
Российские дирижабли покарят мир в 2035 году http://dlvr.it/QXflVs https://twitter.com/f1ashr/status/1008627780785860608/photo/1

Twitter июня 17, 20:42
Азбука киносценариста http://dlvr.it/QXcZL8 https://twitter.com/f1ashr/status/1008449854152929280/photo/1

Twitter июня 14, 11:00
@red_shira @kolomnacity Да это бот какойто , нет никакого запаха

Twitter июня 13, 19:20
Проекционные шоу набирают популярность http://dlvr.it/QXBfLK https://twitter.com/f1ashr/status/1006979531750559745/photo/1

Twitter июня 8, 07:28
Сколько подписчиков должно быть у знаменитости в Твиттере http://dlvr.it/QWbHv9

Twitter мая 29, 07:54
Разбор трилогии Матрицы (The Matrix Trilogy) http://dlvr.it/QVRdCn https://twitter.com/f1ashr/status/1001371287661391873/photo/1

Twitter мая 28, 19:36
Разбор Звездные войны: Последние джедаи http://dlvr.it/QVP48q https://twitter.com/f1ashr/status/1001185546402652161/photo/1

Twitter мая 28, 15:20
По игре престолов небольшая схема http://dlvr.it/QVMyHd https://twitter.com/f1ashr/status/1001121153170096128/photo/1

Twitter мая 28, 13:13
Разбор сериала Кремниевая долина (Silicon Valley) http://dlvr.it/QVMKG9 https://twitter.com/f1ashr/status/1001089026814763008/photo/1

Twitter мая 28, 12:40
читать спутник и погром бесплатно http://dlvr.it/QVM8Wp

Twitter мая 28, 09:59
Пасха в Москве 2 сентября 2017 года http://dlvr.it/QVLNS1 https://twitter.com/f1ashr/status/1001040366471008261/photo/1

Twitter мая 28, 09:27
Канадцы готовят фильм про рептилоидов http://dlvr.it/QVLF4R https://twitter.com/f1ashr/status/1001032318558003200/photo/1

Twitter мая 28, 08:55
Обновлен робот instagram для лайков http://dlvr.it/QVL4kN

Twitter мая 28, 08:55
Инновации в Бангладеше http://dlvr.it/QVL4hr

Twitter мая 28, 08:23
Рейтинг каналов и ботов Телеграмм http://dlvr.it/QVKxrQ https://twitter.com/f1ashr/status/1001016045774159878/photo/1

Мой твиттер

Копирайт

Все мысли, высказанные в блоге, являются моим мнением и за это мнение меня никто не забанит! Кроме того, никто не имеет право копировать материалы блога без использования ctrl+C/V!

© Copyright 2008