Обслуживать различные 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 для выполнения этой отправки.