Запуск и остановка фляги по запросу

Я пишу приложение, которое может предоставить простой интерфейс RPC, реализованный с помощью flask. Однако я хочу, чтобы можно было активировать и деактивировать этот интерфейс. Также должна быть возможность запуска нескольких экземпляров приложения в одном и том же интерпретаторе Python, каждый из которых имеет собственный интерфейс RPC.

Служба доступна только для локального хоста, и это прототип, поэтому я не беспокоюсь о безопасности. Я ищу небольшое и простое решение. Очевидным способом здесь, кажется, является использование сервера разработки фляг, однако я не могу найти способ его отключить.

Я создал план фляги для функций, которые я хочу раскрыть, и теперь я пытаюсь написать класс для оболочки интерфейса RPC, подобный этому:

class RPCInterface:
    def __init__(self, creating_app, config):
        self.flask_app = Flask(__name__)
        self.flask_app.config.update(config)
        self.flask_app.my_app = creating_app

        self.flask_app.register_blueprint(my_blueprint)

        self.flask_thread = Thread(target=Flask.run, args=(self.flask_app,),
                                   name='flask_thread', daemon=True)

    def shutdown(self):
        # Seems impossible with the flask server
        raise NotImplemented()

Я использую переменную my_app текущего приложения, чтобы передать экземпляр моего приложения, с которым работает этот интерфейс RPC, в контекст запросов.

Его можно отключить изнутри запроса (как описано здесь http://flask.pocoo.org/snippets/67/), поэтому одним из решений может быть создание конечной точки выключения и отправка запроса тестовому клиенту, чтобы инициировать выключение. Однако для этого требуется конечная точка фляги. Это далеко не чисто.

Я просмотрел исходный код flask и werkzeug и выяснил важную часть (контекст в https://github.com/pallets/werkzeug/blob/master/werkzeug/serving.py#L688) выглядит так:

def inner():
    try:
        fd = int(os.environ['WERKZEUG_SERVER_FD'])
    except (LookupError, ValueError):
        fd = None
    srv = make_server(hostname, port, application, threaded,
                      processes, request_handler,
                      passthrough_errors, ssl_context,
                      fd=fd)
    if fd is None:
        log_startup(srv.socket)
    srv.serve_forever()

make_server возвращает экземпляр класса сервера werkzeugs, который наследуется от класса http.server python. Это, в свою очередь, python BaseSocketServer, который предоставляет метод завершения работы. Проблема в том, что созданный здесь сервер — это всего лишь локальная переменная, и поэтому он недоступен ниоткуда.

Тут я зашел в тупик. Итак, мой вопрос:

  • У кого-нибудь есть другая идея, как легко отключить этот сервер?
  • Есть ли другой простой сервер для запуска фляги? Что-то, что не требует внешнего процесса и может быть просто запущено и остановлено несколькими строками кода? Все, что перечислено в документе фляги, похоже, имеет сложную настройку.

person Leonidaz0r    schedule 25.03.2017    source источник
comment
Что в документах Flask имеет сложную настройку? Можешь поделиться? меня интересует твой вопрос   -  person aaossa    schedule 26.03.2017
comment
Я имел в виду этот список: flask.pocoo.org/docs/0.12/deploying/ #deployment Казалось, что всем нужен внешний веб-сервер вроде apache или nginx. Однако теперь я понял, что решение gevent отличается и не требует внешнего сервера. Я все еще работаю над этой частью. Я обновлю, когда найду время, чтобы проверить это.   -  person Leonidaz0r    schedule 27.03.2017


Ответы (2)


Отвечая на мой собственный вопрос, если это когда-нибудь повторится с кем-нибудь.

Первое решение заключалось в переключении с flask на klein. Klein — это, по сути, колба с меньшим количеством функций, но работающая поверх скрученного реактора. Таким образом, интеграция очень проста. В основном это работает так:

from klein import Klein
from twisted.internet import reactor

app = Klein()

@app.route('/')
def home(request):
    return 'Some website'

endpoint = serverFromString(reactor, endpoint_string)
endpoint.listen(Site(app.resource()))

reactor.run()

Теперь все навороченные инструменты можно использовать для запуска и остановки сервера по мере необходимости.

Второе решение, к которому я перешел в дальнейшем, заключалось в том, чтобы избавиться от HTTP в качестве транспортного протокола. Я переключился на JSONRPC поверх протокола LineReceiver. Таким образом, все стало еще проще, и я все равно не использовал ничего из HTTP.

person Leonidaz0r    schedule 19.05.2017

Это ужасный, ужасный хак, который никто никогда не должен использовать ни для каких целей... кроме возможно, если вы пытаетесь написать набор интеграционных тестов. Вероятно, есть лучшие подходы, но если вы пытаетесь сделать именно то, что задает вопрос, вот и все...

import sys
from socketserver import BaseSocketServer

# implementing the shutdown() method above
def shutdown(self):
    for frame in sys._current_frames().values():
        while frame is not None:
            if 'srv' in frame.f_locals and isinstance(frame.f_locals['srv'], BaseSocketServer):
                frame.f_locals['srv'].shutdown()
                break
        else:
            continue
        break
    self.flask_thread.join()
person kgutwin    schedule 14.08.2018