APE: Comet serwer oraz kompletny framework javascript. Kompleksowe rozwiązanie Ajax Push.



Coraz popularniejszym rozwiązaniem w serwisach internetowych jest stosowanie modelu komunikacji Comet, czyli rozwiązania opartego na JavaScript i obiekcie XMLHttpRequest umożliwiające komunikacji server <-> client metodą push. Do niedawna stosowanie metody push w aplikacjach webowych wymagało przeważnie instalacji dedykowanego apletu Javy, który dbał o komunikację tego rodzaju. Rozwiązanie Comet umożliwia komunikację typu push z pominięciem instalacji niestandardowych dodatków do przeglądarki.

Comet opiera się na połączeniach typu long-polling, czyli nawiązanie "stałego" połączenia typu
XMLHttpRequest z serwerem. Rozłączenie oraz jego ponowne zestawienie następuje w dwóch przypadkach:

  • gdy serwer zwraca / "wysyła" wiadomość,
  • gdy upłynął limit czasu połączenia, który zdefiniowany jest w ramach rozwiązania Commet.


Z modelu komunikacji Comet korzysta między innymi Facebook w ramach funkcjonalności czat. Połączenia typu long-polling łatwo zauważyć włączając konsolę FireBug.   

APE (Ajax Push Engine) to kompleksowe rozwiązanie Ajax Push udostępnione na licencji GNU GPL. Zawiera serwer Comet oraz kompletny framework JavaScript. Rozwiązanie to w stosunkowo prosty sposób pozwoli nam na implementację rozwiązania push we własnym serwisie.

Jedną z największych zalet APE jest możliwość budowania własnych tzw. komend oraz modułów obsługujących żądania oraz odpowiedzi po stronie serwera. Do większości zastosowań wystarczy dostarczony razem z serwerem moduł 'inlinepush', jednak mamy zupełną dowolność w napisaniu własnego, który spełni nasze oczekiwania. Komendy oraz moduły pracujące po stronie serwera pisane są w języku JavaScript z wykorzystaniem silnika SpiderMonkey. Tworzy się je naprawdę prosto.

APE, to projekt bardzo intensywnie rozwijany. Bardzo podoba mi się otwartość twórców na uwagi i propozycje otrzymane od użytkowników. Na stronie http://www.ape-project.org/demos/6/tcpsocket-demo-irc.html dostępny jest czat (oczywiście oparty na APE, jakże by inaczej), na którym można spotkać członków projektu.

Na minus niestety muszę zaliczyć nieaktualną dokumentację. Pamiętam, gdy pierwszy raz próbowałem uruchomić APE zgodnie z dokumentacją, spędziłem pół dnia na odpowiedniej konfiguracji serwera proxy na Apache, po czym okazało się, że dokumentacja dotyczy wersji 0.9, natomiast aktualną była wówczas 1.0, która proxy nie wymaga ... Również opis API frameworka JavaScript był często nieaktualny. Mam nadzieję, że obecnie się to poprawiło ...

Mimo kilku mniej znaczących wad,  APE to najlepsze kompleksowe rozwiązanie Comet jakie znalazłem.

Pora spróbować zainstalować i skonfigurować własny serwer push.
Poniższe przykłady dotyczą wersji APE 1.0

Pierwszym krokiem jest skompilowanie w środowisku Linux serwera APE. Następnie przechodzimy do konfiguracji serwera. Poniżej przykładowa konfiguracja pliku bin/ape.conf:

Server {
        port = 900
        daemon = yes
        ip_listen = 0.0.0.0
        domain = www.mojadomena.com
        rlimit_nofile = 10000
        pid_file = /var/run/aped_emultipoetry.pid
}

 Najważniejszymi zmiennymi są:

  • domain- musi być analogiczna, jak ta w której działa nasz serwis

  • port - port na którym będzie działał serwer. Domyślnym portem jest 6969, jednak sugeruję jego zmianę na inny, gdyż jest to przestrzeń w której pracuje jedna z sieci P2P

  • pid_file - w pliku tym będzie się znajdował identyfikator uruchamianej instancji procesu (przydatne np. przy automatyzacji restartu serwera)

Następnie przechodzimy do pliku scripts/main.ape.js, gdzie znajduje się lista ładowanych przez serwer modułów i komend w formie skryptów JS. W naszym przykładzie jednymi z użytych komend będą "inlinepush" oraz moduł "nickname". (Uwaga, niektóre z modułów mogą się ze sobą gryźć).

Po skonfigurowaniu serwera pora go uruchomić.

Przechodzimy do kodu serwisu.

Jak pisałem wcześniej, APE to nie tylko serwer Comet, ale również kompletny framework JavaScript. Biblioteki frameworka przegrywamy do katalogu serwisu /js/ape/ (ważne, aby były dostępne z tej samej domeny, w której dostępny jest nasz serwis).
W HEAD dokumentu dołączamy bibliotekę:

<script src="/js/ape/JavaScript.js" type="text/javascript" charset="utf-8"></script>

Pora teraz zapiąć mechanizm. Poniżej przykładowa biblioteka JavaScript oparta na JQuery.

ChatWall = {
   chat_channel: 'chatwall',
   user_nick: 'nick',
    
    init: function(_selector){

        APE.Config.baseUrl = 'http://www.mojadomena.com/';
        APE.Config.domain = 'www.mojadomena.com'; 
        APE.Config.server = 'www.mojadomena.com:900'; 
        
        client = new APE.Client,
        client.load({
            domain: APE.Config.domain,  
         server: APE.Config.server, 
            identifier: 'action',  
            channel: ChatWall.chat_channel, 
            complete: function(ape){ 
                new ChatWall.response(ape).initialize(_selector);  
          }, 
         scripts: [
                APE.Config.baseUrl + 'js/ape/source/mootools-core.js', 
                APE.Config.baseUrl + 'js/ape/source/Core/APE.js', 
                APE.Config.baseUrl + 'js/ape/source/Core/Events.js', 
                APE.Config.baseUrl + 'js/ape/source/Core/Core.js', 
                APE.Config.baseUrl + 'js/ape/source/Core/JSON.js',
                APE.Config.baseUrl + 'js/ape/source/Core/Utility.js',
                APE.Config.baseUrl + 'js/ape/source/Pipe/Pipe.js', 
                APE.Config.baseUrl + 'js/ape/source/Pipe/PipeProxy.js', 
                APE.Config.baseUrl + 'js/ape/source/Pipe/PipeMulti.js', 
                APE.Config.baseUrl + 'js/ape/source/Pipe/PipeSingle.js', 
                APE.Config.baseUrl + 'js/ape/source/Request/Request.js',
                APE.Config.baseUrl + 'js/ape/source/Request/Request.Stack.js', 
                APE.Config.baseUrl + 'js/ape/source/Request/Request.CycledStack.js', 
                APE.Config.baseUrl + 'js/ape/source/Transport/Transport.longPolling.js',
                APE.Config.baseUrl + 'js/ape/source/Transport/Transport.SSE.js', 
                APE.Config.baseUrl + 'js/ape/source/Transport/Transport.XHRStreaming.js', 
                APE.Config.baseUrl + 'js/ape/source/Transport/Transport.JSONP.js'
            ]
        });
    },
    
    response: function(ape){
        
        this.initialize = function(selector){
            ape.selector = selector;
            ape.onRaw('data', this.onMsg);
            ape.start({name: ChatWall.user_nick})
        }
            
        this.onMsg = function(raw){
            var msg = decodeURIComponent(raw.data.content); 
            $(ape.selector).html(msg);
            var objDiv = document.getElementById(ape.selector.replace(/#/, ''));
            objDiv.scrollTop = objDiv.scrollHeight;
        }

    }
        
}

 

Obiekt ChatWall ma dwie ważne zmienne:

  • chat_channel - to nazwa kanału do którego przyłączy się użytkownik. Praktycznie może być to dowolna nazwa, np. identyfikator użytkownika. Należy jedynie pamiętać, że wysyłane komendy push do konkretnego kanału, będą wysyłane do wszystkich użytkowników, którzy są z nim połączeni.
  • user_nick- w tym konkretnym przypadku jest to nick użytkownika, który przesyłany jest do serwera w momencie połączenia. Dane te będą wysyłane do innych klientów na danym kanale w momencie połączenia oraz innych zdarzeń (np. opuszczenie kanału przez użytkownika).

Wyżej napisałem, że APE umożliwia napisanie własnych komend i modułów po stronie serwera. W przykładzie powyżej w momencie połączenia klienta przesyłany jest obiekt ze zmienną "name" ( ape.start({name: ChatWall.user_nick}) ) i jest on po stronie serwera obłużony przez wbudowany moduł nickname.js (dołączony w konfiguracji serwera scripts/main.ape.js). Moduł ten obsługuje dwa zdarzenia: połączenie klienta z kanałem oraz jego opuszczenie. Nic nie stoi na przeszkodzie, aby napisać własny moduł, który przyjmuje i obsługuje inne parametry.

 

Powyżej to jedynie biblioteka. Aby uruchomić połączenie wywołujemy: 

ChatWall.chat_channel = 'nazwa_kanalu';
ChatWall.user_nick = 'nick';
ChatWall.init('#chat-wall-content');

'#chat-wall-content' to identyfikator objektu DOM, który jest obsługiwany przez bibliotekę ChatWall. Do tego obiektu będą dodawane komunikaty z serwera, za pośrednictwem metody ChatWall.response().

Powyższy przykład pokazuje jedynie, jak połączyć się z serwerem push i "nasłuchiwać" komunikatów. Jak wysłać komunikat na kanał, który następnie zostanie wysłany do połączonych użytkowników? Możemy do tego użyć PHP. Poniżej prosty skrypt, który wysyła do serwera APE komunikat:

$body = (!empty($_POST["message"])) ? trim($_POST["message"]) : 'domyslny';
    
$APEserver = 'http://localhost:900';
$APEPassword = 'password'; 
$APEChannel = 'nazwa_kanalu';

             
$url = $APEserver.'/0/?';
$cmd = array(array(
	'cmd'    =>    'inlinepush',
	'params'=>    array(
		'password'    => $APEPassword,
		'raw'         => 'data',
		'channel'     => $APEChannel,
		'data'        => array(
			'content'      => $body
		)
	)
));
                
$url_query = $url . urlencode(json_encode($cmd));
            
$c = curl_init();
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_URL, $url_query);
$data = curl_exec($c);
curl_close($c);

if ($data){
	$data = json_decode($data);
	$data = array_shift($data);
                
	if ($data->raw == 'ERR' && $data->data->code == 401){
		user_error($data->data->value);
	} elseif ($data->raw != 'pushed'){
		user_error($data->data->value);
	}
} else {
	user_error("Empty response!");
}

print json_encode($out_data);

gdzie:

  • $APEserver - host serwera APE. localhost przy założeniu, że skrypt PHP będzie uruchamiany na tej samem maszynie.
  • $APEPassword - hasło autoryzacyjne żądanią używane przez komendę inlinepush. Domyślnie znajduje się ono w pliku modules/conf/inlinepush.cfg na serwerze.

Jak widać dane przesyłane (odbierane przez klienta również) są w formie obiektu JSON. W parametrach wywołania warto zwrócić uwagę na zmienne:

  • cmd - wywoływana komenda po stronie serwera APE
  • raw - kanał danych (nie mylić z kanałem połączenia 'channel', o którym pisałem wyżej), obsługiwany przez klienta JS (w metodzie ChatWall.response())
  • data - przesyłane dane w dowolnej strukturze, które oczywiście muszą być obsłużone po stronie przykładowej biblioteki JS.

 


Pora na testy testów.

Uruchomimy stronę z wywołaniem powyższych metod JS. Aby sprawdzić, czy połączenie klienta jest nawiązane, wystarczy obserwować konsolę w FireBug. Następnie np. w drugim oknie przeglądarki wywołajmy skrypt PHP, który prześle dane do serwera APE. Treść komunikatu powinna się wyświetlić w obiekcie  DOM '#chat-wall-content' na stronie serwisu.


Powodzenia!

 

 

 

 



Proszę czekać...
Nie możesz komentować. Mariusz Turek umieścił Cię na czarnej liście lub Twoje konto nie jest aktywne.