3. Приближение I
Клиент (Браузер) Сервер приложений СУБД
JavaScript
HTTP/JSON Erlang/MochiWeb SQL
QooxDoo PostgreSQL
Совершенно типичная, рядовая, унылая опердень
4. Приближение I
● Результат: FAIL
● Разработчики не довели проект...
● Даже до середины
● Основные причины --- организационные
● И тем не менее
5. Приближение I
● QooxDoo
● Сложный дизайн
● Oчень verbose
● Очень много писать руками на JS
● Практическое отсутствие декларативности
● Ручной маппинг данных
● Странные ограничения в виджетах
● Относительно слабая документация
● Довольно симпатично выглядит, впрочем
6. Приближение I
● Mochiweb/Erlang
● Динамическая типизация
– ОБЯЗАТЕЛЬНО требуются юнит-тесты
● Которые никто не пишет
● Полуживые средства Database Connectivity
– Требовалась ручная доработка
● Генерация и парсинг JSON
– Очень мало и полуживое
– Потребовалась ручная доработка
7. Приближение I
● Mochiweb/Erlang
● Автоматический маппинг HTTP на SQL (CRUD)
– Оказался слишком сложен
● Достаточно бедный язык
● Динамическая типизация
– При этом почти отсутствует метапрограммирование
● Недостаток опыта у разрабатывающих
8. Приближение I
● FAIL
● Недостаточная организация
– Недостаток времени на организацию
● Недостаток опыта
– Овердизайн
● Инструменты увеличивают количество работы
– А должны уменьшать
10. Приближение II
● Другой разработчик
● Erlang/MochiWeb
● ExtJS вместо QooxDoo
● Чуть больше декларативности
● Шаг в сторону --- расстрел
● Мистические глюки
11. Приближение II
● FAIL
● Основные причины
– Недостаточная организация
– Недостаток квалификации
– Недостаток опыта
12. Приближение III
Если хочешь, что бы что-то было сделано хорошо --- сделай это сам
14. Лирическое отступление: Дизайн
“Центр масс” системы
Клиент Сервер СУБД Вариант I
Клиент Сервер СУБД Вариант II
Клиент Сервер
СУБД Вариант III
15. Лирическое отступление: Дизайн
Клиент Сервер СУБД Вариант I
● Pros ● Cons
● Быстро ● Трэш
прототипировать ● Угар
– Иногда ● Содомия
16. Лирическое отступление: Дизайн
Клиент Сервер СУБД Вариант II
● Pros ● Cons
● Миграция между СУБД ● Быстро, толькo если держать всю
– Как секс у тинейжеров: чаще говорят, БД в памяти приложения
чем делают
– Страдает D в ACID
● Использование ORM
● Типичные языки для
– Хорошо, если не знаете SQL манипуляции данными менее
● Возможность строгого удобны, чем SQL + расширения
типизирования модели
● Все равно отчеты придется
Если вы используете типизацию
–
делать на SQL
17. Лирическое отступление: Дизайн
Клиент Сервер
СУБД Вариант III
● Pros ● Cons
● Просто ● Дорогая смена СУБД
● Быстрая разработка – Но см. предыдущий
● “Горячие” апгрейды слайд
– Кроме изменения схемы ● Отвратительная
● Поддерживать может типизация в БД
любой DBA
– Придется с этим жить
– И даже немного
дорабатывать
18. Приближение III
? Сервер приложений
● OCaml/Ocsigen/Eliom ● Haskell/Happstack
● Низкий порог ● Выше качество
вхождения сервера
– Верно для OCaml (но ● API проще
не для Ocsigen/Eliom)
● Отличные
● Слабые библиотеки библиотеки DB
DB Connectivity Connectivity (HDBC)
– Пришлось – Все работает
дорабатывать
19. Приближение III
? Сервер приложений
● OCaml/Ocsigen/Eliom ● Haskell/Happstack
● Легкие процессы (Lwt) ● Легкие процессы (ForkIO)
– Ручной запуск блокирующих – Прозрачный запуск блокирующих
вызовов в native threads вызовов (GHC 6) в native threads
● Например, SQL запросов ● Не будут блокировать сервер
● Либо блокировка всего сервера ● Без усилий с вашей стороны
● Признанные ошибки Ocsigen для – epoll/kqueue IO backend в GHC 7.x
этого случая ● Не блокируют
– Реализация на основе монад ● Как в Erlang
● Время удивляться ● C10K / C100K (?)
● Какие еще монады в Окамле?! – Естественно вписываются в язык
● End of easy-to-learn Ocaml ● Действительно просто
– И это вы еще типов в Eliom не
видели
20. Приближение III
? Сервер приложений
● OCaml/Ocsigen/Eliom ● Haskell/Happstack
● Инфраструктура ● Инфраструктура
– Стандартная библиотека бедна – Стандартная библиотека
и неудобна (Prelude как минимум) хороша
● Почти всегда требуются
дополнительные библиотеки (ExtLib – Немало сторонних библиотек
--- как Boost в C++) ● См. apt-cache search haskell
– Относительно мало сторонних ● См. cabal list
библиотек – Системы сборки / пакетирования
– Системы сборки / пакетирования ● Cabal
● Ocamlfind ● Cabal-dev
● Ocamlbuild ● Capri
● И другие – Последние две решают
● Package hell присутствует проблему package hell
● В целом, удобнее
21. Приближение III
… And the winner is: Haskell/Happstack
Клиент (Браузер) Сервер приложений СУБД
? HTTP/?
Haskell /
Happstack
SQL
PostgreSQL
22. Приближение III
● QooxDoo
Клиент (Браузер)
● ExtJs
● Остальные подобного уровня
Такие же или много хуже
? ● OpenLazslo
● HTML5 (Canvas) / Flash
● Не DOM
● В основном декларативное
● Layout Engine
● Декларативный биндинг данных в XML на
контролы при помощи Xpath
– PostgreSQL умеет отдавать данные в
XML
25. Приближение III
Сервер:
Клиент: JS/OL Happstack App
XHR
Widget
XML
XHR SQL
Widget PostgreSQL
XML
Widget XHR
XML
26. Приближение III
Happstack App
(generic CRUD + BL)
HTTP GET/POST SQL
/list/entity?col1=... SELECT XMLELEMENT(..)
/query/entity?col=CND(CND2(..))&.. SELECT .. FROM .. WHERE CONDITIONS
/create/entity?col1=... INSERT INTO …
/update/entity?key1=...&col1=... UPDATE … WHERE …
/delete/entity?key1=... DELETE FROM … WHERE ...
/fn/function?$0=x1...$n=xn SELECT function(x1, … , xn)
27. Приближение III
Полная картина
Клиент Happstack/App
HTTP
HTTP (XHR)
Node 1 SQL
JS/OL NGINX
PostgreSQL
XML XML Happstack/App XML
mod_proxy
Node N
●Данные
Раздача статики
●Generic CRUD
●
●Вызов бизнес-логики
●View
●Балансирование нагрузки ●Хранимые процедуры
●Отчеты (XML → Latex → PDF)
●Failover ●Триггеры
●Аутентификация / Авторизация
●HTTPS ●Правила
●RBAC
●Констрейнты
28. Приближение III
● WIN
● Разработка завершена
● Деплоймент произведен
● Некоторые свойства
● Достаточно универсальный сервер на Happstack
– Отсутствует специфичный для приложения код
– Можно использовать в другом подобном проекте
● Возможно, даже без перекомпиляции
– Можно дорабатывать, не трогая часть на Haskell вообще (только PG и JS)
● Аутентификация / Авторизация / RBAC
– Сессии пользователей (кэширование привилегий)
– Двухуровневый механизм пермиссий (permissions и metapermissions)
– Проверка пермиссий server-side и client-side
– Сокрытие элементов UI, к которым нет доступа
● Generic CRUD
● Трансляция HTTP запросов в SQL по определенным правилам
– В том числе и для хранимых процедур
● Генерация отчетов: XML → Latex → PDF
29. Приближение III
● Некоторые метрики
● Приблизительно сорок экранов/форм
● Свыше пятидесяти таблиц
● Порядка семидесяти VIEW
● Около сорока хранимых процедур
● 116 различных веб-методов (CRUD + BL +
Отчеты + Auth )
– Судя по количеству пермиссий
31. Haskell
● Сильная типизация
● Как правило, если скомпилировалось, то работает
● Легко и безопасно вносить изменения
– Не собирается, пока некорректно (как правило)
● Компилируется в нативный код
● Работает точно быстрее Erlang, Python, Ruby,
Javascript, Lua ...
– Это не так важно, но довольно приятно
32. Haskell
● Компилируется в нативный код
● Легко разворачивать на Production
– Как правило, не требуется ничего дополнительно
устанавливать
33. Haskell
● Много готовых к использованию библиотек
● Больше, чем в OСaml или Erlang
● Например
– Happstack.Server
● Организация HTTP сервера
● Роутинг и обработка HTTP запросов
– HDBC
● Доступ к БД
● Существует множество альтернатив, включая решения с сильной
типизацией, генерацией схем из ADT, генерацией запросов, большим
выбором бэкенда для хранения данных (аналогов ORM), etc, etc
– hdaemonize
● Стандартный демон, понимающий daemon start|stop|restart в несколько
строчек кода
34. Приближение III
● Haskell
● Много готовых к использованию библиотек
● hslogger
– Логгирование с обширными настройками
● ConfigFile
– Чтение конфигов
● Parsec
– Разбор DSL для не-KV HTTP-запросов:
col1 = OR(EQ(“BAR”), LIKE(“%FOO%”))
col2 = AND(GREATER(1), LESS(10))
● В неcколько строчек кода
35. Haskell
● Много готовых к использованию библиотек
● Регулярные выражения
– Различные бэкенды
● POSIX, TDFA, PCRE ...
– Наличие произвольных операторов в языке делает их
практически встроенными в язык, как в Perl:
●
someString =~ “FOO (BAR)” :: Bool
● someString =~ “FOO (BAR) ALICE (BOB)” :: [[String]]
– Работают с UTF8
● someString =~ “Превед (медвед)” :: [[String]]
36. Haskell
● Много готовых к использованию библиотек
● STM (Software Transactional Memory)
– Атомарный конкурентный доступ к данным в памяти
– Для хранения сессионных данных
● Сеreal
– Сериализация структур данных
– Для сохранения служебной информации в БД
– Один из десятков вариантов
37. Haskell
● Много готовых к использованию библиотек
● MySQL
● PostgreSQL
● SQLite
● ODBC
● Riak
● Redis
● MongoDB
● Memcached
● MACID
● ...
38. Haskell
● Инфраструктура разработки
● Cabal
– Централизованная установка пакетов (как CPAN и подобные)
– Сборка проекта
– Трекинг зависимостей
– Запуск тестов
– Сборка дистрибутивов
● Cabal-dev
– Тот же cabal, но без боли
● Позволяет устанавливать пакеты в локальную песочницу для проекта
● Не трогает остальную систему
● Ликвидирует Package Hell в среде разработки
– А в Production его и так нет
39. Haskell
● Инфраструктура разработки
● Hlint
– Научит вас, как надо писать на Haskell
– Находит дублирующий код и предлагает варианты
исправления
– Способен заменить не слишком въедливое Code
Review
● Каким оно чаще всего и бывает (нехватка времени, лень,
усталость, замыленность, нежелание брать ответственность
за чужой код на себя)
● В Haskell практически отсутствует тип ошибок, которые может
найти Code Review и не может найти компилятор
– Использование неинициализированных данных, etc
40. Haskell
● Инфраструктура разработки
● QuickCheck
– Автоматическая генерация тестов
● Средства автоматического документирования
– Haddock
● Просто запустите cabal haddock
42. Haskell
● Удобный инструмент ● Академический язык для маргиналов
● Большое количество библиотек ● Не поддерживается в вашем IDE
● Удобный FFI – Простите, не поддерживается в чем?
● Хорошая инфраструктура разработки ● Ленивость
● Выразительный язык – Так было надо
– Пишем меньше – Иногда, наоборот, полезна
– Получаем больше – Когда она создаст реальные проблемы, я
● Сильная статическая типизация обязательно расскажу (пока просто нет такого
опыта)
– Безжалостно массово правим код, не боясь что-то
сломать и не заметить ● Монады
● Конкурентность – А что “монады” ?
– epoll/kqueue IO – А вы уверены, что точно знаете, что такое
– STM “объект” и “класс” например?
● Поддержка SMP ● Но продолжаете ими пользоваться
● Либо ваше имя, вероятно, Luca Cardelli
– Считай факториал в два раза быстрее на двух
ядрах вместо одного, просто добавив вызов ● Сложная система типов
функции par Зато многое позволяет
–
● Поддержка UTF-8 ● Ограниченный вывод типов
● Метапрограммирование Местами заставляет расставлять аннотации
–
● Хороший REPL ● И это правильно
45. Haskell
● Напишем маленький HTTP сервер
● Пусть слушает на порту 10000
● И отвечает HELLO WORLD
import Happstack.Server
main = do
simpleHTTP nullConf { port = 10000 } $ ok "HELLO WORLD"
46. Haskell
● Добавим счетчик запросов
import Happstack.Server
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar
import Control.Monad.Reader
main = do
counter <- newTVarIO 0
simpleHTTP nullConf { port = 10000 } $ do
cnt <- readCounter counter
writeCounter counter (cnt+1)
ok $ "HELLO WORLD: " ++ show cnt
where readCounter c =
liftIO . atomically $ readTVar c
writeCounter c v =
liftIO . atomically $ writeTVar c v
47. Haskell
● Демонизируем
import Happstack.Server
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar
import Control.Monad.Reader
import System.Posix.Daemonize
main = do
counter <- newTVarIO 0
let ourHttpDaemon () = do
simpleHTTP nullConf { port = 10000 } $ do
cnt <- readCounter counter
writeCounter counter (cnt+1)
ok $ "HELLO WORLD: " ++ show cnt
serviced simpleDaemon { program = ourHttpDaemon }
where readCounter c =
liftIO . atomically $ readTVar c
writeCounter c v =
liftIO . atomically $ writeTVar c v
48. Haskell
● Добавим обработку сигналов
● Обнулим счетчик запросов по SIGUSR1
● Напишем что-нибудь в системный лог
import Happstack.Server
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar
import Control.Monad.Reader
import System.Posix.Daemonize
import System.Posix.Signals
import System.Posix.Syslog
main = do
counter <- newTVarIO 0
let ourHttpDaemon () = do
simpleHTTP nullConf { port = 10000 } $ do
cnt <- readCounter counter
writeCounter counter (cnt+1)
ok $ "HELLO WORLD: " ++ show cnt
let onUSR1 = writeCounter counter 0 >> syslog Notice "Counter is set to 0" :: IO ()
installHandler sigUSR1 (Catch onUSR1) (Just fullSignalSet)
serviced simpleDaemon { program = ourHttpDaemon }
where readCounter c =
liftIO . atomically $ readTVar c
writeCounter c v =
liftIO . atomically $ writeTVar c v
50. Haskell
● Запишем что-нибудь в БД
● И постараемся остаться в рамках одного кадра презентации
● Допустим, у нас есть база test и таблица counter (int, timestamp)
import Happstack.Server
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar
import Control.Monad.Reader
import System.Posix.Daemonize
import System.Posix.Signals
import System.Posix.Syslog
import Database.HDBC
import Database.HDBC.PostgreSQL
main = do
counter <- newTVarIO 0
let ourHttpDaemon () = do
simpleHTTP nullConf { port = 10000 } $ do
cnt <- readCounter counter
writeCounter counter (cnt+1)
liftIO $ doInsert cnt
ok $ "HELLO WORLD: " ++ show cnt
let onUSR1 = writeCounter counter 0 >> syslog Notice "Counter is set to 0" :: IO ()
installHandler sigUSR1 (Catch onUSR1) (Just fullSignalSet)
serviced simpleDaemon { program = ourHttpDaemon }
where readCounter c =
liftIO . atomically $ readTVar c
writeCounter c v =
liftIO . atomically $ writeTVar c v
insert c conn = quickQuery' conn "INSERT INTO counter VALUES(?)" [toSql (c::Int)]
doInsert cnt = withPostgreSQL "dbname=test user=dmz" (c -> withTransaction c (insert cnt))