從Java裡呼叫R – 使用Rserve
java中設定呼叫R語言-使用JRI方法,有一些弊端:
- R需要與JVM執行在同一臺機器上,當R需要執行大型計算時,會耗用大量CPU與記憶體,因此會影響到JVM的效能
- JRI的設定需要本地庫的支援,執行的時候還是有些麻煩的
Rserve可以解決這兩個問題,同時也有JRI一樣的易用性。它的原理是提供以HTTP方式連線的R語言介面, 因此可以讓專門一臺機器來執行RServe,然後讓Java或其它語言的客戶端來進行連線。額外的好處是,可以支援Java在內的多種客戶端。
Rserve推薦在Linux下使用,因為在Windows平臺上還很不完善(下面會介紹)。但由於多數程式設計師還是習慣於在Windows環境下進行開發,還是有必要找到在Windows上進行它的辦法。這篇文章主要介紹這個環境的話題。
安裝RServe
以下以Windows平臺為例介紹安裝的方法。Rserve可以從其網站下載單獨安裝,但更方便的方式是從R中直接進行安裝,只需執行
install.packages(‘Rserve’)
然後,需要手動複製$R_HOME/library/rserve/Rserve*.exe到bin/i386裡(視平臺而定)。
執行時,在命令列輸入:
R CMD Rserve
(說明:上述方法不可行時,使用如下方法開啟Rserve服務----在R控制檯下載入該包,然後輸入命令Rserve(),開啟伺服器,就可以供客戶端呼叫。)
你會得到它成功執行的提示。
要想關閉服務,最好的方法是從工作管理員裡直接殺死rserve.exe程序。我發現在命令列視窗按Ctrl-C似乎有時並不能成功關閉服務, 這個很奇怪。
使用與侷限
Windows上執行Rserve還是有很大的侷限性的。
最大的問題是,所有的連線都會在一個工作空間內執行。所以當客戶端是多執行緒程式時,可能會造成執行緒間互相干擾。我們需要在客戶端做一些額外的工作,使它能夠以執行緒安全的方式執行。Java端可以通過使用synchronize使得同一時間只有一個執行緒使用R服務。
Rserve的另一個缺點是,它的錯誤處理過於簡單化,如果R端出現錯誤,它丟擲的異常對於找到問題的根源幾乎沒有任何用處。所以我建議R端的程式一定要多寫LOG,或以統一的方式處理異常。
其次,開發中需要注意的是,Rserve同時只允許一個客戶端連線。因此,如果第二個執行緒試圖連線時,它就會一直處在等待狀態。另外,如果要更新R包,也需要關閉並重新啟動Rserve。所以,如果發現客戶端連線時出現異常情況,檢視一下工作管理員,看是不是有多個Rserve的執行緒在執行,或者你的服務有沒有重啟,往往可以幫助解決問題。
以下原始碼是一個使用Rserve實現執行緒安全的R服務的Java客戶端。包含了用Spring配置的RService物件, 以及相關的TestNG的測試。下載後需要做一些配置和修改才能使用,不過東西比較簡單,主要是演示這個idea。
示例
以下Java程式碼演示了使用上面zip包中的R服務物件來呼叫R語言來生成圖形的方法。
RService rService = new RService();
RConnection re = rService.startTransaction(); //開始獨佔工作空間
double[][] xx = ...; //生成兩個二維陣列
double[][] yy = ...;
File tempFile = null;
try {
re.assign("x", xx); //給R的變數賦值
re.assign("y", yy);
tempFile = File.createTempFile("test-", ".jpg"); //把影象生成到檔案
String filePath = tempFile.getAbsolutePath();
logger.info("Plot file to be produced:" + filePath);
re.eval("jpeg('" + StringEscapeUtils.escapeJava(filePath) + "')");
re.eval("qqplot(x,y)"); //繪圖
re.eval("dev.off()");
} catch (IOException e) {
logger.warn("Failed to create temp file", e);
} finally {
this.rService.endTransaction(); //退出時一定要關閉此工作空間
}