在Flask会话中存储大量数据或服务连接

21

我正在编写一个小型的Flask应用程序,并使用pyRserve将其连接到Rserve。我希望每个会话都能启动并保持自己的Rserve连接。

类似这样:

session['my_connection'] = pyRserve.connect()

不能工作是因为连接对象不可JSON序列化。另一方面,像这样的东西:

flask.g.my_connection = pyRserve.connect()

无法正常工作,因为它不能在请求之间持久化。更加困难的是,似乎pyRserve没有为连接提供任何标识符,所以我不能将连接ID存储在会话中,并在每个请求之前使用它来检索正确的连接。

有没有一种方法可以实现每个会话拥有唯一连接?


1
为什么需要在会话中使用相同的连接? - dirn
1
因为我需要在会话期间使R命名空间中的对象对同一用户持久化(但其他用户无法看到/访问)。例如,用户可能会加载一些数据并拟合模型 - 我希望能够在其他页面上访问该模型(而无需重新拟合它)(即在进行其他Flask请求之后)。 - alexizydorczyk
1
我明白了。我不确定我真正需要每个用户的可重用连接。我的唯一要求是,用户的R连接/会话能够访问该用户之前请求创建的R对象。我想一个可行的解决方案可能是让R连接将当前的R工作区保存到服务器,将该工作区的ID保存为cookie,在新请求时,让新的R连接读取该工作区... - alexizydorczyk
看一下DeployR(http://deployr.revolutionanalytics.com/)-它在Rserve的基础上添加了API和其他功能,使得管理这种需求变得容易。 - Andrie
@Andrie 我考虑过这个问题 - 尽管看起来只有Java、Javascript和.NET的客户端库。我受限于Python... - alexizydorczyk
您可以直接调用API,而无需使用客户端库。请参见http://deployr.revolutionanalytics.com/documents//dev/api-doc/guide/architecture.html或在https://groups.google.com/forum/#!forum/deployr上提问。 - Andrie
1个回答

31
以下适用于任何全局Python数据,您不想为每个请求重新创建,不仅限于rserve,也不仅限于每个用户独有的数据。
我们需要一个共同的位置来为每个用户创建一个rserve连接。最简单的方法是运行一个multiprocessing.Manager作为一个单独的进程。
import atexit
from multiprocessing import Lock
from multiprocessing.managers import BaseManager
import pyRserve

connections = {}
lock = Lock()


def get_connection(user_id):
    with lock:
        if user_id not in connections:
            connections[user_id] = pyRserve.connect()

        return connections[user_id]


@atexit.register
def close_connections():
    for connection in connections.values():
        connection.close()


manager = BaseManager(('', 37844), b'password')
manager.register('get_connection', get_connection)
server = manager.get_server()
server.serve_forever()

在启动应用程序之前运行它,这样管理员就可以使用了:
python rserve_manager.py

我们可以使用一个简单的函数在应用程序请求期间访问此管理器。这假定您在会话中有“user_id”的值(例如,Flask-Login会这样做)。这最终使得rserve连接对于每个用户是唯一的,而不是对于每个会话是唯一的。
from multiprocessing.managers import BaseManager
from flask import g, session

def get_rserve():
    if not hasattr(g, 'rserve'):
        manager = BaseManager(('', 37844), b'password')
        manager.register('get_connection')
        manager.connect()
        g.rserve = manager.get_connection(session['user_id'])

    return g.rserve

在视图中访问它:

result = get_rserve().eval('3 + 5')

这应该可以让您开始,虽然有很多可以改进的地方,比如不要硬编码地址和密码,不要丢弃与管理器的连接。这是使用Python 3编写的,但应该可以在Python 2中工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接