Operden1

9,667
-1

Published on

1 Comment
24 Likes
Statistics
Notes
  • 46 слайд, добавим счётчик запросов: чтение и запись не синхронизированы, так и задумано?
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
9,667
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
110
Comments
1
Likes
24
Embeds 0
No embeds

No notes for slide

Operden1

  1. 1. Динамическая Опердень на Haskell Как за три шага превратить острый гемморой в хронический
  2. 2. Приближение I
  3. 3. Приближение IКлиент (Браузер) Сервер приложений СУБД JavaScript HTTP/JSON Erlang/MochiWeb SQL QooxDoo PostgreSQL Совершенно типичная, рядовая, унылая опердень
  4. 4. Приближение I● Результат: FAIL● Разработчики не довели проект... ● Даже до середины● Основные причины --- организационные ● И тем не менее
  5. 5. Приближение I● QooxDoo ● Сложный дизайн ● Oчень verbose ● Очень много писать руками на JS ● Практическое отсутствие декларативности ● Ручной маппинг данных ● Странные ограничения в виджетах ● Относительно слабая документация ● Довольно симпатично выглядит, впрочем
  6. 6. Приближение I● Mochiweb/Erlang ● Динамическая типизация – ОБЯЗАТЕЛЬНО требуются юнит-тесты ● Которые никто не пишет ● Полуживые средства Database Connectivity – Требовалась ручная доработка ● Генерация и парсинг JSON – Очень мало и полуживое – Потребовалась ручная доработка
  7. 7. Приближение I● Mochiweb/Erlang ● Автоматический маппинг HTTP на SQL (CRUD) – Оказался слишком сложен ● Достаточно бедный язык ● Динамическая типизация – При этом почти отсутствует метапрограммирование ● Недостаток опыта у разрабатывающих
  8. 8. Приближение I● FAIL ● Недостаточная организация – Недостаток времени на организацию ● Недостаток опыта – Овердизайн ● Инструменты увеличивают количество работы – А должны уменьшать
  9. 9. Приближение II
  10. 10. Приближение II● Другой разработчик● Erlang/MochiWeb● ExtJS вместо QooxDoo ● Чуть больше декларативности ● Шаг в сторону --- расстрел ● Мистические глюки
  11. 11. Приближение II● FAIL ● Основные причины – Недостаточная организация – Недостаток квалификации – Недостаток опыта
  12. 12. Приближение III Если хочешь, что бы что-то было сделано хорошо --- сделай это сам
  13. 13. Приближение IIIКлиент (Браузер) Сервер приложений СУБД ? HTTP/? ? SQL PostgreSQL
  14. 14. Лирическое отступление: Дизайн “Центр масс” системы Клиент Сервер СУБД Вариант I Клиент Сервер СУБД Вариант IIКлиент Сервер СУБД Вариант III
  15. 15. Лирическое отступление: Дизайн Клиент Сервер СУБД Вариант I● Pros ● Cons ● Быстро ● Трэш прототипировать ● Угар – Иногда ● Содомия
  16. 16. Лирическое отступление: Дизайн Клиент Сервер СУБД Вариант II● Pros ● Cons ● Миграция между СУБД ● Быстро, толькo если держать всю – Как секс у тинейжеров: чаще говорят, БД в памяти приложения чем делают – Страдает D в ACID ● Использование ORM ● Типичные языки для – Хорошо, если не знаете SQL манипуляции данными менее ● Возможность строгого удобны, чем SQL + расширения типизирования модели ● Все равно отчеты придется Если вы используете типизацию – делать на SQL
  17. 17. Лирическое отступление: ДизайнКлиент Сервер СУБД Вариант III ● Pros ● Cons ● Просто ● Дорогая смена СУБД ● Быстрая разработка – Но см. предыдущий ● “Горячие” апгрейды слайд – Кроме изменения схемы ● Отвратительная ● Поддерживать может типизация в БД любой DBA – Придется с этим жить – И даже немного дорабатывать
  18. 18. Приближение III ? Сервер приложений● OCaml/Ocsigen/Eliom ● Haskell/Happstack ● Низкий порог ● Выше качество вхождения сервера – Верно для OCaml (но ● API проще не для Ocsigen/Eliom) ● Отличные ● Слабые библиотеки библиотеки DB DB Connectivity Connectivity (HDBC) – Пришлось – Все работает дорабатывать
  19. 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. 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. 21. Приближение III … And the winner is: Haskell/HappstackКлиент (Браузер) Сервер приложений СУБД ? HTTP/? Haskell / Happstack SQL PostgreSQL
  22. 22. Приближение III ● QooxDooКлиент (Браузер) ● ExtJs ● Остальные подобного уровня Такие же или много хуже ? ● OpenLazslo ● HTML5 (Canvas) / Flash ● Не DOM ● В основном декларативное ● Layout Engine ● Декларативный биндинг данных в XML на контролы при помощи Xpath – PostgreSQL умеет отдавать данные в XML
  23. 23. Приближение IIIКлиент (Браузер) Сервер приложений СУБД Haskell / JS/OpenLaszlo HTTP/XML SQL Happstack PostgreSQL
  24. 24. Приближение III Опердень: CRUD UPDATE генерируется CREATE генерируется READ Businness Logic генерируется (хранимые процедуры) DELETE (SOFT)
  25. 25. Приближение III Сервер:Клиент: JS/OL Happstack App XHR Widget XML XHR SQL Widget PostgreSQL XML Widget XHR XML
  26. 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. 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. 28. Приближение III● WIN ● Разработка завершена ● Деплоймент произведен● Некоторые свойства ● Достаточно универсальный сервер на Happstack – Отсутствует специфичный для приложения код – Можно использовать в другом подобном проекте ● Возможно, даже без перекомпиляции – Можно дорабатывать, не трогая часть на Haskell вообще (только PG и JS) ● Аутентификация / Авторизация / RBAC – Сессии пользователей (кэширование привилегий) – Двухуровневый механизм пермиссий (permissions и metapermissions) – Проверка пермиссий server-side и client-side – Сокрытие элементов UI, к которым нет доступа ● Generic CRUD ● Трансляция HTTP запросов в SQL по определенным правилам – В том числе и для хранимых процедур ● Генерация отчетов: XML → Latex → PDF
  29. 29. Приближение III● Некоторые метрики ● Приблизительно сорок экранов/форм ● Свыше пятидесяти таблиц ● Порядка семидесяти VIEW ● Около сорока хранимых процедур ● 116 различных веб-методов (CRUD + BL + Отчеты + Auth ) – Судя по количеству пермиссий
  30. 30. Приближение IIIА причем тут вообще Haskell? Какая от него польза?
  31. 31. Haskell● Сильная типизация ● Как правило, если скомпилировалось, то работает ● Легко и безопасно вносить изменения – Не собирается, пока некорректно (как правило)● Компилируется в нативный код ● Работает точно быстрее Erlang, Python, Ruby, Javascript, Lua ... – Это не так важно, но довольно приятно
  32. 32. Haskell● Компилируется в нативный код ● Легко разворачивать на Production – Как правило, не требуется ничего дополнительно устанавливать
  33. 33. Haskell● Много готовых к использованию библиотек ● Больше, чем в OСaml или Erlang ● Например – Happstack.Server ● Организация HTTP сервера ● Роутинг и обработка HTTP запросов – HDBC ● Доступ к БД ● Существует множество альтернатив, включая решения с сильной типизацией, генерацией схем из ADT, генерацией запросов, большим выбором бэкенда для хранения данных (аналогов ORM), etc, etc – hdaemonize ● Стандартный демон, понимающий daemon start|stop|restart в несколько строчек кода
  34. 34. Приближение III● Haskell ● Много готовых к использованию библиотек ● hslogger – Логгирование с обширными настройками ● ConfigFile – Чтение конфигов ● Parsec – Разбор DSL для не-KV HTTP-запросов: col1 = OR(EQ(“BAR”), LIKE(“%FOO%”)) col2 = AND(GREATER(1), LESS(10)) ● В неcколько строчек кода
  35. 35. Haskell● Много готовых к использованию библиотек ● Регулярные выражения – Различные бэкенды ● POSIX, TDFA, PCRE ... – Наличие произвольных операторов в языке делает их практически встроенными в язык, как в Perl: ● someString =~ “FOO (BAR)” :: Bool ● someString =~ “FOO (BAR) ALICE (BOB)” :: [[String]] – Работают с UTF8 ● someString =~ “Превед (медвед)” :: [[String]]
  36. 36. Haskell● Много готовых к использованию библиотек ● STM (Software Transactional Memory) – Атомарный конкурентный доступ к данным в памяти – Для хранения сессионных данных ● Сеreal – Сериализация структур данных – Для сохранения служебной информации в БД – Один из десятков вариантов
  37. 37. Haskell● Много готовых к использованию библиотек ● MySQL ● PostgreSQL ● SQLite ● ODBC ● Riak ● Redis ● MongoDB ● Memcached ● MACID ● ...
  38. 38. Haskell● Инфраструктура разработки ● Cabal – Централизованная установка пакетов (как CPAN и подобные) – Сборка проекта – Трекинг зависимостей – Запуск тестов – Сборка дистрибутивов ● Cabal-dev – Тот же cabal, но без боли ● Позволяет устанавливать пакеты в локальную песочницу для проекта ● Не трогает остальную систему ● Ликвидирует Package Hell в среде разработки – А в Production его и так нет
  39. 39. Haskell● Инфраструктура разработки ● Hlint – Научит вас, как надо писать на Haskell – Находит дублирующий код и предлагает варианты исправления – Способен заменить не слишком въедливое Code Review ● Каким оно чаще всего и бывает (нехватка времени, лень, усталость, замыленность, нежелание брать ответственность за чужой код на себя) ● В Haskell практически отсутствует тип ошибок, которые может найти Code Review и не может найти компилятор – Использование неинициализированных данных, etc
  40. 40. Haskell● Инфраструктура разработки ● QuickCheck – Автоматическая генерация тестов ● Средства автоматического документирования – Haddock ● Просто запустите cabal haddock
  41. 41. Приближение III Заключение
  42. 42. Haskell● Удобный инструмент ● Академический язык для маргиналов ● Большое количество библиотек ● Не поддерживается в вашем IDE ● Удобный FFI – Простите, не поддерживается в чем? ● Хорошая инфраструктура разработки ● Ленивость ● Выразительный язык – Так было надо – Пишем меньше – Иногда, наоборот, полезна – Получаем больше – Когда она создаст реальные проблемы, я ● Сильная статическая типизация обязательно расскажу (пока просто нет такого опыта) – Безжалостно массово правим код, не боясь что-то сломать и не заметить ● Монады ● Конкурентность – А что “монады” ? – epoll/kqueue IO – А вы уверены, что точно знаете, что такое – STM “объект” и “класс” например? ● Поддержка SMP ● Но продолжаете ими пользоваться ● Либо ваше имя, вероятно, Luca Cardelli – Считай факториал в два раза быстрее на двух ядрах вместо одного, просто добавив вызов ● Сложная система типов функции par Зато многое позволяет – ● Поддержка UTF-8 ● Ограниченный вывод типов ● Метапрограммирование Местами заставляет расставлять аннотации – ● Хороший REPL ● И это правильно
  43. 43. Конец
  44. 44. Haskell● Напишем маленький HTTP сервер ● Пусть слушает на порту 10000 ● И отвечает HELLO WORLD import Happstack.Server main = do simpleHTTP nullConf { port = 10000 } $ ok "HELLO WORLD"
  45. 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. 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. 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
  48. 48. Haskell● Проверяем $ sudo hello start $ tail /var/log/daemon.log Apr 28 12:39:53 slim hello: starting $ ls /var/run/hello.pid /var/run/hello.pid $ curl http://localhost:10000 HELLO WORLD: 0$ curl http://localhost:10000 HELLO WORLD: 1$ curl http://localhost:10000 HELLO WORLD: 2$ curl http://localhost:10000 HELLO WORLD: 3$ curl http://localhost:10000 HELLO WORLD: 4$ curl http://localhost:10000 HELLO WORLD: 5$ curl http://localhost:10000 HELLO WORLD: 6$ curl http://localhost:10000 $ sudo kill -s USR1 `cat /var/run/hello.pid` $ tail /var/log/daemon.log Apr 28 12:44:11 slim hello: Counter is set to 0 $ curl http://localhost:10000 HELLO WORLD: 0
  49. 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))
  50. 50. Haskell● Проверим $sudo hello restart $ curl http://localhost:10000 HELLO WORLD: 0$ curl http://localhost:10000 HELLO WORLD: 1$ curl http://localhost:10000 HELLO WORLD: 2$ curl http://localhost:10000 HELLO WORLD: 3$ $ cat | psql test select * from counter; k | ts ---+---------------------------- 0 | 2011-04-28 14:15:58.294183 1 | 2011-04-28 14:15:59.252331 2 | 2011-04-28 14:15:59.843991 3 | 2011-04-28 14:16:00.323366 (4 rows)
  51. 51. Вот теперь всё. Спасибо за внимание
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×