SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.
SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.
Successfully reported this slideshow.
Activate your 14 day free trial to unlock unlimited reading.
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 )
– Судя по количеству пермиссий
30.
Приближение III
А причем тут вообще Haskell?
Какая от него польза?
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 ● И это правильно
44.
Haskell
● Напишем маленький HTTP сервер
● Пусть слушает на порту 10000
● И отвечает HELLO WORLD
import Happstack.Server
main = do
simpleHTTP nullConf { port = 10000 } $ ok "HELLO WORLD"
45.
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
46.
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
47.
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
49.
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))