some image

Parallel Curl, параллельная загрузка и обработка страниц в PHP

Обо всем

Как увеличить производительность тяжелого парсера контента, обрабатывающего несколько запросов в секунду? При такой производительности каждый такт на счету, и очень не хочется тратить время на простой в ожидании загрузки контента, в то время как можно было бы поработать с уже загрузившимся.

Хочу представить вашему вниманию малоизвестную, но очень полезную библиотеку parallelcurl, являющуюся надстройкой над мультипотоковым curl.

https://github.com/petewarden/ParallelCurl

В чем смысл: библиотека предоставляет простой интерфейс callback-функций для одновременной загрузки и обработки веб контента.

Подключаем библиотеку, создаем объект класса:

$parallelcurl = new ParallelCurl(10);

 

Аргумент — число потоков для одновременной загрузки.

Весь механизм работы очень похож на js callback’и.

Стартуем загрузку, можно в цикле:

$parallelcurl->startRequest('http://example.com', 'on_request_done', array('something'));

 

Можно стартовать хоть 1000 запросов одновременно. Они все складываются в очередь. Выполняется, скажем, 10 одновременно. Как только 1 выполнился, сразу же из очереди берется следующий, а для выполнившегося потока стартует callback функция, переданная параметром:

on_request_done($content, 'http://example.com', $ch, array('something));

 

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

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

$parallelcurl->finishAllRequests();

 

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

Безусловно, многое нужно допилить под свои нужды, но как начальный каркас — очень рекомендую.

php preg_match длинные строки

Метки: Работа

Если при работе с длинной строкой регулярное выражение возвращает false вместо результата — добавьте

ini_set('pcre.backtrack_limit', '5000000');

simple_html_dom утечка памяти

Метки: , , Работа

Если при работе с simple_html_dom вы получаете следующую ошибку: Fatal error: Allowed memory size of bytes exhausted, значит, скорее всего, вы забыли после работы с отдельной страницей выполнить $html->clear();

Пример:

$html = file_get_html($url);
foreach($html->find('div[class=posts]') as $element){
    foreach($element->find('img') as $el){
      // do smth
    }
}
$html->clear();
unset($html);

jQuery ready bug в Internet Explorer 9

Работа

Возможно вы пришли сюда, т.к. ready событие в вашем jQuery запускается раньше полной загрузки станицы, раньше построения DOM дерева.
Так вот хочу поделиться очень странным и несвойственным для jQuery багом:

http://bugs.jquery.com/ticket/12282

Суть в том, что в jQuery 1.8.0 нельзя пользоваться ready событием, вообще. Т.к. в IE9 оно срабатывает как попало. Вы можете не обнаружить элементы в DOM (как это было в моем случае), код может не прогрузиться. Это очень опасно и ведет к совершенно неисправимым багам.

Решение — срочно обновить jQuery с сайта, т.к. баг там уже поправили.

Регулярные выражения — исключить слово

Обо всем

Для того чтобы исключить слово из регулярного выражения, или набор слов, или даже целое предложение, используем конструкцию:
((?!word1|word2).)*

Пример: получить все последовательности символов такие, которые начинаются с begin, заканчиваются end и не содержат ни слова word1, ни слова word2:
begin((?!word1|word2).)*end

MSSQL, TSQL, динамический SQL, динамическое формирование запросов

Метки: , , Работа

Задача: в stored procedure динамически формировать SQL для получения данных. Динамическими должны быть все части: названия таблиц, количество условий, названия колонок. В идеале нужно просто составлять строку, а потом выполнять запрос из нее, при этом вообще хорошо, если удастся обеспечить параметризацию.

Решение: использование sp_executesql
Сразу на примере, ситуация.

Пусть у нас есть несколько сущностей в системе. Есть пользователи, есть записи в блоге, есть статьи.
Все они хранятся в трех разных таблицах account, blog_post, content_page (primary keys: account_id, blog_post_id, content_page_id).
В каждой из этих таблиц есть, соответственно, поля acc_created, bp_created, cp_created. И представьте теперь, что в одной из sp нам необходимо за заданному id и типу сущности получить creation date. Можно написать 3 запроса — легко, ну а если сущностей, скажем, 50? Тогда простое решение становится уж очень некрасивым.

На помощь приходит динамический sql.
Attention. Я знаю про перфоманс и про все остальное, я знаю что 50 отдельных запросов будут быстрее. Речь не об этом, а о ситуации, когда принято решение, что делать нужно так.

set @sql =     N'select @created =  ' + @pspAbbrev + '_created from ' + @pspFull + ' where ' + @pspFull + '_ID = @id;';

set @params =  N'@id     integer, ' +
	       N'@created   datetime OUTPUT';
			   
exec sp_executesql @sql, @params, @id = @id, @created = @created OUTPUT

Прежде всего, нам необходимо определить @pspAbbrev (например, ‘usr’) и @pspFull(например, ‘user’).
После чего сформировать @sql, определить input-output параметры, вызвать sp_executesql и далее пользоваться полученным значением из переменной @created.

Сортировка dropdown(select) списка на jQuery

Метки: , Работа

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

        //Sorting by text value asc
        jQuery('.class').each( function(){
            jQuery(this).html( jQuery(this).find('option').sort(function(a, b) {
                return a.text == b.text ? 0 : a.text < b.text ? -1 : 1
         }) );
        });

ie8 window.focus не работает + работа с окнами, содержащими PDF документ

Обо всем

Сегодня наткнулся на достаточно злой баг Internet Explorer 8 ранних билдов.

Суть в том что конструкция


if( windowObjects && windowObject.focus ){

windowObject.focus();

}

может ни с того ни с сего падать с ошибкой Member not found при попытке установить фокус на окне с открытым PDF документом. Подробнее тут: http://bytes.com/topic/javascript/answers/558302-member-not-found-error-when-opening-pdfs-new-window

Вообще поведение IE8 до фикса http://support.microsoft.com/kb/979954 очень и очень странное при работе с PDF в отдельном окне. Если вы загружаете PDF документ, окно с ним тут же меняет свойства. Объект все еще есть, но в то же время его нельзя закрыть из родительского, в него нельзя запостить форму, можно только изменить document.location, тем самым закрыв PDF и дальше работая с окошком как с обычным.

Таким образом, если хотим вернуть контроль надо окном — просто меняем document.location на заготовленную пустую страницу. По-моему это самый правильный вариант. Но есть и много других решений, например — через mod_rewrite не допускать прямого открытия pdf, а вместо этого делать редирект на pdf.html?sorce=file.pdf. Страничка pdf.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
<html>
  <head>
    <meta name="generator" content=
    "HTML Tidy for Windows (vers 25 March 2009), see www.w3.org">
    <title></title>
  </head>
  <frameset>
    <frame src="test.pdf">
  </frameset>
</html>

Решение, конечно, очень некрасивое, зато 100% работоспособное, может кому пригодится

Я люблю javascript

Метки: Работа

Уже не раз писал заметки о JS, но еще ни разу не говорил о своем отношении к нему. Так вот, я считаю, что если продолжительное время писать на нем и использовать как основной язык — прежним ты уже не станешь. Я действительно считаю, что этот язык не просто плох, а очень плох. Потому что я не знаю второго такого языка, который бы позволял накидать столько логических ошибок на 1 строчку кода. Такого количества неявных вещей, упрощений и тп нет ни в одном другом языке. И меня бы мало это волновало, если бы это был какой-нибудь Lisp или Haskell. Но это язык, на котором приходится писать половину кода браузерного приложения, существующую на стороне клиента.

Я считаю что JS это реально самая большая проблема веба сейчас. Количество кода, не поддающегося рефакторингу, просто зашкаливает даже в самом крутом, на первый взгляд, веб проекте. Загляните в исходники вашего любимого портала. Удивитесь. Да это же просто вакханалия в половине случаев.

Я хочу рассказать в этой заметке о самом жестоком виде логических ошибок, который мне приходилось встречать. Это перемешивание глобальных и локальных переменных. А все идет корнями в дебильный синтаксис javascript. Почему если я пишу

function alertURL(){
 alert(url);
}

я не могу предугадать то что получу? Почему это вообще никаким образом не зависит от меня? Это зависит от того до jQuery я вызвал мою функцию или после, от того какие вообще пакеты я включал в head, от того какой сегодня день, как зовут маму Брендана Айка, но только не от меня.

Правильно! Потому что я не добавил волшебное слово var! Этот целебный эликсир  предотвращающий ошибки, которые вообще не поддаются отладке. А теперь вопрос: накой черт дергать все из глобальной области видимости? Как часто вам внутри функции нужны глобальные переменные? Что, действительно чаще, чем локальные? Почему Zend догадались сделать слово global, a в JS его нет?

Так вот представьте. Мне дают 2 библиотеки, большущие библиотеки в несколько тысяч строк кода каждая, где нет не одного слова var, нет какого-то подобия namespaces, вообще ничего, отделяющего область видимости, нет. Ну не думал тот программист, который писал каждую из них, что их понадобится использовать вместе, а может он вообще не знал что такое var, лабал да лабал себе потихоньку. И они начинают падать, неистово падать из-за смешения глобальных переменных. Само даже допущение такой ситуации — это ошибка в дизайне языка.

Казалось бы — ну введите вы namespaces, и все, и нет проблем, хотя бы на уровне смешения пакетов. Чтоб я мог написать просто в начале каждого пакета ‘use namespace popa’ и все, и решить все проблемы. Но нет, все в очередной раз пользуются гипер костылем, эмулирующим namespaces в js.

И nobody cares, все лабают быдлокод не поддающийся пониманию, и все счастливы.

Месяц езжу с Webasto

Обо всем

В начале декабря поставил webasto. Месяц эксплуатирую, доволен как слон.

Поставили перед печкой, как объяснили — специально (разницы там перед-после реально нет, шланги просто местами меняются грубо говоря), т.к. считается, что в печке часто, особенно в морозы, в верхнее части радиатора после стоянки может образоваться воздушная пробка и вебасте будет плохо если она ее прогонит. Не знаю правда — нет, я доволен и так.

Но рассказать и поделиться все равно есть чем.

Сразу после установки выезжаю — тачка еле греется, вообще фиг прогреешь до рабочих оборотов, ну думаю ладно, за бортом -35, лишние 2 литра антифриза в системе — привыкну. Потом понимаю что даже когда прогрелась — из печки дует холодом. Ну т.е. не просто там чуть похуже стало а вообще зябко. После установки webasto печка не греет. Ну, думаю, приплыли. Заезжаю на горку, газую, что-то булькает, эффекта нет. А машина позарез нужна была, в морозы пешком двигаться по делам удовольствие то еще. Вообщем ездил я пару дней с затянутыми окнами и холодом из печки.

Приезжаю к установщикам. Справедливости ради надо сказать, что меня постоянно спрашивали про пробку, что надо выгонять и просто все завоздушено. Но я говорю надо — выгоняйте, попробовали еще погазовать — ноль реакции. Ну делать нечего — еще раз проверили всю новую трассу шлангов на перегибы и вывели помпу вебасты на кнопку в салон.

Той же ночью поехал в академ, и совершилось. Штатная помпа на скорости под сотню + вебастовская разогнали антифриз в системе и, как и ожидалось, в машине начался дикий ташкент. Пробка вышла. В машине ташкент и без помпы, как и раньше, а с бонусной халявной кнопкой и вообще в пробке жарит как на скорости 40 км/ч.

Вывод: причин плохой работы печки после установки webasto две и только две: перегибы шлангов и воздушные пробки. Все рассказы установщиков про ухудшившуюся циркуляцию и «так и должно быть» — бред и нежелание работать, не больше.

Отдельно хочется сказать о том, куда ставить webasto.

Даже официалы норовят воткнуть webasto в бампер, не говоря уже о серых установщиках. Правильно — работы меньше всего + вот он готовенький клиент после первой аварии. Это, я считаю, полная халтура. Сразу сказал что в бампер я себе ставить не дам. Всегда можно найти место, если мозгами пошевелить. Мне вот расширительный бочек перенесли и поставили поближе к движку. Если места нет сверху, то всегда найдется место снизу, при этом выше ватерлинии. Короче надо настаивать чтоб искали, а не халтурили.

Теперь о приятном, т.е. об ощущениях после установки. Само собой, мне теперь строго без разницы на температуру за бортом. В морозы webasto завожу за 40 минут до выхода с брелка. Далее за 10 минут до выхода завожу движок чтоб в салоне стало тепло. Садишься в машину — красота. На улке -30 а тут уже теплынь, шапку снять можно, руль холодом не обжигает. Ну и выключаю отопитель только когда машина полностью прогреется уже с работающей на полную печкой, а это очень быстро. Через 10 минут езды климат, настроенный на -25, уже прикрывает печку, что как-бы говорит о быстроте прогрева.

Цена вопроса: 40т.р. Оно того стоит:)