【问题标题】:serve different wsgiapplications depending on request domain on GAE with threadsafe:true根据 GAME 上的请求域提供不同的 wsgi 应用程序,线程安全:true
【发布时间】:2012-03-31 12:27:27
【问题描述】:

我试图做的是根据请求域加载不同的应用程序(webapp2.WSGIApplication)。 例如 www.domain_1.com 应该加载 app1.main.application 中的应用程序,而 www.domain_2.com 应该加载 app2.main.appplication。
当然,我在同一个 GAE appid 上,并且我使用命名空间来分隔应用程序数据。

这对 '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 个域是不同的,还是只是您尝试使用命名空间实现分离的数据存储区?
  • 逻辑不同,否则通过设置不同的命名空间很容易完成。
  • 那么您在 runner.py 中有一个 WSGI 应用程序,它将请求分派给几个 WSGI 应用程序之一? “内部”应用程序是否存储在覆盖调度逻辑的全局中?
  • @DanSanderson 我编辑了问题并添加了 runner.py

标签: google-app-engine python-2.7 wsgi


【解决方案1】:

您的运行脚本是 CGI 脚本。未记录启用多线程的 CGI 脚本的完整行为,并且文档的编写方式我猜这不会得到完全支持。相反,文档说,当多线程打开时,您必须直接从app.yaml 引用 WSGI 应用程序对象,使用指向包含该对象的全局变量的模块路径。 (CGI 脚本保留了它们在 Python 2.7 中的旧行为,并关闭了多线程。)

您看到的行为是通过您使用导入来解释的。在单个实例中,每个 import 语句仅在第一次遇到时才起作用。之后,假设模块被导入,import 语句对后续请求没有影响。您可以将这两个值导入到单独的名称中,然后使用适当的值调用 run()。

但是如果你想启用多线程(这是一个好主意),你的调度器应该是一个 WSGI 应用程序本身,存储在一个由app.yaml 引用的全局模块中。我不知道如何将请求从 WSGI 应用程序中分派到另一个 WSGI 应用程序,但这可能是合理的做法。或者,您可以考虑在 WSGI 之上使用或构建一个层来执行此调度。

【讨论】:

  • 当然开启多线程我不会调用 CGIHandler().run(application) 而是直接返回应用程序。我正在考虑将调度逻辑放在 webapp2.Router 中,但我必须先弄清楚如何做到这一点。
  • 使用 python27 和多线程,app.yaml 中“script”的值应该是包含 WSGI 应用程序对象的全局变量的模块路径。在此级别,您不能基于每个请求返回不同的 WSGI 应用程序对象。 app.yaml 应该引用一个 WSGI 应用程序,该应用程序负责由应用程序实例处理的所有请求。一旦进入这个应用程序,你就可以做任何 WSGI 可以做的事情,包括可能分派到其他 WSGI 应用程序。
【解决方案2】:

通过子类化 webapp2.WSGIApplication 并覆盖__call__() 来实现这一点,__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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    相关资源
    最近更新 更多