суббота, 30 октября 2010 г.

php Cacher

Опубликовал набор классов для кеширования:
http://github.com/valmat/Cacher
Опубликовано под лицензией
GPL v.3 http://www.gnu.org/licenses/gpl.txt
Т.е. свободно для использования и изменения. Разумеется, приветствуются любые исправления и дополнения.

Назначение


Мне нужен был очень простой, но в то же время мощный кеширующий модуль с понятной логикой. При этом он должен быть пригодным для работы на односерверном, но в тоже время нагруженном проекте.
Так же немаловажным моментом является возможность прозрачно менять стратегию кеширования в зависимости от текущего уровня нагрузки проекта.
Т.е.решение должно удовлетворять требованию изменять стратегию кеширования по мере роста нагрузки на проект и по мере изменения аппаратных возможностей (речь об ОЗУ) сервера.

Архитектура


Основными логическими единицами являются:
  • Cacher - фронтен к кеширующим классам.
  • Cacher_Backend - собственно сами кеширующие классы.
  • Слоты - кеширование и доступ к кешу осуществляется через слоты.
  • Теги - для упрощения управления кешем, и, главным образом, для переуеширования.
  • Типы кеширования - для прозрачного изменения стратегии кеширования. Т.е. конкретный кеширующий бекенд подключается только через слот (или тег), которые в свою очередь оперируют типами. Таким образом для изменения тратегии кеширования нужно всего лишь поменять привязку типов к бекендам.


Использование


class Cacher


Требует наличия классов унаследованных от Cacher_Backend - семейство классов, реализующих бэкэнд для класса Cacher
Все операции с кешем осуществляется на низшем уровне через тот или иной бекенд
Бэкэндом может быть файловая система, shared memory, memcache, Sqlite и другие системы кеширования
Для работы с классом представляются слоты и теги. Слоты реализованы в виде набора дружественных функций и неявно зашиты в интерфейс текущего класса

Пример использования:

define AnyObj // может быть класс, массив или дрогой объект. На основании эого объекта слот функция вычислит ключь и, возможно другиие параметры (бэкэнд и время жизни).
Cacher::Slot('AniObj',AniObj); // Инициализируем слот кеширования. Первый параметр для определения имени слота, второй - наш объект

Получаем данные
if (false === ($CacheData = Cacher::get())) { // Если данные из кеша получить не удалось...
$CacheData = GetFromAnyExternal(); // Получаем данные из внешнего хранилища
Cacher::addTag(Cacher::newTag('AniTagData',AniTagDataObj)); // Создаем и сразуже добавляем новый тег к слоту перед сохрананеием в кеш
$tag2 = Cacher::newTag('AniTagData2',AniTagDataObj1) // Создаем новый тег
Cacher::addTag($tag2); // добавляем новый тег к слоту перед сохрананеием в кеш
Cacher::set($CacheData); // Кешируем данные
}
...
...
Если затем нужно сбросить какой нибудь тег, то нужно будет сделать так:
Cacher::newTag('AniTagData2',AniTagDataObj1)->clear() // Очищаем кеш тега

четверг, 28 октября 2010 г.

define vs const в php

Как известно, при разработке крупных веб приложений помимо архитектуры постоянно приходится задумываться так же и о производительности. Этим постом я хотел бы открыть серию постов по тестированию php на производительность.
Речь пойдет о сравнении способа хранения констант в приложении на php
А именно сравниваются два подхода:
define('CONST1', 'val11' );
define('CONST2', 'val12' );
define('CONST2', 'val13' );

и
class Consts{
 const CONST1 = 'val1';
 const CONST2 = 'val2';
 const CONST3 = 'val3';
}
 


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

Тест 1. Инициализация


Инициализируем 100 констант при помощи define
define('CACHER_TYPE_1', 'b60861c4492f88589429aab0c67abdd4' );
/*
      ...
   */
define('CACHER_TYPE_100', 'a66aedeafbc3f1e9fcbaa6a9e8060739' );


memory_start: 114.7578125Кб
time: 0.442981719971 ms
memory_finish: 120.8515625Кб
memory_diff: 6.09375Кб

$ab -n 1000 http://test/test/mem_class.php

Requests per second: 714.52 [#/sec] (mean)
Time per request: 1.400 [ms] (mean)


Теперь инициализируем через константы класса

class SlotType{
 const TYPE_CACHER_1 = 'b60861c4492f88589429aab0c67abdd4';
/*
      ...
   */
 const TYPE_CACHER_100 = 'a66aedeafbc3f1e9fcbaa6a9e8060739';
}

memory_start: 114.7578125Кб
time: 0.0340938568115 ms
memory_finish: 114.9921875Кб
memory_diff: 0.234375Кб

$ab -n 1000 http://test/test/mem_class.php

Requests per second: 818.27 [#/sec] (mean)
Time per request: 1.222 [ms] (mean)

Тест 2. Чтение


Считываем все константы определенные через define:
$var = CACHER_TYPE_1 . CACHER_TYPE_2 . /*...*/ . CACHER_TYPE_100;
time: 0.4 ms
memory_diff: 9.3Кб
ab -n1000:
Requests per second: 488.63 [#/sec] (mean)
Time per request: 2.047 [ms] (mean)

Считываем
$var = SlotType::TYPE_CACHER_1 . SlotType::TYPE_CACHER_2 . /*...*/ . SlotType::TYPE_CACHER_100;
time: 0.12 ms
memory_diff: 3.5Кб
ab -n1000:
Requests per second: 609.62 [#/sec] (mean)
Time per request: 1.640 [ms] (mean)

Вывод


Надо сказать, результат меня несколько удивил. Я ожидал, что по крайней мере скорость обработки с define будет выше. Оказывается Использование варианта
class Consts{
 const CONST1 = 'val1';
 const CONST2 = 'val2';
 const CONST3 = 'val3';
}
 

не только удобней, но и эффективней как по скорости исполнения, так и по расходу памяти.

среда, 27 октября 2010 г.

php Counter

Выложил на github.com свой класс Counter
В основном сделал это что бы потестить сам GitHub
Адрес страницы на гитхабе: http://github.com/valmat/MC_Counter
Как использовать:

class Counter
Это образец реализации счетчика на memcache
Можно построить другие реализации на общем интерфейсе
Сохранение результатов применения значений счетчика осуществляется по заданному числу.
Можно реализовать сохранение по заданному интервалу времени
*
Конструктор принимает три аргумента: ключ, имя слота, и идентификатор для инициализации слота.
Для чего это сделано: инримент счетчика должен быть очень быстрой операцией.
Не целесообразно тратить время и сстемные ресурсы на создание объетов, которые е будут использовны.
Поэтому передается только имя слот класа, который создается только в случае необходимости.
К таким случаям относитсяя обмен данными между локальным и постоянным хранилищем счтчика.
Слоты необхоимы, так как Counter не может знать о способе хранения данных в постоянном хранилище и путях доступа к ним.
Для предотвращения состаяния гонки, необходим механизм блокировок.
При наличии блокировки, процессы не получившие эксклюзивные права на получение данных будут писать во временное хранилище, а процесс,
установивший блокировку по окончанию своей работы инкриментирует счетчик данными из временного хранилища
*
При сбросе данных в постоянное хранилище по условию достежения кратности значения счетчика ($this->Val%$this->upd_delim),
блокировка не требуется, т.к. в этом случае (при достаточно большом значении $this->upd_delim) в текущий момент времени только один процесс
приходит к неоходимости сброса данных

Пример использования:
*
$cnt = new Counter('anykey', 'AnySlot',15);
echo $cnt->increment();
echo $cnt->get();
echo $cnt->set(11);

понедельник, 18 октября 2010 г.

Восстановление удаленных и поврежденных данных в Linux

Когда то давно, лет десять назад случалось мне отформатировать раздел жесткого диска под виндой. На диске была важная и нужная информация, поэтому встала задача данные восстановить. Помню путем продолжительного гугления были найдены несколько замечательных программ и кряков к ним. И данные были хоть и частично, но восстановлены. Назывались эти программы, вроде бы easy recovery, recover4all и какая то еще.
И вот на днях мне принесли жесткий диск с разделом, на который была поставлена винда по верх старой и вся нужная владельцу винта информация была благополучно потерта.
Поскольку сейчас винды у меня нет, то была выгуглена чудесная Линуксовая утилита под названием foremost
Вот, все таки, за что я люблю Линукс, это за лаконичность и изящность решений (ну и за логичность архитектуры, конечно).
Для восстановления данных потребовалась всего одна строчка в терминале:
#foremost -t jpg -o /dev/sdb1 -i ~/bak

Теперь по порядку, что к чему.

  • # - запускаем от root'а что бы не было проблем с чтением

  • -t - восстанавливаемых файлов. Можно написать -t all что бы восстановить файлы всех типов, либо одно из значений из списка: avi, bmp, dll, doc, exe, gif, htm, jar, jpg, mbd, mov, mpg, pdf, png, ppt, rar, rif, sdw, sx, sxc, sxi, sxw, vis, wav, wmv, xls, zip

  • -o /dev/sdb1 - здесь указываем раздел, который нужно сканировать. Поддерживаются разные файловые системы. Тот диск, который приносили мне был с ntfs.

  • -i ~/bak - куда складывать результат


Если запустить с опцией -t all, то будут созданы разные каталоги под каждый тип файлов, что само по себе очень удобно.
Я особо не вглядывался что он там на восстанавливал, но при беглом обзоре можно было заключить, что в своей массе почти все файлы были восстановлены корректно. Было несколько битых фотографий, но так ведь раздел был не пустой. Его не просто отформатировали но и успели по писать на него.
PS есть в репозитарии Ubuntu.
Т.е. установить можно так:
sudo apt-get install foremost

понедельник, 20 сентября 2010 г.

Принтер Canon LBP 3200 в Ubuntu

К великому моему сожалению, прентер Canon LBP 3200 не стартанул в Ubuntu 9.10 из коробки.
Драйверов для него в дефолтной комплектации нет. Просто скачать и установить драйвер тоже сразу не получилось. Поэтому была предпринята попытка поиска ответа в интернете.
Мне удалось найти две адекватные ссылки:
http://forum.ubuntu.ru/index.php?topic=87445.0
http://help.ubuntu.ru/wiki/%D0%BF%D1%80%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%8B_canon_capt
Собственно, моя инструкция полностью написана руководствуясь указанными ссылками. К сожалению обе они по отдельности результата не дали.
Все что написано ниже, у меня дало положительный результат.
открываем териминал:
sudo su
удаляем
libcupsys2 и libstdc++5:
/usr/sbin/ccpdadmin -x LBP3200
sudo /usr/sbin/lpadmin -x LBP320
sudo dpkg -P cndrvcups-capt
sudo dpkg -P cndrvcups-common

Далее скачиваем и устанавливаем libcupsys2 и libstdc++5:

libcupsys2:https://launchpad.net/ubuntu/karmic/+package/libcupsys2
libstdc++5: http://packages.ubuntu.com/jaunty/libstdc++5

далее надо скачать драйвер принтера:
sudo su
cd /tmp
wget http://files.canon-europe.com/files/soft31118/software/CAPTDRV180.tar.gz
tar -xzf CAPTDRV180.tar.gz
cd ./CANON_UK/Driver/Debian
dpkg -i cndrvcups-common_1.80-1_i386.deb
dpkg -i cndrvcups-capt_1.80-1_i386.deb

Далее надо отредактировать файл /etc/ccpd.conf

sudo gedit /etc/ccpd.conf

Меняем строки
 #<Printer  LBP3200>
#DevicePath  /dev/usb/lp0
#</Printer>
на
<Printer  LBP3200>
DevicePath  /dev/usblp0
</Printer>


Перегружаем сервер печати:

sudo /etc/init.d/cups restart
sudo /etc/init.d/ccpd stop && sudo /etc/init.d/ccpd start

Регистрируем принтер в ccpd:

sudo /usr/sbin/ccpdadmin -p LBP3200 -o /dev/usblp0
sudo /etc/init.d/ccpd stop && sudo /etc/init.d/ccpd start

окно статуса принтера можно открыть так:

captstatusui -P LBP3200

UPDATE

Поставил Linux Mint 16 64bit (~ Ubuntu 13.10) в нем моя инструкция, конечно не подходит.
Поэтому пришлось проходить этот квест заново.
Благо, Canon выпустил новые драйвера что немного облегчает нам задачу.
Итак. Имеем названную систему.
  1.  Скачиваем и устанавливаем deb пакеты с драйверами от производителя
  2. Подключаем и включаем принтер. Смотрим usb порт на который он подключился (у меня /dev/usb/lp1) смотреть нужно тут: /dev/usb
  3. Правим /etc/ccpd.conf
    sudo gedit /etc/ccpd.conf

    Меняем строки
     #<Printer  LBP3200>
    #DevicePath  /dev/usb/lp0
    #</Printer>
    на
    <Printer  LBP3200>
    DevicePath  /dev/usb/lp1
    </Printer>
     
  4. Добавляем принтер (В архиве с драйверами есть README можно посмотреть откуда взялись эти строки и на что их менять, если принтер отличается):
    sudo /usr/sbin/lpadmin -p LBP3200 -P /usr/share/cups/model/CNCUPSLBP3200CAPTK.ppd -v ccp://localhost:59687 -E
    sudo /usr/sbin/lpadmin -p LBP3200 -m CNCUPSLBP3200CAPTK.ppd -v ccp://localhost:59787 -E
    sudo /usr/sbin/ccpdadmin -p LBP3200 -o /dev/usb/lp1
     
  5. Перегружаем сервер печати (см выше)
  6.  Печатаем пробную страницу.
Все!

PS
Что еще обнаружилось:
Нужно добавить /etc/init.d/ccpd в автозагрузку
update-rc.d ccpd defaults 20;
Причем, даже после добавления в автозагрузку нужно перезагружать ccpd. Поэтому еще нужно в /etc/rc.local перед exit 0 добавить
/etc/init.d/ccpd restart

Кроме того выяснилось, что в зависимости от того был ли подключен включенный принтер до загрузки системы или нет принтеру назначаются разные файлы устройства.
Если включен после загрузки ОС, то /dev/usb/lp1, если до, то /dev/usb/lp0

Что бы преодолеть эту неприятность я изобрел следующий костыль:
В /etc/init.d/ccpd в начало секций ccpd_start () и ccpd_stop () (только ccpd_start не достаточно) добавил следующий блок:

###############################
# Fix гуляние портов
if [ -e /dev/usb/lp0 ]; then
    echo "Exist /dev/usb/lp0"
    if [ ! -e /dev/usb/lp1 ]; then
        echo "NOT exist /dev/usb/lp1"
        echo "ln -s /dev/usb/lp0 /dev/usb/lp1"
        ln -s /dev/usb/lp0 /dev/usb/lp1
    fi
fi
###############################

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



среда, 8 сентября 2010 г.

Установка LaTeX в Linux Ubuntu

Как утверждает Викепидия с 2006-го года пакет teTeX более не поддерживается, а вместо него поддерживается TeX Live. Его и ставим.
Инструкцию по установке подглядел здесь: http://linuxandfriends.com/2009/10/06/install-latex-in-ubuntu-linux/
Ставим:

$sudo su
#apt-get install texlive texlive-full texlive-fonts-recommended latex-beamer texlive-pictures texlive-latex-extra


texlive-full попросил достаточно много места на диске (около 700 мб). Место у меня было, поэтому я его все же установил, но, при необходимости, можно не устанавливать texlive-full, а установить только нужные пакеты, которые входят в texlive-full.

В качестве IDE были на пробу установлены LyX, gedit-latex-plugin и TeXmaker
Про них пока ничего сказать не могу, т.к. еще не юзал.

PS это была моя первая установка LaTex'а в Линуксе, до этого я устанавливал MiKTeX под виндой. Как всегда, небо и земля. Вот уж что действительно танцы с бубном, так это ставить МикТех под винду.

воскресенье, 29 августа 2010 г.

Рассказ о том, как я жесткий диск на ноуте менял

Был у меня, в общем то, достаточный для работы жесткий диск объемом 250 Га, но не жилось мне спокойно, и захотелось мне поставить жесткий диск на 500 Га.
Сказано - сделано. Винт купил и поставил. Надо переносить систему. Переустановка ОС - это не наш путь, разумеется.
Далее будет описано что я делал (и, что не нужно было делать), а в конце как все таки у меня получилось через одно место.
Итак на моем старом винте было три раздела: ntfs, ext4 и swap.
Новый жесткий диск я воткнул в ноутбук, а старый подключил по usb через переноску.

Самое логичное, было бы сделать

dd if=/dev/sdb of=/dev/sda bs=4096 conv=sync,noerror

Но этот способ не сработал.
Разделы создались, но ФС у них не определилась. Из за чего пришлось бы их переформатировать.
Собственно того же результата можно было добиться просто скопировав загрузчик и таблицу разделов:

dd if=/dev/sdb of=/dev/sda bs=512 count=1

После чего я по очереди убил каждый раздел на новом жестком диске и gparted'ом перенес их со старого жесткого диска. В результате, конечно же, загрузочный сектор и таблица разделов у меня потерлись.
Всевозможные попытки осуществить восстановление grub'а, осуществляемые при помощи гугления результата не дали.
Что в итоге я сделал.
Раздел с ntfs оставил как есть (т.е. полностью клонированным со старого жесткого диска), на раздел ext4, на котором у меня стоял Linux, я заново установил дистрибутив. Только устанавливать нужно обязательно тот дистрибутив, который стоял до этого, потому что иначе может не получиться.
Таким образом у меня пересоздалась таблица разделов и загрузочный сектор (вообще таблица разделов конечно была).
Затем я все полностью стер на Линуксовом разделе и скопировал при помощи утилиты rsync все со старого линуксового раздела. Все это я делал загрузившись с LiveCD.
rsync в отличии cp (даже cp -a) нормально копирует скрытые файлы, т.е. те, которые начинаются с ".". Обе команды rsync -a и cp -a могут копировать атрибуты файлов и права доступа.
Выглядит команда так:

rsync -a /.../.../ /.../.../

Закрывающий слеш существенен, поскольку без него получится не совсем то что нужно.
Что бы система начала загружаться нужно еще в файлах
/boot/grub/grub.cfg и /etc/fstab
поменять uuid разделов на новые.
Узнать их можно командой
$blkid

Ну и напоследок, поскольку у меня появилось теперь дофига свободного места, я создал 250 гиговый раздел и примонтировал его к /home
Для этого я все тем же rsync'ом перенес все файлы (сидя на LiveCD) на отведенный для этого раздел и прописал в его fstab:

# /home
/dev/sda4 /home ext4 defaults,noatime,errors=remount-ro 0 2

понедельник, 16 августа 2010 г.

Решил я обгрейдить свой ноутбук - увеличить память, да выяснилось, что на HP ProBook 4710s доступ к внутренностям, не как у людей, а совершенно через другое место. По сему выкладываю здесь инструкцию по открытию этого ноутбука.
Шаг 1:
снимаем аккумулятор и откручиваем 5 болтов под ним:



Шаг 2:
Затем откручиваем 2 болта на задней части ноутбука





Складываем все шурупы в одном месте, что бы не потерять

Шаг 3:
Толчком от себя снимаем заднюю часть верхней панели (ту, на которой динамики)


Эту часть можно полностью удалить, что бы не мешала

Шаг 4:
Далее у нас открывается доступ к болтам крепления клавиатуры


Их нужно открутить и так же толчком от себя (без лишних усилий, это все же не совковая техника можно и сломать) отделяем клавиатуру

Вуаля у нас появился доступ к оперативной памяти

А дальше я не полез.
Собирается все, естественно, в обратном порядке.

суббота, 24 июля 2010 г.

tmpfs Операции с файловой системой в виртуальной памяти

tmpfs Операции с файловой системой в виртуальной памяти:

Для примонтирования при старте вносим в /etc/fstab:
tmpfs /tmp tmpfs size=500M,nr_inodes=1m,nosuid 0 0
tmpfs /var/lib/php5 tmpfs size=200M,nr_inodes=1m,nosuid 0 0
первая строчка размещает в памяти /tmp, вторая папку хранения сессий

Для сосздания папки для сессий без рестарта системы нужно запустить (или набрать в терминале) следующую последовательность строк:
mkdir /tmp/ses
/etc/init.d/nginx stop
mv /var/lib/php5/* /tmp/ses
mount tmpfs /var/lib/php5 -t tmpfs -o size=200M,nr_inodes=1m,nosuid
mv /tmp/ses/* /var/lib/php5
/etc/init.d/nginx start
rm -r /tmp/ses

предварительно лучше отредактировать fstab

Вот более сложный вариант, когда данные сессий хранятся в tmp:

mkdir /dev/shm/ses
/etc/init.d/nginx stop
/etc/init.d/php5-spawn stop
/etc/init.d/mysql stop
mv /tmp/* /dev/shm/ses
mount tmpfs /tmp -t tmpfs -o size=1g,nr_inodes=1m,nosuid
mount tmpfs /var/lib/php5 -t tmpfs -o size=200M,nr_inodes=1m,nosuid
mv /dev/shm/ses/* /tmp
/etc/init.d/mysql start
/etc/init.d/php5-spawn start
/etc/init.d/nginx start
rm -r /dev/shm/ses
du -hsx /tmp

Запуск memcache через unix.socket

Запуск memcache через unix.socket
В файл /etc/memcached.conf добавляем строчки

#-s unix socket path to listen on (disables network support)
-s /tmp/memcached.socket
#-a access mask for unix socket, in octal (default 0700)
-a 0777

Последняя нужна, что бы пользователь от которого работает вебсервер (у меня www-data) смог прочитать сокет
Перезапускае деамон мемкеша /etc/init.d/memcached restart

Подключаемся к Memcache из php скрипта:
$memcache = new Memcache;
$memcache->connect('unix:///tmp/memcached.socket',0);
Все теперь memcache не должен уступать по производительности tmpfs или /dev/shm

Правда в этом случае перестают работать сессии в мемкешед. Т.е. следующая конструкцияр работать не будет:

$session_save_path = 'localhost:11211';
$session_save_path = 'localhost:11211,unix:///tmp/memcached.socket:0';

ini_set('session.save_handler', 'memcache');
ini_set('session.save_path', $session_save_path);

Но для сессиий лучше всего все таки tmpfs