Kategorie
Nejnovější článek
- Building a shared library in Haskell
- Real World Haskell se šíří Brnem
- Benchmarky pro quad-core na shootoutu
- fotopaste.cz: webová služba psaná v Haskellu
- Haskell a Ruby on Rails II: sessions
- Google summer of code 2008
- Haskell a Ruby on Rails I.
Archiv
Odkazy
Hledání
Haskell a Ruby on Rails I.
Během práce na jednom projektu v Ruby on Rails jsem narazil na závažný problém tohoto řešení. Interpret ruby je neskutečně pomalý a Ruby on Rails, hlavně starší verze, která se používala, na celkové rychlosti zrovna nepřidává. Protože to je velký projekt, přepsat to kompletně v něčem jiném nepřicházelo v úvahu.
Takže jsem si začal pohrávat s myšlenkou vytvořit fastcgi se službami, které se budou pomocí AJAXu (zdravíme Web 2.0 fanatiky a milovníky moderních buzwordů) volat z railsové aplikace. Zmiňované fastcgi aplikace je psaná v Haskellu.
Tohle řešení má několik výhod
- spolehlivost
- rychlost provádění (zejména v porovnání s jazyky, které se pro tvorbu webů běžně používají)
- relativně nízká paměťová náročnost
Spolehlivost vychází z vlastností jazyka Haskell, který má pro tvorbu vysoce spolehlivých systémů výborné předpolady.
- Má silnou a statickou typovou kontrolu,
- je vysokoúrovňový (méně kódu, méně chyb, automatická správa paměti, ...),
- je kompilovaný -- spousta chyb se na rozdíl od Ruby odhalí už při překladu, často to funguje ve spojení s typovou kontrolou stylem "když už se to celý podaří zkompilovat, tak už to funguje správně" a
- existují nástroje a knihovny pro zajištění spolehlivosti (QuickCheck, Catch, HPC, ...).
A tak jsem se jal navrhovat
Vytvořil jsem si ve struktuře railsového projektu v adresáři app nový adresář app/services, ve kterém budu tvořit mé dílo. Než se do toho pustím (potažmo pustíme), budu potřebovat
to bude prozatím stačit. Později si přidám minimálně knihovnu pro práci s databází.
Tak si prozatím vyrobme nějaký ten hello world. Bez zbytečných okolků na vás vystrčím obsah app/services/Main.hs:
module Main where import Network.FastCGI import Data.Maybe (fromMaybe) -- spusti smycky obsluhujici fastcgi dotazy main = runFastCGI handleCommands handleCommands :: CGI CGIResult handleCommands = do cmd <- getInput "command" outStr <- case cmd of -- zde bude obsluha nasich prikazu --------------------------- Just "hello" -> return "Hello, world!" Just "echo" -> processEcho -------------------------------------------------------------- Just c -> return $ "Neznamy prikaz " ++ show c Nothing -> return "Zadej prikaz, hulvate!" setHeader "Content-type" "text/html" output outStr processEcho = do str <- getInput "text" return $ fromMaybe "N/A" st
Jak je nejspíš po prvním pohledu patrné, funkce, která se vykonává při kažném požadavku, je handleCommands. V main se volá funkce runFastCGI, která se postará o správnou spolupráci s webovým serverem. Načteme parametr command, kterým určujeme, kterou funkci z budoucí services.fcgi chceme volat. Na základě příkazu a dalších případných parametrů provedeme akci a vrátíme řetězec, který se předá zpět prohlížeči. Může jít o obyčejný text, html nebo třeba JSON.
Dál kód vysvětlovat nebudu, protože je jednoduchý a hlavně už pomalu půjdu spát .-)
Ještě to ale rozběháme
ghc --make Main.hs -o ../../public/services.fcgi
Pokud máte Main.hs v app/services/, stejně jako já, pak tento příkaz vyrobí services.fcgi v adresáři public. Pak si přidáme do konfiguračního souboru webserveru config/lighttpd.conf obsluhu services.fcgi.
fastcgi.server = ( "/services.fcgi" => ( "localhost" => ( "min-procs" => 1, "max-procs" => 4, "socket" => CWD + "/tmp/sockets/services.socket", "bin-path" => CWD + "/public/services.fcgi", ) ) , ".fcgi" => ( "localhost" => ( "min-procs" => 2, "max-procs" => 4, "socket" => CWD + "/tmp/sockets/fcgi.socket", "bin-path" => CWD + "/public/dispatch.fcgi", "bin-environment" => ( "RAILS_ENV" => "development" ) ) ) )
A pokud všechno šlo, jak má, pak je teď (tedy až po spuštění serveru pomocí lighttpd -f config/lighttpd.conf) možné volat služby ze services.fcgi. Volání v railsovém viewu může vypadat např. takto (pro jednoduchost použiji pro ajaxové volání javscriptový framework Prototype):
<script type="text/javascript"> function hello() { new Ajax.Updater("hello", "/services.fcgi", { asynchronous: true, method: 'post', postBody: 'command=hello', evalScripts: false}); } </script> ... <span id="hello" style="border: 1px solid" onclick="hello();">klik</span> ...
Po kliknutí na text "klik", se zavolá asynchronně services.fcgi s parametrem "command=hello". Ve funkci handleCommnads se získá hodnota command a vrátí se text "Hello, world!". S tim si Ajax.Updater naloží po svém, tedy nahradí jím obsah elementu s id=hello. Takže ve výsledku se tedy změní text "klik" na text "Hello, world!".
Trochu odlišný příklad spolu s kompletními zdrojovými kódy jsem popsal i na wiki haskell.cz.
Co dál?
Příště se podíváme na nějaká vylepšení. Je potřeba pracovat s prvky formulářů, také s databází, jakou používá railsová aplikace, je potřeba pomocí services.fcgi renderovat html či updatovat více elementů na stránce, pracovat se sešnama společně s railsovou aplikací a podobně...
Autor: jh
Květen 2008
Přidání komentáře