Run R/Rook as a web server on startup

◇◆丶佛笑我妖孽 提交于 2019-12-09 11:32:40

问题


I have created a server using Rook in R - http://cran.r-project.org/web/packages/Rook Code is as follows

#!/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.
$ 

And the process ends here.

Its running fine in the R shell but then i want to run it as a server on system startup. So once the start is called , R should not exit but wait for requests on the port. How will i convince R to simply wait or sleep rather than exiting ? I can use the wait or sleep function in R to wait some N seconds , but that doesnt fit the bill perfectly


回答1:


Here is one suggestion:

First split the example you gave into (at least) two files: One file contains the definition of the application, which in your example is the value of the app parameter to the Rhttpd$add() function. The other file is the RScript that starts the application defined in the first file.

For example, if the name of your application function is named pingpong defined in a file named Rook.R, then the Rscript might look something like:

#!/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()

As you can see, this script takes one argument that specifies the listening port. Now you can create a shell script that will invoke this Rscript multiple times to start multiple instances of your server listening on different ports in order to enable some concurrency in responding to HTTP requests.

For example, if the Rscript above is in a file named start.r then such a shell script might look something like:

#!/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

The above shell script takes two arguments: (1) the lowest port-number to listen on and (2) the number of instances to start. For example, if the shell script is in an executable file named start.sh then

./start.sh 9000 3

will start three instances of your Rook application listening on ports 9000, 9001 and 9002, respectively.

You see the last line of the shell script reads from the fifo which prevents the script from exiting until caused to by a received signal. When one of the specified signals is trapped, the shell script kills all the Rook server processes that it started before it exits.

Now you can configure a reverse proxy to forward incoming requests to any of the server instances. For example, if you are using Nginx, your configuration might look something like:

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/;
    }
}

Then your service can be available on the public Internet.

The final step is to create a control script with options such as start (to invoke the above shell script) and stop (to send it a TERM signal to stop your servers). Such a script will handle things such as causing the shell script to run as a daemon and keeping track of its process id number. Install this control script in the appropriate location and it will start your Rook application servers when the machine boots. How to do that will depend on your operating system, the identity of which is missing from your question.

Notes

  1. For an example of how the fifo in the shell script can be used to take different actions based on received signals, see this stack overflow question.

  2. Jeffrey Horner has provided an example of a complete Rook server application.

  3. You will see that the example shell script above traps only INT and TERM signals. I chose those because INT results from typing control-C at the terminal and TERM is the signal used by control scripts on my operating system to stop services. You might want to adjust the choice of signals to trap depending on your circumstances.




回答2:


Have you tried this?

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


来源:https://stackoverflow.com/questions/15872440/run-r-rook-as-a-web-server-on-startup

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!