You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R Plumber结合Future Promise:无法将变量赋值至全局环境的问题

问题:Plumber异步接口无法将变量写入全局环境

当使用future::plan("multisession")运行Plumber API时,未使用future的test1接口能成功将Test1写入全局环境,但使用future异步执行的test2接口无法将Test2写入全局环境。

复现代码

主运行脚本

### Set the asynchronous coding
library(promises) ; library(future)
future::plan("multisession")

## Plumber app
library(plumber)

pr <- pr("APIs_TestAsync.R")
pr %>% pr_run()

APIs_TestAsync.R脚本

#* Test 1
#* @get test1
#* @serializer unboxedJSON
#* @tag TestFuture
function() {
  
  # Create a dataset
  df<-data.frame(A=c("a", "a", "a", "b", "b", "c"), B=c(1,2,3,4,5,6))
  assign("Test1", df, .GlobalEnv)

  return(
    list(SUM=sum(df$B))
    )
  }


#* Test 2
#* @get test2
#* @serializer unboxedJSON
#* @tag TestFuture
function() {
  
  # Create a future promise
  Prom<-future({
    
      # Wait for 10 seconds (needed to test the asynchronous functions)
      Sys.sleep(10)
  
      # Create a dataset
      df<-data.frame(A=c("a", "a", "a", "b", "b", "c"), B=c(1,2,3,4,5,6))
      assign("Test2", df, .GlobalEnv)

      # Store the list in Prom
      return(
        list(SUM=sum(df$B))
      )

  }, seed=T) # Close future promise
  
  return(Prom) # Return Prom to the API

}

问题原因

multisession模式下,future会启动独立的R子进程,每个子进程拥有自己独立的全局环境,和主Plumber进程的全局环境完全隔离。test2中的assign("Test2", df, .GlobalEnv)实际上是把变量写入了future子进程的全局环境,而非主进程的全局环境,因此主进程和其他API接口无法访问该变量。

解决方案

1. 使用跨进程共享存储(推荐)

放弃依赖全局环境,改用支持跨进程访问的存储方案,比如Redis、磁盘文件或共享内存包。这里以Redis为例:

步骤1:安装依赖包

install.packages("redux")

步骤2:修改test2接口存入数据

#* Test 2
#* @get test2
#* @serializer unboxedJSON
#* @tag TestFuture
function() {
  library(redux)
  r <- redux::hiredis()
  
  Prom<-future({
    Sys.sleep(10)
    df<-data.frame(A=c("a", "a", "a", "b", "b", "c"), B=c(1,2,3,4,5,6))
    # 序列化数据后存入Redis
    r$SET("Test2", serialize(df, connection = NULL))
    list(SUM=sum(df$B))
  }, seed=T)
  
  return(Prom)
}

步骤3:新增接口读取数据

#* Read Test2 from Redis
#* @get read_test2
#* @serializer unboxedJSON
function() {
  library(redux)
  r <- redux::hiredis()
  raw_data <- r$GET("Test2")
  
  if (!is.null(raw_data)) {
    df <- unserialize(raw_data)
    return(df)
  } else {
    return(list(message = "Test2 not found"))
  }
}

2. 利用Plumber内置状态管理

Plumber提供了pr$state用于存储主进程的全局状态,通过promise的then回调在主进程中更新状态:

修改test2接口

#* Test 2
#* @get test2
#* @serializer unboxedJSON
#* @tag TestFuture
function(req, res) {
  Prom<-future({
    Sys.sleep(10)
    df<-data.frame(A=c("a", "a", "a", "b", "b", "c"), B=c(1,2,3,4,5,6))
    list(SUM=sum(df$B), data=df)
  }, seed=T)
  
  # 异步任务完成后,回到主进程更新Plumber状态
  Prom <- Prom %>% then(function(result) {
    req$pr$state$Test2 <- result$data
    return(list(SUM=result$SUM))
  })
  
  return(Prom)
}

新增接口读取状态数据

#* Read Test2 from Plumber state
#* @get read_test2_state
#* @serializer unboxedJSON
function(req) {
  if (!is.null(req$pr$state$Test2)) {
    return(req$pr$state$Test2)
  } else {
    return(list(message = "Test2 not found in state"))
  }
}

3. 改用非多会话future计划(不推荐)

如果必须依赖全局环境,可以改用plan("sequential")(同步执行,失去异步优势)或plan("multicore")(仅支持Unix/Linux/macOS,子进程与主进程有部分环境共享,但可靠性不足)。但这种方式会牺牲异步并行的能力,不建议在生产环境使用。


内容的提问来源于stack exchange,提问作者Victor Cz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 23:15:36