我在R中使用Rook创建了一个服务器 - http://cran.r-project.org/web/packages/Rook 代码如下
#!/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.
$
这个过程就此结束。
它在R shell中运行良好,但后来我想在系统启动时将其作为服务器运行。 因此,一旦调用了start,R就不应该退出,而是等待端口上的请求。 我如何说服R只是等待或睡觉而不是退出? 我可以使用R中的等待或睡眠功能等待N秒,但这并不完全符合法案
答案 0 :(得分:1)
这是一个建议:
首先将您提供的示例拆分为(至少)两个文件:一个文件包含应用程序的定义,在您的示例中,该定义是app
函数的Rhttpd$add()
参数的值。另一个文件是 RScript ,用于启动第一个文件中定义的应用程序。
例如,如果应用程序函数的名称在名为pingpong
的文件中定义为Rook.R
,则 Rscript 可能如下所示:
#!/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()
如您所见,此脚本采用一个指定侦听端口的参数。现在,您可以创建一个shell脚本,该脚本将多次调用此 Rscript ,以启动服务器侦听不同端口的多个实例,以便在响应HTTP请求时启用某些并发。
例如,如果上面的 Rscript 位于名为start.r
的文件中,那么这样的shell脚本可能类似于:
#!/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
上面的shell脚本有两个参数:(1)要侦听的最低端口号和(2)要启动的实例数。例如,如果shell脚本位于名为start.sh
的可执行文件中,则
./start.sh 9000 3
将启动 Rook 应用程序的三个实例,分别在端口9000,9001和9002上进行侦听。
您会看到shell脚本的最后一行从fifo读取,这会阻止脚本在收到信号之前退出。当捕获其中一个指定信号时,shell脚本将终止它在退出之前启动的所有 Rook 服务器进程。
现在,您可以配置反向代理以将传入请求转发到任何服务器实例。例如,如果您使用Nginx,则您的配置可能如下所示:
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/;
}
}
然后您的服务可以在公共互联网上使用。
最后一步是创建一个控制脚本,其中包含start
(用于调用上述shell脚本)和stop
等选项(向其发送TERM
信号以停止服务器)。这样的脚本将处理诸如使shell脚本作为守护进程运行并跟踪其进程ID号之类的事情。将此控制脚本安装在适当的位置,它将在计算机启动时启动 Rook 应用程序服务器。如何做到这将取决于您的操作系统,您的问题中缺少其身份。
有关如何使用shell脚本中的fifo根据收到的信号采取不同操作的示例,请参阅this stack overflow question。
Jeffrey Horner提供了一个a complete Rook server application的例子。
您将看到上面的示例shell脚本仅捕获INT
和TERM
信号。我选择了那些因为INT
来自终端输入control-C而TERM
是我的操作系统上的控制脚本用来停止服务的信号。您可能需要根据具体情况调整陷阱信号的选择。
答案 1 :(得分:0)
你试过这个吗?
while (TRUE) {
Sys.sleep(0.5);
}