Ne v kontakte Asocial programmer's blog

Собираем домашний сервер. Часть 2, софтовая.

Feature image

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

Операционная система

Специфика домашних серверов такова, что это машина, которая делает абсолютно все: сетевая файлопомойка, DNS, DHCP, маршрутизация, торренты, GIT и еще бог весть что, вопреки всем рекомендациям экспертов по безопасности. Я тоже поддался на это искушение, успокоив себя тем, что это во-первых, удобно, во-вторых, дешево и, в-третьих, наружу будет торчать VPN-сервер и больше ничего. Зато изнутри будет виден весь зоопарк сервисов, необходимых для комфортной жизни гика.

Таким образом, выбор ОС делался из соображений “изкоробочной” поддержки всех необходимых сервисов и удобного интерфейса к ним. Безусловно, настроить все это самому можно без особых проблем, но оно мне надо? После того, как настройка софта из фана превращается в производственную необходимость, удобство и скорость управления резко возрастают в цене.

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

Zentyal

Поиск не был долгим. Увидев трехстраничный список роутерных дистрибутивов в википедии, я просто выбрал те названия, которые были мне хоть чуть-чуть знакомы и сосредоточился на них. И очень быстро выяснил, что из них только Zentyal (бывший eBox) удовлетворяет последнему пожеланию, а именно, в его основу легла Ubuntu 10.04, хорошо мне знакомая.

Разработчиики позиционируют свой дистрибутив как Small Business Server, но балогдаря развитой модульной структуре, он прекрасно подходит и для домашнего сервера. Сам по себе дистрибутив является не только бесплантым, но еще и лицензирован под GPLv2. Основной козырь этого дистрибутива - хорошо продуманный веб-интерфейс, покрывающий 95% лично моих нужд. Из коробки в нем нет разве что торрентов, но это не проблема.

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

Немного об установке и настройке

Ставится Zentyal так же как и Ubuntu Server и единственной проблемой оказалось, что не очень понятно, как его инсталлировать в headless режиме. Мне  оказалось проще подключить монитор, чем устраивать сложности с ssh, но не для всех это будет хорошим выходом.

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

Наиболее тонким моментом был процесс плавного переключения домашней сети со старого роутера на новый сервер. Напомню, что до сего момента, в центре нашей домашней сети стоял роутер ASUS WL-520GU (очень хорошая и надежная машинка, кстати) с прошивкой от Олега, он раздавал все остальным интернет через NAT и локальные IP из подсети 192.168.171.0/24. В конце лета к нам к квартиру пришел интернет от РосТелекома и, как более шустрый, вытеснил прежнего провайдера (HomeNet) из WAN порта, оставив его болтаться без дела.

Поэтому на период первичной настройки подключил HomeNet к новому серверу в качестве WAN и настроил LAN на тот же диапазон, что и “боевую” сеть, то есть на 192.168.171.0/24. Процесс настройки сети меня приятно поразил своей простотой и продуманностью — задание параметров сетевых интерфейсов и настройка одного из них как WAN прошла как по нотам. В итоге у меня возникло две параллельных сети с очень похожей топологией, и в процессе настройки я переключал свой ноутбук с WiFi соединения на Ethernet и обратно, чтобы попасть в общую и новую сети соответственно. Сконфигурировав сетевые подключения, я прогулялся по квартире, собрав MAC-адреса всех имеющихся устройств и внес их в настройки DHCP-сервера, не забыв и роутер (это важно, да).

Наконец, я произвел слияние новой и старой сетей при помощи вот таких нехитрых шагов:

  1. Перевел роутер в режим точки доступа (т. е. отключил NAT и все, что с ним связано). В результате WAN-порт функционально уравнялся с другим четырьмя портами роутера.
  2. Отключил DHCP на роутере, поскольку эту функцию теперь возьмет на себя сервер.
  3. Вместо WAN’a от РосТелекома я подключил роутер ко внутреннему сетевому интерфейсу сервера, а РосТелеком воткнул во второй внешний интерфейс сервера.
  4. Ребутнул роутер и перезапустил сеть на всех девайсах.

И, о чудо! Ничего не сломалось, все девайсы получили правильные адреса, включая роутер, и интернет спокойно пошел через HomeNet. Далее я настроил второй внешний интерфейс, который был подключен к РосТелекому, а так же две полезных фитчи: traffic balancing и WAN failover. Первая означает балансировку трафика между двумя внешними каналами, а вторая - оперативное переключение трафика на один канал, если второй вдруг откажет. Это делается тоже на удивление просто, если следовать советам в документации. Именно поэтому я не стану приводить никаких конфигов — не скриншоты же веб-гуя мне вам показывать? :-) Там и так все предельно ясно.

Файлопомойка

С нею тоже не возникло особенных проблем. Поначалу, я расстроился, выяснив, что для этого Zentyal предлагает только Samba. Однако, современные дистрибутивы на удивление хорошо умеют с нею работать (чего не скажешь о Винде с NFS), а замеры скорости окончательно рассеяли мои сомнения. Да и в вопросе контроля доступа это решение, пожалуй, лучше NFS. Поэтому, я создал пользователей по числу членов семьи, каждому из них по личной шаре и общую — для собственно файлопомойки.

Виртуальные машины

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

Начну с того, что Zentyal умеет работать с двумя типами виртуальных машин — qemu-kvm и VirtualBox, но при том он не умеет работать с виртуалками разных типов одновременно, а выбором по умолчанию является kvm. Kvm мне показался несколько тормознутым, да и с настройками сетевых интерфейсов в bridged режиме у него было мутно (хоть я в конце концов и разобрался, но это можно честно считать вторым камнем). Недолго думая, я удалил из системы qemu-kvm, поставил VirtualBox и был очень удивлен, когда только что созданная машина отказалась запускаться, а в тексте ошибки было что-то про неизвестный системе тип виртуальной машины hvm. Гугл ничего посоветовать не смог, и мне пришлось заглянуть в сорцы, благо, они открыты. Оказалось, что Zentyal ориентируется не по наличию в системе kvm, а по наличию libvirt, через который он с ним работает, а вот этого в документации уже не было. После удаления libvirt Zentyal замечательно определил наличие VirtualBox’a и даже позволил создать и запустить одну виртуалку :-)

С настройкой bridged режима в vbox все проще некуда — надо лишь потом не забыть настроить DHCP на выдачу вирутальной машине постоянного IP адреса, а вот для kvm пришлось бы один из физических интерфейсов переводить в bidged режим (при этом теряя все привязанные к нему настройки), а потом привязывать виртуальную машину в появившемуся интерфейсу br1.

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

GIT-хостинг

Здесь, я, пожалуй, закончу с диферамбами по Zentyal’у и похвалю еще один продукт. Этим продуктом будет Indefero — система для управления проектами, выполненная в стиле Google Code, но обладающая тремя несомненными преимуществами: open-source, поддержка GIT, SVN и Mercurial в качестве репозитория, и очень легко устанавливающаяся, по крайней мере на Ubuntu, для которой есть ppa с актуальными пакетиками. По приведенной мною ссылке вы найдете исчерпывающую информацию о проекте и его использовании, а я лишь заявлю, что если вам нужна простая, функциональная система, да непременно на своем сервере, и вас не пугает отсутствие дизайна, тогда эта система для вас.

Еще вкусняшки?

Вообще, по ходу настройки всего этого добра я поиграл с кучей разного интересного софта, о чем я не буду писать (настройка VPN, серевого принтера и т. п.), пост и без того гигантский. Если вы дочитали до сюда, ничего не пропуская, я восхищен. Может быть, я расскажу, как я настраивал стриминг видео из сетевого хранилища, но это после того, как донастрою его. Отдельным постом я, наверное, оформлю бенчмарки с сервера, но это тоже чуть позже. Ну а на этот раз достаточно :-)

Ну и, кстати, насчет вкусняшек. Вся эта деятельность не развивалась бы так бурно, если бы не вкусняшки, которыми меня все это время подкармливала жена. И кое-какие рецепты она наверняка опубликует, правда, Солнышко? ;-)

Покойся с миром, Деннис Ритчи

Feature image

8-го октября, на 71-м году жизни скончался Деннис Ритчи (Dennis Ritchie). Об этом стало известно из сообщения его коллеги Роба Пайка.

Дэннис Ритчи — один из величайших инженеров современности, создатель вездесущего языка C и один из авторов ОС UNIX. Сложно переоценить влияние этого человека на современную IT-индустрию: его идеи легли в основу подавляющего большинства современных систем, и почти все повсеметсно использованные ОС написаны на языке Си.

Но его труды не ограничиваются этими двумя вещами. Он занимался развитием операционных систем Plan 9 и Inferno, развивавших концепции UNIX и устранявших его недостатки, а так же языком Limbo. Он занимался общей теорией создания ОС и писал книги, одна из которых, «Язык программирования C» стала хрестоматийной.

Покойся с Миром, Дэннис Ритчи. Человечество благодарно тебе за твои труды.

Собираем домашний сервер. Часть 1, железная.

Feature image

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

На всякий случай напомню ключевые требования, которые предъявлялись к серверу:

  • Дешевизна железа (бюджет был установлен 5000 руб. ± 1000 руб.).
  • Возможность постепенного наращивания мощности в максимально широких рамках.
  • При всем при том как можно менее шумный.

Нетрудно заметить, что первые два пункта слегка конфликтуют с последним. Впрочем, как оказалось, не фатально. Устроенная ревизия закромов показала, что у меня уже есть в наличии две сетевых карты на 100 Мбит с интерфейсом PCI и жесткий диск Seagate на 160 Гб, которые решено было использовать. Для основного хранилища будет использован внешний жесткий диск от WD, работающий в текущем сервере. Исходя из этих требований, были выдвинуты уже более технические требования к железу:

  • Процессор на сокете LGA 1155 (как-то сложилось, что в интеловских процессорах я ориентируюсь лучше, поэтому AMD был отброшен из рассмотрения). Это даст возможность апгрейда процессора аж до Core i7, буде оно потребуется.
  • Память DDR3. Желательно с возможностью апгрейда до 8 или 16 Гб.
  • Минимум 2 слота PCI (для сетевых карт) + гигабитный интерфейс Ethernet на самой материнке, который будет смотреть в локальную сеть.
  • Дополнительно слот PCI или PCI-E, чтобы в будущем добавить Wi-Fi адаптер, желательно с поддержкой стандарта 802.11n.
  • SATAIII-интерфейсы.

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

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

Суммарно — 5746 руб.

Оказалось, что найти материнскую плату, полностью отвечающую пожеланиям, нелегко: из-за “свежести” сокета цены на материнки несколько задраны, и платы форм-фактора ATX, на которые реально упихнуть 4 слота для памяти и 4 слота PCI + пару PCI-E, стоят довольно дорого и позиционируются как профессиональные-производительные. Поэтому пришлось ограничиться платой mATX всего с двумя слотами PCI, одним PCI-E и парой слотов памяти.

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

На десерт выложу несколько фотографий процесса сборки.

Запчасти перед сборкой:

100_4004.JPG_smaller.jpg100_4006.JPG_smaller.jpg100_4007.JPG_smaller.jpg100_4010.JPG_smaller.jpg

Установленная мать:

100_4014.JPG_smaller.jpg

Ну и после сборки:

100_4015.JPG_smaller.jpg

Из небольших “твиков” при сборке стоит упомянуть, пожалуй, только одну тонкость. На системнике присутствуют индикаторы как для жесткого диска, так и для питания, при том индикатор питания довольно яркий. Но чтобы этот индикатор не мешал по ночам, имеет смысл его просто отключить; включенность легко определить по шуму. Либо, если сервер не планируется использовать в качестве файлопомойки, можно поменять местами индикаторы жесткого диска и питания. Тогда яркий индикатор будет загораться очень редко. Но если использовать машину и для файлохранилища, то такого лучше не делать, ибо часто моргающий яркий огонек будет раздражать неизмеримо сильнее, чем горящий постоянно.

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

Собираем домашний сервер. Часть 0, вводная.

Feature image

Этим постом я начинаю серию, в которой расскажу о процессе сборки, настройки и эксплуатации домашнего сервера.

Начать же я хочу с изложения предпосылок, которые обусловили выбор железа, софта и настроек, о которых будет рассказано далее.

Сейчас в квартире любого гика скапливается изрядное количество довольно умных девайсов, умеющих ходить в интернет, играть музыку и делать многие другие полезные вещи. Все это хозяйство в первую очередь увязывается в сеть с помощью Wi-Fi и, в случае стационарников, Ethernet. В центре сети оказывается какой-нибудь недорогой роутер-точка-доступа. У некоторых со временем заводится еще и отдельная машинка, работающая файлопомойкой и торрент-качалкой (свежие релизы своего любимого дистрибутива раздавать на благо сообщества, а не поганый пиратский софт качать, вестимо).

В этом отношении и моя семья не стала исключением. В качестве роутера у нас трудился ASUS WL-520GU с прошивкой от Олега. ну а семейной файлопомойкой работал допотопный ноутбук Toshiba Satellite Pro 4600 с подключеным внешним USB HDD. До поры до времени эта конструкция всех устраивала, хотя скорость работы роутера под нагрузкой былаа не ахти какой, а старенький ноутбук делал файлопомойку не слишком шустрой. Стояло все это дело под столом:

100_4016.JPG_smaller.jpg

Но в один прекрасный момент HomeNet устроил нам эпичный факап с недельным отсутствием интернета, а РосТелеком как раз в это время заменил у нас телефонную линию на оптическую G-Pon, предложив заодно более дешевый и быстрый интернет. Подключение к HomeNet мы решили сохранить на всякий случай, перейдя на младший тариф.

Одновременно с этим меня начала неколько раздражать запутанность нашей домашней сети, в которой теперь болтались роутер, G-Pon модем (это 2 NAT’a на пути во внешку), файлопомойка, все остальные девайсы и два провода от разных провайдеров, которые надо было по мере необходимости перетыкать руками. Безобразие? Безобразие.

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

  1. Максимально тихий.
  2. Недорогой, верхняя планка бюджета — 6000 рублей на все.
  3. Возможность постепенного апгрейда железа в перспективе.

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

С днем программиста!

Поздравляю, коллеги, с профессиональным праздником!

Не буду долго перечислять пожелания — всё всем и так понятно :-) Ну а кому все же хочется пожеланий — добро пожаловать на Хабр, по такому случаю там целых два топика: первый и второй. А для тех, кому не лень пошевелить мозгами, там же есть набор праздничных головоломок. Сумеете решить?

Возвращаясь к пожеланиям, я пожелаю лишь одно: не пускайте корни в своей берлоге. Она хоть и уютная, но за ее пределами есть много интересного!

MODX Library - в Open Source!

Feature image

Как я и планировал, я начинаю по-тихоньку публиковать полезные наработки, использованные мною в phpBB Constructor. Сегодня черед, пожалуй, наиболее важной из них — это MODX Library.

MODX Library — это библиотека, обеспечивающая автоматическую обработку инструкций по установке модов в формате MODX. Идейно этот формат является развитием патчей со значительно расширенным набором операций, удобных при установке модов. В настоящее время подавляющее большинство модов для phpBB3 распространяется именно в этом формате. MODX Library практически полностью реализует поддержку последней версии стандарта MODX 1.2.5, отличия от стандарта описаны в README файле.

Замечу, что именно с этой библиотеки началась работа над phpBB Constructor в начале февраля 2010 года. Впрочем, история разработки конструктора — это отдельная и местами поучительная тема, которой я коснусь как-нибудь в другой раз.

Идеологическим предшественником этой библиотеки была другая моя, гораздо более ранняя разработка под названием Lite MOD Parser, занимавшегося теми же задачами, но только для более раннего формата MOD, популярного в эпоху phpBB2. Сейчас эта штука может быть полезна разве что в исторических целях, а тогда планировалось ее использование в претензиозном проекте Web Update, призванном накорню автоматизировать обновление сайтовых движков. И, вероятно, будь у меня тогда больше настырности и опыта, из этого чего-нибудь бы и вышло :-)

phpBB Constructor

Пришло время представить широкой публике проект, над которым я работал все последнее время. Знакомьтесь, phpbbconstructor.com.

Вводная

phpBB — один из самых лучших open source движков для создания форумов. На нем работают сотни тысяч конференций, многие из которых обладают впечатляющей функциональностью. И несмотря на то, что в базовой комплектации phpBB3 предоставляет практически все, что может понадобиться для создания форума, его истинная сила в модах, написанных сообществом. В настоящий момент только в его официальном каталоге модификаций зарегистрирован 431 мод разной сложности. И еще больше можно найти на форуме, в разделе бета-версий.

Однако, несмотря на усилия phpBB Group, установка модов по-прежнему является непростой задачей для людей, далеких от IT (снобы от phpBB в этом месте замечают, что это как естественный отбор: не справились с установкой мода — нечего вам вообще администрированием форума заниматься, а фрилансеры начинают тонко намекать на свои услуги). Установка модов вручную требует усидчивости и внимательности, а AutoMOD справляется не со всеми модами (и хорошо, если он сам где-нибудь свалится, хуже, если он мод установит, а проблемы обнаружатся недели через три, когда уже и непонятно, откуда у них корни). Кроме того, поиск нужных модов, при условии, что сам толком не знаешь, что ищешь, остается хитрым и увлекательным квестом.

Эпоха сборок

Проблемы, описанные мною, стары как мир phpBB2. На пике популярности phpBB стали появляться команды энтузиастов, которые вручную делали сборки дистрибутивов phpBB2, включающие в себя более или менее широкий набор модов, совершенно необходимых по мнению авторов сборки. Самым известным проектом из этой серии был, наверное phpBB Plus, впечатлявший новичков симпатичной модификацией subsilver и полусотней модов на борту.

Однако, и здесь были свои проблемы. Это и задержки с выходом обновлений (пока команда сборки накатит все новые моды на новую и протестирует, долгая песня…), это и разногласия по набору модов в сборке (на всех не угаодишь), это и “левые” сборки с незаметными бекдорчиками на борту и масса других сложностей.

С выходом phpBB3 количество активно поддерживаемых сборок существенно сократилось благодаря богатому функционалу базового дистрибутива и скорому релизу официального AutoMOD’a, но несколько проектов все же осталось на плаву.

Сделай сам!

И вот тут-то на сцену выходит phpBB Constructor. Он позволяет самому выбрать состав сборки, которая вам нужна из предлагаемого набора модов, а так же указать нужные вам локализации. После чего эта сборка будет автоматически создана на основе свежайшего дистрибутива phpBB3 и отдана вам :-)

Таким образом, Конструктор выигрывает и у готовых сборок “от умельцев”, поскольку вы можете сами выбирать состав сборки, и у упомянутого AutoMOD’a: в него включены моды, решающие наиболее распространенные проблемы, и они заранее протестированы на работоспособность и совместимость. В результате, вы в несколько кликов получаете хороший базовый набор модов, и при желании можете так же расширить его самостоятельно после установки форума.

Я записал небольшой скринкаст, демонстрирующий работу с phpBB Constructor:

 Участие - приветствуется

Я с удовольствием выслушаю и постараюсь принять к сведению любую конструктивную критику и пожелания. Это касается как функционала конструктора, так и набора модов, который пока невелик, но будет расти (сейчас в очереди на добавление около 25 модов из официального каталога, потом дело дойдет и до модов в состоянии бета-версий).

А пока лишь приведу список наиболее интересный фитч, которые я планирую сделать:

  • Собственные профили сборок, с возможностью генерации пакетов автообновлений.
  • Форма предложения модов и сообщения о выходе новой версии мода, чтобы мне было проще поддерживать моды в актуальном состоянии.
  • Разбивка модов по категориям.
  • Рекомендуемые моды.
  • Генерация инструкций по модификации нестандартных стилей.
  • Авотматический скрипт для установки на нестандартные стили.

Есть в планах еще и много менее заметных улучшений, которые перечислять не имеет особого смысла.

Кроме того, я и сообщество phpBB в целом будем благодарны, если вы примете участие в локализации модов из Конструктора для разных языков.

Open Source

Я давно являюсь сторонником open source, и потому планирую как можно больше наработок, вошедших в phpBB Constructor, передать phpBB-сообществу. Сюда относятся и сердце конструктора — MOD Library, и все правки, которые мне приходится вносить в моды для обеспечения совместимости, и другие вспомогательные инструменты. Кроме того, есть публично доступная Зона 51зона разработки, в которой каждый может наблюдать за ходом разработки и репортить баги.

Обработка множества исключений: решение

Feature image

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

Решение 1, читерское.

В условии было сказано, что надо обрабатывать исключения в методах init(), run() и shutdown(). И хотя это явно не оговаривалось, по логике функционирования эти методы вызываются подряд. Следовательно, можно сделать так:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php

// Code ...


$api = new ApiDispatcher();
try {
	$api->init();
	$api->run();
	$api->shutdown();
} catch(MyException1 $e) {
	// Handle exception
}
// More exception handling...
catch(Exception $e) {
	// Handle exception
}

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

Достоинства решения:

  • простота;
  • минимум дублирования кода;
  • локальность модификации кода.

Недостатки:

  • жестко привязываемся к сценарию последовательного вызова методов init(), run(), shutdown(), в реальной жизни может потребоваться вызывать их из разных частей приложения в разные моменты времени;
  • метод-обработчик исключений должен очень многое знать обо всех возможных исключениях, при появлении нового исключения придется править метод-обработчик.

Решение 2, логичное.

Следующее решение приходит в голову практически сразу после прочтения условия, и оно действительно хорошо подходит для решения задачи.

Именно его в комментариях предложил Vladimir Rusinov, за что ему спасибо:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python

class ApiDispatcher(object):

    def _exception_handler(self, e):
        print str(e)
        print type(e)
        if type(e) == IndexError:
             print "Check your indexes!"
        # logging, error messages, etc...

class MyClass(ApiDispatcher):

    def init(self):
        try:
            # do something
            myfunction()
        except Exception, e:
            self._exception_handler(e)

    def run(self):
        try:
            # do something else
            myfunction()
        except Exception, e:
            self._exception_handler(e)

    def shutdown(self):
        try:
            # finish our work
            myfunction()
        except Exception, e:
            self._exception_handler(e)


def myfunction():
    #a = []
    #return a[42]
    raise IndexError('blah-blah-blah')

if __name__ == '__main__':
    c = MyClass()
    c.init()
    c.run()
    c.shutdown()

Достоинства решения:

  • простота,
  • независимость от мест вызова методов (то есть, их не обязательно вызывать подряд),
  • минимум дублирования кода,
  • локальность правок.

Недостатки решения:

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

Решение 3, хитрое.

Это решение в конечном итоге я и использовал в своем проекте. Сначала я приведу код, а потом дам дополнительные комментарии:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php

class ApiException {
	public function init() {
		try {
			throw MyException(); // Just exception without error code and message
		} catch(Exception $e) {
			$this->handleException($e);
		}
	}

	public function run() {
		try {
			$operation_name = 'foo';
			throw AnotherException("Failed to perform $operation_name"); // Custom error message
		} catch(Exception $e) {
			$this->handleException($e);
		}
	}
	public function init() {
		try {
			throw OneMoreException(Errors::ERROR_CODE_DATABASE); // Custom error code
		} catch(Exception $e) {
			$this->handleException($e);
		}
	}

	protected function handleException(Exception $e) {
		$error_code = $e->getCode();
		$error_message = $e->getMessage();
		if(empty($error_message)) {
			$error_message = Errors::getMessageByCode($error_code);
		}

		Logger::get()->log($error_code, $error_message);
		// Do other stuff...
	}
}

class MyException extends Exception {
	protected $code = Errors::ERROR_CODE_MYERROR;
}

class AnotherException extends Exception {
	protected $code = Errors::ERROR_CODE_ANOTHERERROR;
}

class OneMoreException extends Exception {
	protected $code = Errors::ERROR_CODE_ONEERROR;
}

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

В PHP все исключения имеют по умолчанию два поля (есть и другие, но они нас не интересуют): $message = null и $code = 0, сообщение и код ошибки соответственно. Во всех наших исключениях мы переопределяем значение по умолчанию для кода ошибки, чтобы оно соответствовало типу исключения.

Теперь, когда мы создаем исключение, мы можем задать ему сообщение, которое описывает причину его возникновения, и, если надо, можем уточнить и код ошибки. Если же мы сообщение вручную не зададим - оно будет получено через класс Errors. В случае, если мы поймаем “не наше” исключение (типа простого Exception), у него код ошибки будет 0, и класс Errors успешно опишет его как “Unknown exception”.

Достоинства решения:

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

Недостатки решения

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

Итоги

Я привел целых три решения задачи и видно, что ни одно из них не является безоговорочно правильным. А это значит, что даже такая простая задача, как описано в этом упражнении, требует внимательного анализа требований к системе и ваших возможностей, как ее разработчика. В моем случае решение 3 оказалось наименьшим злом, и я использовал его. Но запросто может оказаться, что в другой ситуации оптимально “наивное” решение 1.

Упражнение: обработка множества исключений

Feature image

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

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

  1. init() — инициализирует, необходимые для обработки запроса объекты.
  2. run() — запускает обработку бизнес-логики.
  3. shutdown() — освободжает ресурсы.

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

Так же будем считать, что нам на любом этапе доступен метод Client::reportError($error_code, $error_message), который обеспечивает вывод клиенту сообщения об ошибке и прекращение работы программы.

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

Естественно, решение “в лоб” не подходит, поскольку оно неизбежно приведет к большому количеству дублирующего кода.

Как бы вы решили такую задачу? Языки решения принимаются любые в пределах разумного: Java, PHP, C++. Не возбраняется использование языко-специфичных конструкций, если они действительно удобны — расширение кругозора никому лишним не будет ;-)

Я нашел два приемлемых решения, и опишу оба завтра, если их никто не назовет до меня.

Три top-подобных утилиты, которые должны быть на каждой Linux-машине

Feature image

Практически всем пользователям знакома утилита top, показывающая интерактивный список процессов, отсортированный по нагрузке на процессор. История этой утилиты идет от 1984 года, когда Уильям ЛеФевр написал такую утилиту для BSD 4.1. С тех пор top или его аналог есть практически в каждой UNIX-подобной ОС.

Годами доказав свою практичность, top вдохновил многих других программистов на разработку похожих утилит, относящихся к разряду must-have на любой Linux-машине, поскольку они дают возможность быстро оценить ситуацию в системе.

htop

htop — это логическое развитие top. Его интерфейс сделан с помощью ncurses, и благодаря этому он предоставляет гораздо больше возможностей по визуализации процессов и общей загрузки системы, а так же значительно более интуитивный интерфейс настройки всего этого.

iotop

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

iftop

Эта утилита аналогична предыдущей, но показывает она загрузку сетевого интерфейса. Надо заметить, что в отличие от двух предыдущих программ, она для работы требует прав root’а, и для запуска ей желательно указать имя интерфейса, который ей надо мониторить с помощью ключа -i.

[Бонус] duf

(Добавлено 2020-10-03) Duf — это аналог известной утилиты du, но с более приятным текстовым графическим интерфейсом, который отображает состояние всевозможных устройств хранения, группируя их в удобные таблички по типу. Утилита не интерактивная в том смысле, что она просто печатает текущее состояние файловой системы и выходит, но если сильно хочется, то можно сделать так:

1
$ watch duf -width $(tput cols)

Итог

Все три утилиты (htop, iotop и iftop) позволяют легко и быстро оценить положение дел на вашей машине даже в условиях отсутствия иксов или когда система бодро закапывается в своп и нужно срочно выяснить, почему: в такой ситуации дожидаться запуска графической утилиты — смерти подобно.