Kategorie

Nejnovější článek

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 vychází z vlastností jazyka Haskell, který má pro tvorbu vysoce spolehlivých systémů výborné předpolady.

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