【问题标题】:How sessions work in shiny-server?会话如何在闪亮服务器中工作?
【发布时间】:2023-03-23 03:29:01
【问题描述】:

我在理解闪亮服务器中的会话如何工作时遇到了一些麻烦。我假设当用户关闭浏览器时会话结束,但是,通过在服务器函数中使用print(session$isClosed()),我在开始时收到FALSE 响应(没关系),然后当我关闭浏览器时没有任何反应。谁能给我有关闪亮服务器会话的线索?我想存储特定于会话的图,让用户只下载他们的图。

【问题讨论】:

  • 如果我没记错的话,你想让用户只下载与当前会话有关的图和数据等,而不是从任何以前的会话下载,是吗?

标签: r shiny shiny-server


【解决方案1】:

好吧,从一个闪亮的会话对象开始,它是闪亮的特定('R6')数据结构,由公共和私有元素组成。它的目的是记录一个用户和闪亮之间的关系的一个实例(稍后会详细介绍)。

>str(session)
Classes 'ShinySession', 'R6' <ShinySession>
  Public:
    @uploadEnd: function (jobId, inputId) 
    @uploadieFinish: function () 
    @uploadInit: function (fileInfos) 
    allowReconnect: function (value) 
    clientData: reactivevalues
    clone: function (deep = FALSE) 
    close: function () 
    closed: FALSE
    decrementBusyCount: function () 
    defineOutput: function (name, func, label) 
    dispatch: function (msg) 
    doBookmark: function () 
    downloads: Map, R6
    exportTestValues: function (..., quoted_ = FALSE, env_ = parent.frame()) 
    files: Map, R6
    fileUrl: function (name, file, contentType = "application/octet-stream") 
    flushOutput: function () 
    freezeValue: function (x, name) 
    getBookmarkExclude: function () 
    getTestEndpointUrl: function (inputs = TRUE, outputs = TRUE, exports = TRUE, format = "rds") 
    groups: NULL
    handleRequest: function (req) 
    incrementBusyCount: function () 
    initialize: function (websocket) 
    input: reactivevalues
    isClosed: function () 
    isEnded: function () 
    makeScope: function (namespace) 
    manageHiddenOutputs: function () 
    manageInputs: function (data) 
    ns: function (id) 
    onBookmark: function (fun) 
    onBookmarked: function (fun) 
    onEnded: function (endedCallback) 
    onFlush: function (flushCallback, once = TRUE) 
    onFlushed: function (flushedCallback, once = TRUE) 
    onInputReceived: function (callback) 
    onRestore: function (fun) 
    onRestored: function (fun) 
    onSessionEnded: function (sessionEndedCallback) 
    output: shinyoutput
    outputOptions: function (name, ...) 
    progressStack: environment
    reactlog: function (logEntry) 
    registerDataObj: function (name, data, filterFunc) 
    registerDownload: function (name, filename, contentType, func) 
    reload: function () 
    request: environment
    resetBrush: function (brushId) 
    restoreContext: RestoreContext, R6
    rootScope: function () 
    saveFileUrl: function (name, data, contentType, extra = list()) 
    sendBinaryMessage: function (type, message) 
    sendCustomMessage: function (type, message) 
    sendInputMessage: function (inputId, message) 
    sendInsertUI: function (selector, multiple, where, content) 
    sendModal: function (type, message) 
    sendNotification: function (type, message) 
    sendProgress: function (type, message) 
    sendRemoveUI: function (selector, multiple) 
    session: active binding
    setBookmarkExclude: function (names) 
    setShowcase: function (value) 
    showProgress: function (id) 
    singletons: 
    token: d44d583f13b3cd4ccce43f59fe410f61
    unhandledError: function (e) 
    updateQueryString: function (queryString) 
    user: NULL
    wsClosed: function () 
  Private:
    .clientData: ReactiveValues, R6
    .input: ReactiveValues, R6
    .outputOptions: list
    .outputs: list
    bookmarkCallbacks: environment
    bookmarkedCallbacks: environment
    bookmarkExclude: 
    busyCount: 2
    closedCallbacks: environment
    createBookmarkObservers: function () 
    enableTestEndpoint: function () 
    fileUploadContext: environment
    flushCallbacks: environment
    flushedCallbacks: environment
    getOutputOption: function (outputName, propertyName, defaultValue) 
    inputMessageQueue: list
    inputReceivedCallbacks: environment
    invalidatedOutputErrors: Map, R6
    invalidatedOutputValues: Map, R6
    outputValues: list
    progressKeys: character
    registerSessionEndCallbacks: function () 
    restoreCallbacks: environment
    restoredCallbacks: environment
    sendErrorResponse: function (requestMsg, error) 
    sendMessage: function (...) 
    sendResponse: function (requestMsg, value) 
    shouldSuspend: function (name) 
    showcase: FALSE
    storeOutputValues: function (values = NULL) 
    testEndpointUrl: session/d44d583f13b3cd4ccce43f59fe410f61/dataobj/shinyte ...
    testValueExprs: list
    websocket: WebSocket
    write: function (json) 

探索会话对象的一个​​好方法是使用 shiny example in shiny gallery client-data-and-query-string。它允许see 包含例如session$clientdata 或对象的任何其他元素中的内容。

几个额外的和误导性的琐碎点:

  • 会话何时开始?当用户连接闪亮的应用时
  • 会话何时结束?当用户与闪亮的应用断开连接时

例如,为了说明问题实际上是多么复杂,如果我刷新浏览器,我会结束当前会话并创建一个新会话。

来到session$isClosed(),这不是在会话结束时连接到特定操作的正确功能。 这其实是一个闪亮的回调函数的作用

onSessionEnded(fun, session = getDefaultReactiveDomain())

一个最小的例子如下:

library(shiny)

ui =(
  fluidPage(
    titlePanel("This is an example")
  )
)

server = function(input, output, session){
  session$onSessionEnded({
    print("Stop!")
    stopApp   
  }) 
}

runApp(list(ui = ui, server = server))

如果您尝试,刷新(或与 browser() 分手)将打印“停止”并停止应用程序。

2017 年 9 月 26 日编辑:

一般来说,如果会话的连续性很重要,我认为最好谨慎(无论如何直接在Shiny ServerShiny Server Pro 上测试session 代码是合适的)。 Shiny Server Pro 可能是最重要的用例,其中任何断开连接 may 都会影响登录状态等。

我还知道shiny 团队在最近的版本中对这些区域进行了更改。例如,虽然onSessionEnded 似乎仍然有效,但它可能不再是这个用例的最佳功能。

请参阅以下代码作为示例(来自shiny 参考指南),使用onStop,可以在会话结束以及应用停止时工作。

library(shiny)

cat("Doing application setup\n")
 onStop(function() {
   cat("Doing application cleanup\n")
 })

 shinyApp(
   ui = basicPage("onStop demo"),

   server = function(input, output, session) {
     onStop(function() cat("Session stopped\n"))
   }
 )

【讨论】:

  • 你确定这句话:“如果我用 browser() 调试一个闪亮的应用程序,当应用程序在中断时中断时,它会结束当前会话。” ?我不认为那是真的。此外,示例代码中的print("Stop!") 具有误导性,因为它不会在会话结束时运行,而是在会话开始时运行,只有在会话结束时调用stopApp 回调。我认为其中一些信息是错误的。
  • 我认为shiny 改变了一段时间后session 被中断时的行为(我将在帖子中添加警告)。我重新测试了session#token 在使用browser() 进行调试期间是否发生变化,并且确实令牌保持不变。我认为@Dean Attali 的评论也有点错误:根据闪亮的参考。 “onSessionEnded注册了一个在客户端断开连接后要调用的函数”,这对我来说意味着该函数实际上是断开连接后的run。例如。过去我使用session$onSessionEnded 来保存具有应用程序某些状态的文件或断开与数据库的连接。
  • onSessionEnded() 确实注册了一个函数回调,但我说的仍然是正确的。试试看:打印语句在启动时立即运行。发生这种情况的原因是因为onSessionEnded() 需要一个函数,但是您向它传递了一个包含打印语句和函数的表达式。所以第二行,stopApp,是会话结束时调用的函数。但是表达式中的代码会立即运行。为了获得你想要的行为,我认为你需要onSessionEnded(function(){print(...);stopApp())
  • 我在一台旧笔记本电脑上使用闪亮的 0.13.0(2016 年 1 月)进行了测试,在 browser() 之前/之后会话没有改变。我真的不认为它曾经这样做过,会话仅在应用程序死亡、应用程序超时或用户离开页面时结束(例如通过关闭选项卡或刷新它)。但我很想知道为什么每次使用浏览器时都会运行打印。你能分享一下代码吗? print 运行一次是有道理的,但如果你以某种方式使它onSessionEnded() 在每个浏览器上运行,那么我会感到惊讶(并承认我的错误:)
  • 是否可以在会话中保留“radioButtons”组件的选定值?例如,当默认是第一个元素时,我选择了第二个元素,然后在当前会话期间想将其用作默认值,直到它改变为止。
猜你喜欢
  • 1970-01-01
  • 2016-02-23
  • 2014-12-16
  • 2013-02-08
  • 2017-03-29
  • 1970-01-01
  • 1970-01-01
  • 2023-01-16
  • 2015-05-14
相关资源
最近更新 更多