Обслуживать различные wsgiapplications в зависимости от домена запроса на GAE с помощью threadsafe:true

Я пытаюсь загрузить разные приложения (webapp2.WSGIApplication) в зависимости от домена запроса. например, www.domain_1.com должен загрузить приложение в app1.main.application, а www.domain_2.com должен загрузить app2.main.appplication.
Конечно, я в том же приложении GAE и использую пространства имен для разделения данных приложений.

это работает очень хорошо с 'threadsafe:false' и файлом runner.py, где функция определяет, какое приложение вернуть

кажется, что при использовании threadsafe:true первый запрос загружает wsgiapplication в экземпляр, а дальнейшие запросы больше не выполняют логику "диспетчеризации приложения", поэтому запрос получает ответ от неправильного приложения.

я использую python2.7 и webapp2

Каков наилучший способ сделать это?

редактировать:

очень упрощенная версия моего runner.py

def main():
    if domain == 'www.mydomain_1.com':
        from app_1 import application
        namespace = 'app_1'
    elif domain == 'www.domain_2.com':
        from app_2 import application
        namespace = 'app_2'
    namespace_manager.set_namespace(namespace)
    return wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
    main()

и в app.yaml

- url: /.*
  script: app-runner.py

2 ответа

Решение

Сделал это путем создания подкласса webapp2.WSGIApplication и переопределения __call__() который вызывается перед отправкой в ​​RequestHandler.
префикс маршрутов (и удаление префикса в инициализаторах обработчиков) и настройка подструктуры, чтобы можно было использовать память экземпляра.

class CustomWSGIApplication(webapp2.WSGIApplication):

    def __call__(self, environ, start_response):

        routes, settings, ns = get_app(environ)
        namespace_manager.set_namespace(ns)

        environ['PATH_INFO'] = '/%s%s' %(ns, environ.get('PATH_INFO'))

        for route in routes:
            r, h     = route # returns a tuple with mapping and handler
            newroute = ('/%s%s'%(ns, r), h,)
            self.router.add(newroute)


        if settings:
            self.config[ns] = settings

        self.debug  = debug

        with self.request_context_class(self, environ) as (request, response):
            try:
                if request.method not in self.allowed_methods:
                    # 501 Not Implemented.
                    raise exc.HTTPNotImplemented()

                rv = self.router.dispatch(request, response)
                if rv is not None:
                    response = rv
            except Exception, e:
                try:
                    # Try to handle it with a custom error handler.
                    rv = self.handle_exception(request, response, e)
                    if rv is not None:
                        response = rv
                except HTTPException, e:
                    # Use the HTTP exception as response.
                    response = e
                except Exception, e:
                    # Error wasn't handled so we have nothing else to do.
                    response = self._internal_error(e)

            try:
                return response(environ, start_response)
            except Exception, e:
                return self._internal_error(e)(environ, start_response)

Ваш скрипт для бегуна - это скрипт CGI. Полное поведение сценария CGI с включенной многопоточностью не задокументировано, и способ написания документов, я полагаю, не будет полностью поддерживаться. Вместо этого в документах говорится, что вы должны ссылаться на объект приложения WSGI непосредственно из app.yaml, используя путь модуля к глобальной переменной, содержащей объект, когда многопоточность включена. (CGI-скрипты сохраняют свое старое поведение в Python 2.7 с отключенной многопоточностью.)

Поведение, которое вы видите, объясняется вашим использованием импорта. В пределах одного экземпляра каждый оператор импорта оказывает влияние только при первом обнаружении. После этого предполагается, что модуль импортирован, а оператор импорта не влияет на последующие запросы. Вы можете импортировать оба значения в отдельные имена, а затем вызвать run() с соответствующим значением.

Но если вы хотите включить многопоточность (и это хорошая идея), вашим диспетчером должно быть само приложение WSGI, хранящееся в глобальном модуле, на который ссылается app.yaml, Я не знаю, как от руки отправлять запрос в другое приложение WSGI из приложения WSGI, но это может быть разумным решением. В качестве альтернативы вы можете рассмотреть возможность использования или создания слоя выше WSGI для выполнения этой отправки.

Другие вопросы по тегам