估计阅读时长: 5 分钟

https://github.com/rsharp-lang/Rserver

在R语言之中,存在有一个FastRWeb的框架可以将R语言编写的脚本以http服务的方式运行于后台,供其他的语言进行调用。在R#语言之中,我也模仿着R语言之中的FastRWeb框架,创建了一个用于R#语言的web服务的程序包框架。

HTTP协议(Hypertext Transfer Protocol)是建立在TCP协议基础上的一种文件传输协议。本质上,HTTP协议是一种CS模式的TCP网络协议,因为存在客户端和服务器端;但是我们更加通常的将其称作为BS模式,即浏览器端对服务器端(虽然我们通常进行REST请求的客户端并不是浏览器)。我们的浏览器进行网页浏览就是进行基于HTTP协议的文件下载操作:例如打开一个网页就是下载一个html文件以及对应的image,css,js等附件文件

关于HTTP协议的基础,可以阅读文章《从零开始手撸一个Web服务器》
R#脚本语言的语法,大家可以阅读这两篇博客做一些初步的学习:《R#语言简明教程》《R#语法教程之二》

使用Rhttp模块创建Web服务器

在R#语言之中,除了R#语言的Studio套件环境中的Rweb模块可以直接用来以http的方式执行我们的R#脚本代码。我们还可以通过使用Rhttp程序包模块所提供的Http相关的函数进行R#语言的web服务器的创建。在这里我们来主要介绍使用Rhttp的方式将R#脚本以Http服务的形式执行。

在Rhttp程序包模块之中,提供了一个http程序包,所以假若我们需要使用这个http程序包,会首先需要导入对应的模块,例如

imports "http" from "Rhttp";

之后R#的运行时环境中,就会导入了下面几个比较重要的可以用于进行Http操作相关的函数,给我们进行编程操作:

  • http_socket 这个函数为我们创建了一个最基本的HTTP服务的驱动程序模块
  • headers 这个函数可以为我们向HTTP的响应返回中添加默认的自定义消息头,例如版权信息等
  • httpMethod 这个函数是必须要调用的,因为我们必须要通过这个函数向HTTP服务驱动程序之中添加对应的请求处理函数才会构建出一个完整的Web服务。
  • listen 我们最后会需要通过这个函数来执行前面加载好的HTTP驱动程序模块。这个函数会需要传递一个端口号来监听来自浏览器的HTTP请求。

我们使用上面提到的R#脚本的HTTP服务的创建函数,可以通过下面的一段管道流程代码创建出一个相对完整的HTTP服务

http::http_socket()
|> headers(
  "X-Powered-By" = "R# web server",
  "Author"       = "xieguigang <xie.guigang@gcmodeller.org>",
  "Github"       = "https://github.com/rsharp-lang/Rserver",
  "Organization" = "R# language <https://github.com/rsharp-lang/>"
)
|> httpMethod("GET",  handleHttpGet)
|> httpMethod("POST", handleHttpPost)
|> httpMethod("PUT", [req, response] => writeLines("HTTP PUT test success!", con = response))
|> listen(port = httpPort)
;

可以看到,在上面所示的R#脚本代码示例之中,我们:

  • 首先通过http_socket函数创建了一个HTTP服务的驱动程序;
  • 接着添加自己的自定义HTTP消息头
  • 再接着呢,通过httpMethod函数添加对应类型的请求处理函数
  • 最后通过listen函数启动HTTP服务

处理HTTP请求

我们再前面的代码仅仅是创建了一个空的HTTP的服务框架,即里面是不包含有任何的Web请求的处理代码的。所以我们在这里介绍怎样通过httpMethod函数向我们的Web服务器框架之中添加对应的请求处理代码。

对于httpMethod函数而言,其主要接受三个参数:

  • driver 参数为我们之前所创建的HTTP服务的驱动程序
  • method 制定了当前所传入的处理函数所对应的目标HTTP方法,例如GET/POST/PUT等
  • 最后最重要的一个参数就是process参数,我们必须要传递一个R#函数才行

例如

http::http_socket()
|> httpMethod("PUT", [req, response] => writeLines("HTTP PUT test success!", con = response))
;

上面的示例代码,我们通过一个lambda函数,直接向浏览器返回了一条固定的HTTP响应消息,例如下面是运行的效果

可以看到,我们传递的lambda函数已经正确的响应了来自于浏览器的PUT请求。

FastRWeb服务示意

在这里我为大家演示,如何通过GET请求执行一个R#脚本代码,用来实现一个类似于在R语言之中的FastRWeb服务的效果。

我们希望我们的R#脚本可以通过URL来执行对应的R#脚本文件,则我们会首先需要将URL逻辑地址转换为目标R#脚本在本地的物理路径,这个转换过程我们是会需要通过getUrl函数来实现的,例如:

#' Route url as local R script file
#'
#' @param url the url object that parsed from the
#'     http request.
#'
const router as function(url) {
  `${webContext}/${ trim(url$path, ".") }.R`;
}

const R as string = router(getUrl(req));

上面的代码示例中的req对象就是来自于浏览器的HTTP请求数据,我们会需要通过getURL函数得到一个URL对象。在URL对象之中,我们这里主要是使用其path数据,例如http://localhost/ping的path数据就是/ping。在添加了.R文件后缀名之后,这样子我们就可以将前面所提到的URL转换为一个本地的R#脚本文件路径。

我们可以通过str函数查看getURL得到的URL解析数据,例如:

[1] "request from the browser client:"
List of 7
 $ url      : chr "http://localhost:80/ping?#"
 $ path     : chr "ping"
 $ hostName : chr "localhost"
 $ hash     : <NULL>
 $ query    :  ..list()
 $ port     : int 80
 $ protocol : chr "http://"

接着我们就可以source所转换得到的目标R#脚本文件了。因为在R#环境之中,source函数会返回目标脚本的最后一段表达式的结果值,所以我们可以直接将source函数的返回结果作为HTTP的响应输出返回给浏览器端。在R#脚本之中,对于文本数据,几乎可以使用writeLines函数进行所有处理。我们在这里直接将返回给浏览器的HTTP响应作为目标文件,使用writeLines函数写入响应的结果。所以整个的脚本执行以及HTTP响应的处理代码可以如下所示:

# run R script and then send source evaluation result
# response back to the browser
writeLines(source(R), con = response);

假若我们的目标脚本文件找不到呢?别急,这个时候我们就可以使用httpError函数返回对应的HTTP错误代码了:

response
|> httpError(404, `the required Rscript file is not found on filesystem location: '${ normalizePath(R) }'!`)
;

可以看到,上面的一段错误处理代码,如果我们尝试访问一个根本就不存在的R#脚本的话,错误处理代码已经正确的将错误反馈给了谷歌浏览器了。

服务器的ping/pang测试

在这里,我创建了一个最简单的脚本进行我们所创建的Web服务器的ping/pang测试。在这段脚本之中,仅包含有最简单的一个pang!字符串。因为source函数会将目标脚本的最后一条表达式的求值结果返回,所以我们的ping.R脚本肯定总是会返回pang!这个字符串给前面所示的writeLines函数的。

# https://github.com/rsharp-lang/Rserver/blob/bf09d25923da4d3852702ae496d579bf408554cc/web.R/ping.R
"pang!";

将上面的脚本代码保存在ping.R文件之中,然后启动我们的R#网页服务器。在浏览器中请求http://localhost/ping

很好,我们自己通过R#脚本创建的Web服务器已经正确的向浏览器响应了一个pang!。一切都与我们所期待的那样,工作正常。

命令行调用方法

上面的完整的脚本代码,大家可以进入到Github代码库中查看:https://github.com/rsharp-lang/Rserver/blob/main/script/http.R。这个写好的R#脚本的Web服务器的命令行使用方法非常简单,我们通过R#命令查看脚本的命令行使用方法:

可以看到这个脚本的使用方法非常的简单,就只需要告诉这个脚本我们的Web服务器监听哪一个端口号,需要执行的脚本的位置在哪一个文件夹之中就可以了。

谢桂纲
Latest posts by 谢桂纲 (see all)

Attachments

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *

博客文章
October 2024
S M T W T F S
 12345
6789101112
13141516171819
20212223242526
2728293031  
  1. 在mysql之中,针对24小时内的数据按照半个小时进行一次统计数量: ```sql SELECT DATE_FORMAT(FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(add_time) / 1800) * 1800), '%Y-%m-%d %H:%i') AS half_hour, COUNT(*) AS count FROM user_track.page_view WHERE add_time >=…

  2. 针对图对象进行向量化表示嵌入: 首先,通过node2vec方法,将node表示为向量 第二步,针对node向量矩阵,进行umap降维计算,对node进行排序,生成node排序序列 第三步,针对node排序序列进行SGT序列图嵌入,实现将网络图对象嵌入为一维向量