简体   繁体   English

如何从基于服务器的 Jupyter Notebook 连接到本地主机中的服务?

[英]How to connect from server-based Jupyter Notebook to service in localhost?

Note: This is the reverse of what most StackOverflow users ask注意:这与大多数 StackOverflow 用户的要求相反

I have a Jupyter notebook running on a remote server (via a browser) and a test service running on my localhost .我有一个在远程服务器上运行的 Jupyter 笔记本(通过浏览器)和一个在我的localhost上运行的测试服务。 I'd like to call the local service from the remote server.我想远程服务器调用本地服务。

The problem is that connecting to localhost when running on the remote server connects to the remote server's localhost, not my own PC (in which the browser is running).问题是在远程服务器上运行时连接到 localhost 连接到远程服务器的本地主机,而不是我自己的 PC(其中运行浏览器)。

I'm expecting that maybe I'd have to drop into Javascript to do this??我期待也许我必须使用 Javascript 才能做到这一点? I'm guessing this would be an AJAX-like call, with a callback involved.我猜这将是一个类似 AJAX 的调用,涉及回调。 I think this complicates things somewhat.我认为这使事情变得有些复杂。 Is there a simpler approach?有没有更简单的方法?

Does anyone have any experience with this??有人对这个有经验么?? Maybe some sample code??也许一些示例代码?

all --全部 -

OK ... I have tried this every which way, and have gotten a decent education along the way.好的......我已经尝试了各种方法,并且一路上得到了体面的教育。 The bottom line is that it is possible to call a localhost-based service from a Jupyter Notebook (aka IPython) running on a remote server, and it may also be possible to get a result back so it can be used in Python (though, with restrictions).底线,它可以调用从Jupyter笔记本基于本地主机服务(又名IPython中)在远程服务器上运行,它也有可能得到一个结果返回,因此它可以在Python中使用(虽然,有限制)。 This is important because it enables biological workflows to call the REST-based automation interface of the Cytoscape desktop application.这很重要,因为它使生物工作流程能够调用 Cytoscape 桌面应用程序的基于 REST 的自动化接口。

I'm writing this up so successor-me can see this and profit, or give up.我写这篇文章是为了让继任者——我可以看到这一点并从中获利,或者放弃。 I have been helped by tons of other StackOverlow and free standing articles ... too many to mention, most prominently:我得到了大量其他 StackOverlow 和独立文章的帮助......太多了,最突出的是:

ipython-notebook-javascript-python-communication ipython-notebook-javascript-python-communication

Execute Discussion 执行讨论

workaround-for-wrapping-a-js-function-in-python-in-jupyter-notebook workaround-for-wrapping-a-js-function-in-python-in-jupyter-notebook

There are three parts to this problem:这个问题分为三个部分:

  • Execute an HTTP operation (... suppose GET, but POST, PUT, etc work, too)执行 HTTP 操作(...假设 GET,但 POST、PUT 等也可以工作)
  • Get the HTTP status and result back获取 HTTP 状态和结果
  • Use the result in Python在 Python 中使用结果

There are multiple challenges:有多重挑战:

  • Pass a URL and payload (for PUT/POST) into the HTTP caller将 URL 和有效负载(用于 PUT/POST)传递给 HTTP 调用方
  • Return to caller only when an HTTP result is available (and not before)当 HTTP 结果可用时(而不是之前)返回给调用者
  • Make the HTTP result available via a Python variable通过 Python 变量使 HTTP 结果可用

A major problem exists because Python is a traditional synchronous language executing (partially) in a browser-based (IPython) environment.存在一个主要问题是因为 Python 是一种在基于浏览器 (IPython) 的环境中(部分)执行的传统同步语言。 Normally, this isn't a problem, as the remote Jupyter server executes a full cell of Python and then another cell.通常,这不是问题,因为远程 Jupyter 服务器先执行一个完整的 Python 单元,然后再执行另一个单元。 The problem arises because the HTTP call must be in Javascript (ie, in a %%js or %%javascript cell or as an argument to an IPython.display.HTML() call), which is executed in the local browser (instead of the remote server).出现问题是因为 HTTP 调用必须在 Javascript 中(即,在 %%js 或 %%javascript 单元格中或作为 IPython.display.HTML() 调用的参数),它在本地浏览器中执行(而不是远程服务器)。 A base principal of browser-based Javascript is that long (eg, HTTP) operations should be done asynchronously (eg, Fetch() using Promises, generators using Yield(), or normal XMLHttpRequest calls) to maintain highly interactive browser performance.基于浏览器的 Javascript 的基本原则是长时间(例如 HTTP)操作应该异步完成(例如,使用 Promise 的 Fetch()、使用 Yield() 的生成器或正常的 XMLHttpRequest 调用)以保持高度交互的浏览器性能。 For a workflow waiting on an answer, asynchronous execution would put a poor bioinformatics Python programmer into shock via callback hell.对于等待答案的工作流,异步执行会通过回调地狱让一个糟糕的生物信息学 Python 程序员感到震惊。 Additionally, such workflows are expected to be compute-heavy, and interactivity isn't expected ... causality must be preserved.此外,此类工作流预计会占用大量计算资源,并且不需要交互性……必须保留因果关系。

In Javascript, the gist of the solution is to use synchronous XMLHttpRequest calls.在 Javascript 中,解决方案的要点是使用同步XMLHttpRequest 调用。 This gets a synchronous answer.这得到了一个同步的答案。 Next, use the undocumented IPython.notebook.kernel.execute() to store a value into a Python variable on the remote server.接下来,使用未记录的 IPython.notebook.kernel.execute() 将值存储到远程服务器上的 Python 变量中。 This is an asynchronous operation, and must be done last.这是一个异步操作,必须最后完成。 (You can see the source to execute() only by using the Development mode in a Chrome browser: F12->static/notebook/js/services/kernels/kernel.js, and find kernel.js/Kernel.prototype.execute). (只有在Chrome浏览器中使用开发模式才能看到execute()的源码:F12->static/notebook/js/services/kernels/kernel.js,找到kernel.js/Kernel.prototype.execute) . If this is done at the end of a cell, IPython will pause while it gets the next cell ready or waits for user input.如果这是在单元格的末尾完成的,IPython 将在下一个单元格准备好或等待用户输入时暂停。 During either of these times, it appears that the Python kernel services the statement queue created by execute().在这两种情况下,Python 内核似乎都在为 execute() 创建的语句队列提供服务。 If that's what's really happening, it should be safe to expect that the Python variable containing the HTTP result will be ready to read in the follow-on cell.如果这确实发生了,那么可以安全地预期包含 HTTP 结果的 Python 变量将准备好在后续单元格中读取。 It's a fair bet it won't be ready if the execute() and Python variable fetch are performed in the same cell.可以肯定的是,如果 execute() 和 Python 变量提取在同一个单元格中执行,它不会准备好。

A synchronous XMLHttpRequest call is created when you pass "false" to the XMLHttpRequest.open() function.当您将“false”传递给 XMLHttpRequest.open() 函数时,将创建同步 XMLHttpRequest 调用。 Some browsers may complain about this, as they think that synchronous calls are evil and are deprecated.一些浏览器可能会抱怨这一点,因为他们认为同步调用是邪恶的并且已被弃用。 Best to use a browser (eg, Chrome) that doesn't complain.最好使用不会抱怨的浏览器(例如 Chrome)。

Weirdness: If you call HTML() to execute the Javascript, it should be the last call in a cell;奇怪:如果你调用 HTML() 来执行 Javascript,它应该是单元格中的最后一次调用; otherwise, the HTML() call will fail.否则,HTML() 调用将失败。 If you wrap the HTML() call in a IPython.display.display() call, you can put additional Python statements after it in the same cell ... just don't look for the HTTP result variable to have been set until IPython transitions to the next cell.如果您将 HTML() 调用包装在 IPython.display.display() 调用中,您可以在它之后在同一个单元格中放置额外的 Python 语句……只是在 IPython 之前不要查找已设置的 HTTP 结果变量过渡到下一个单元格。 Note that workarounds like Timeout() are ineffective here, as it doesn't appear to force processing of the Execute() statement queue, so you really do have to wait until the next cell before accessing the HTTP result.请注意,像超时()的解决方法是无效的位置,因为它似乎并没有强制执行()语句队列的处理,所以你确实有访问HTTP结果之前等待,直到下一个单元格。

This Javascript cell creates the Javascript functions that will do the work.这个 Javascript 单元创建了将完成工作的 Javascript 函数。 Note the Cy* naming convention, which makes collision with IPython browser code unlikely.请注意 Cy* 命名约定,它不太可能与 IPython 浏览器代码发生冲突。

%%js

function CyHttpGet(url) {
  var xhr = new XMLHttpRequest()
  xhr.open('GET', url, false)
  xhr.send(null);
  if (xhr.status === 200) {
    return xhr.responseText;
  }
  else {
    return "bad news:" + xhr.status
  }
}

function CySetKernelVar(varValue) {
  varValue = varValue.replace(/'/g, "/'");  // Double any single quotes
        
  var command = "httpResult = '"+ varValue + "'";
        
  var kernel = IPython.notebook.kernel;
  kernel.execute(command);
}

This Python cell makes the actual HTTP call:这个 Python 单元进行实际的 HTTP 调用:

from IPython.display import HTML

javascript = """
<script type="text/Javascript">

// This is what actually executes the HTTP and sets the result as a Python variable
CySetKernelVar(CyHttpGet('http://localhost:1234'));

</script>
"""

HTML(javascript)

This Python cell makes use of the HTTP result (and must not be part of the previous cell):这个 Python 单元使用了 HTTP 结果(并且不能是前一个单元的一部分):

print(httpResult)

httpResult='xxx' # clear variable so we're not fooled if the Javascript code doesn't do what we thought it would

Note that the ability to pass a value from Javascript to Python via Execute() really does depend on the Python kernel clearing the Execute queue between cells.请注意,通过 Execute() 将值从 Javascript 传递到 Python 的能力确实取决于 Python 内核清除单元格之间的 Execute 队列。 If this isn't really happening, we would expect to see "xxx" in the print(httpResult) from time to time - very bad news.如果这不是真的发生,我们希望不时在打印(httpResult)中看到“xxx” - 非常糟糕的消息。

Note that some web posts suppose that a value can be passed from Javascript to Python by having the Javascript store the value as the innerHTML of a DIV statement, and then having an HTML() or Javascript() function read the innerHTML and return it.请注意,一些网络帖子假设可以通过让 Javascript 将值存储为 DIV 语句的 innerHTML,然后让 HTML() 或 Javascript() 函数读取 innerHTML 并返回它来将值从 Javascript 传递到 Python。 I haven't gotten this to work, but if it can work, the Execute() may not be necessary.我还没有让它工作,但如果它可以工作,那么 Execute() 可能不是必需的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM