#python #python-3.x #flask #flask-restful
#python #r #flask #rserve #pyrserve
Вопрос:
Я пишу небольшое приложение Flask и подключаю его к Rserve с помощью pyRserve. Я хочу, чтобы каждый сеанс инициировал, а затем поддерживал свое собственное соединение Rserve.
Что-то вроде этого:
session['my_connection'] = pyRserve.connect()
не работает, потому что объект connection не является сериализуемым в формате JSON. С другой стороны, что-то вроде этого:
flask.g.my_connection = pyRserve.connect()
не работает, потому что он не сохраняется между запросами. Чтобы добавить сложности, не похоже, что pyRserve предоставляет какой-либо идентификатор для соединения, поэтому я не могу сохранить идентификатор соединения в сеансе и использовать его для получения правильного соединения перед каждым запросом.
Есть ли способ обеспечить уникальное соединение для каждого сеанса?
Комментарии:
1. Почему вам нужно использовать одно и то же соединение для сеанса?
2. Потому что мне нужно, чтобы объекты в пространстве имен R сохранялись для одного и того же пользователя во время сеанса (но не были видны / доступны другим пользователям). Например, пользователь может загрузить некоторые данные и подогнать модель — я хочу иметь возможность доступа к этой модели (без ее переоборудования) на других страницах (т. Е. После выполнения других запросов Flask).).
3. Я понимаю. Я не уверен, что мне действительно нужно повторно используемое соединение для каждого пользователя. Мое единственное требование заключается в том, чтобы пользовательское R-соединение / сеанс мог получать доступ к R-объектам, созданным с использованием предыдущих запросов этого пользователя. Я полагаю, что приемлемым решением может быть сохранение R-соединением текущей рабочей области R на сервере, сохранение идентификатора этой рабочей области в виде файла cookie и по новому запросу, чтобы новые R-соединения считывали эту рабочую область обратно…
4. Взгляните на DeployR ( deployr.revolutionanalytics.com ) — он добавляет API и дополнительные функциональные возможности поверх Rserve, что упрощает управление требованиями такого типа.
5. @Andrie Я рассмотрел это — хотя, похоже, существуют только клиентские библиотеки для Java, Javascript и .NET. Я ограничен python…
Ответ №1:
Следующее относится к любым глобальным данным 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.