2013-04-08 10 views
6

Stworzyłem serwer używając wieżę w R - http://cran.r-project.org/web/packages/Rook Kod jest następującyRun R/Rook jako serwer WWW na starcie

#!/usr/bin/Rscript 
library(Rook) 
s <- Rhttpd$new() 
s$add(
    name="pingpong", 
    app=Rook::URLMap$new(
    '/ping' = function(env){ 
     req <- Rook::Request$new(env) 
     res <- Rook::Response$new() 
     res$write(sprintf('<h1><a href="%s">Pong</a></h1>',req$to_url("/pong"))) 
     res$finish() 
    }, 
    '/pong' = function(env){ 
     req <- Rook::Request$new(env) 
     res <- Rook::Response$new() 
     res$write(sprintf('<h1><a href="%s">Ping</a></h1>',req$to_url("/ping"))) 
     res$finish() 
    }, 
    '/?' = function(env){ 
     req <- Rook::Request$new(env) 
     res <- Rook::Response$new() 
     res$redirect(req$to_url('/pong')) 
     res$finish() 
    } 
) 
) 
## Not run: 
s$start(port=9000) 

$ ./Rook.r 
Loading required package: tools 
Loading required package: methods 
Loading required package: brew 
starting httpd help server ... done 

Server started on host 127.0.0.1 and port 9000 . App urls are: 

    http://127.0.0.1:9000/custom/pingpong 
Server started on 127.0.0.1:9000 
[1] pingpong http://127.0.0.1:9000/custom/pingpong 

Call browse() with an index number or name to run an application. 
$ 

A proces kończy się tutaj.

Działa poprawnie w powłoce R, ale chcę go uruchomić jako serwer podczas uruchamiania systemu. Tak więc po wywołaniu startu R nie powinno wychodzić, ale czekać na żądania na porcie. W jaki sposób przekonam R, aby po prostu czekała lub spała, zamiast wychodzić? Mogę użyć funkcji oczekiwania lub uśpienia w R, aby odczekać około N sekund, ale to nie pasuje idealnie do rachunku

Odpowiedz

1

Oto jedna sugestia:

Pierwszy rozłam przykład dałeś do (przynajmniej) dwa pliki: jeden plik zawiera definicję aplikacji, które w swoim przykładem jest wartość parametru app do funkcji Rhttpd$add(). Drugi plik to RScript, który uruchamia aplikację zdefiniowaną w pierwszym pliku.

Na przykład, jeśli nazwa funkcji aplikacji jest nazwany pingpong zdefiniowane w pliku o nazwie Rook.R, wówczas Rscript może wyglądać:

#!/usr/bin/Rscript --default-packages=methods,utils,stats,Rook 

# This script takes as a single argument the port number on which to listen. 

args <- commandArgs(trailingOnly=TRUE) 

if (length(args) < 1) { 
     cat(paste("Usage:", 
       substring(grep("^--file=", commandArgs(), value=T), 8), 
       "<port-number>\n")) 
     quit(save="no", status=1) 
} else if (length(args) > 1) 
    cat("Warning: extra arguments ignored\n") 

s <- Rhttpd$new() 
app <- RhttpdApp$new(name='pingpong', app='Rook.R') 
s$add(app) 
s$start(port=args[1], quiet=F) 

suspend_console() 

Jak widać, ten skrypt ma jedną argument określający port nasłuchiwania. Teraz możesz utworzyć skrypt powłoki, który będzie wielokrotnie wywoływał ten kod, aby uruchomić wiele instancji serwera nasłuchujących na różnych portach, aby umożliwić pewną współbieżność odpowiedzi na żądania HTTP.

Na przykład, jeśli Rscript powyżej znajduje się w pliku o nazwie start.r wówczas taki skrypt może wyglądać mniej więcej tak:

#!/bin/sh 

if [ $# -lt 2 ]; then 
    echo "Usage: $0 <start-port> <instance-count>" 
    exit 1 
fi 

start_port=$1 
instance_count=$2 
end_port=$((start_port + instance_count - 1)) 

fifo=/tmp/`basename $0`$$ 

exit_command="echo $(basename $0) exiting; rm $fifo; kill \$(jobs -p)" 
mkfifo $fifo 

trap "$exit_command" INT TERM 

cd `dirname $0` 

for port in $(seq $start_port $end_port) 
    do ./start.r $port & 
done 

# block until interrupted 
read < $fifo 

Powyższy skrypt przyjmuje dwa argumenty: (1) najniższy portu -Numer do nasłuchu i (2) liczba instancji do uruchomienia. Na przykład, jeśli skrypt jest w pliku wykonywalnego o nazwie start.sh następnie

./start.sh 9000 3 

rozpoczną trzy instancje swojej Rook aplikacji nasłuchuje na portach 9000, 9001 i 9002, odpowiednio.

Widzisz ostatni wiersz skryptu powłoki czyta z fifo, który uniemożliwia skryptowi wyjście, dopóki nie zostanie wywołany przez odebrany sygnał. Gdy jeden z określonych sygnałów zostanie uwięziony, skrypt powłoki zabije wszystkie procesy serwera, które zostały uruchomione przed jego wyjściem.

Teraz możesz skonfigurować odwrotne proxy, aby przekazywać przychodzące żądania do dowolnej instancji serwera.Na przykład, jeśli używasz Nginx, konfiguracja może wyglądać mniej więcej tak:

upstream rookapp { 
    server localhost:9000; 
    server localhost:9001; 
    server localhost:9002; 
} 
server { 
    listen your.ip.number.here:443; 
    location /pingpong/ { 
     proxy_pass http://rookapp/custom/pingpong/; 
    } 
} 

Następnie usługa może być dostępna w publicznej sieci Internet.

Ostatnim krokiem jest stworzenie skrypt sterujący z opcjami takimi jak start (aby wywołać powyższy skrypt) i stop (aby wysłać mu sygnał do zatrzymania TERM serwerów). Taki skrypt obsłuży takie rzeczy, jak uruchomienie skryptu powłoki jako demona i śledzenie jego numeru identyfikacyjnego procesu. Zainstaluj ten skrypt sterujący w odpowiedniej lokalizacji i uruchomi on serwery aplikacji podczas uruchamiania. Sposób wykonania zależy od systemu operacyjnego, którego tożsamości brakuje w pytaniu.

Uwagi

  1. Na przykład, jak kolejka w skrypcie powłoki mogą być wykorzystywane do podejmowania różnych działań na podstawie odebranych sygnałów, patrz this stack overflow question.

  2. Jeffrey Horner podał przykład a complete Rook server application.

  3. Zobaczysz, że przykładowy skrypt powłoki powyżej pułapek tylko sygnały INT i. Wybrałem te, ponieważ INT wynika z wpisania control-C na terminalu i TERM jest sygnałem używanym przez skrypty sterujące w moim systemie operacyjnym, aby zatrzymać usługi. Możesz dostosować wybór sygnałów do pułapki w zależności od okoliczności.

0

Czy próbowałeś tego?

while (TRUE) { 
    Sys.sleep(0.5); 
}