【问题标题】:How to make CherryPy serve concurrent requests?如何让 CherryPy 服务并发请求?
【发布时间】:2015-01-28 10:59:48
【问题描述】:

我读到cherrypy 使用自己的线程池。但我看不出这样做的好处。

假设我触发了一个需要很长时间的请求,然后在另一个选项卡中触发了一个需要很短时间的请求。如果它真的使用多线程,短请求应该在长请求之前完成。但我看到首先完成长请求,然后完成短时间,好像所有内容都按顺序处理。

我尝试过与不同的 uWSGI 框架集成,例如 Tornado 和 twistd,但我仍然看不出有什么不同。 http://cherrypy.readthedocs.org/en/latest/deploy.html#tornado

这是我的入门代码。有谁能帮帮我吗?

cfg = {
'global' : {
  'server.socket_host' : Utils.gflags.FLAGS.bind_addr,
  'server.socket_port' : Utils.gflags.FLAGS.bind_port,
  'server.thread_pool' : 10,
  'engine.timeout_monitor.frequency' : gflags.FLAGS.request_time_out_secs,
},
'/static' : {"tools.sessions.on": False, 'tools.auth.on': False},
'/favicon.ico' : {"tools.sessions.on": False, 'tools.auth.on': False},
}

# To turn off the cherrypy errors on screen.
cfg['global'].update({'log.screen': False})
cfg['/static'].update({'tools.staticdir.on': True})
cfg['/static'].update({'tools.staticdir.dir': Utils.gflags.FLAGS.static_dir})
cfg['/favicon.ico'].update({'tools.staticfile.on': True})
cfg['/favicon.ico'].update({'tools.staticfile.filename':
                          Utils.gflags.FLAGS.favicon_file})


# Disable the auto reload on code change.
cherrypy.engine.autoreload.unsubscribe()

# Start the cherrypy
#Root() is defined somewhere else. Don't worry about that
cherrypy.quickstart(Root(), config = cfg)

【问题讨论】:

  • 嗨,您确定浏览器在第二个选项卡上使用了不同的连接吗?它可能只是简单地重复使用第一个,这意味着它将按顺序执行两个请求。
  • 嗯,你说的似乎是对的。但我确实需要请求级别的并发,而不是连接级别的并发。禁用会话可能是一种方式,我会检查。

标签: python multithreading cherrypy web-frameworks


【解决方案1】:

是的,您似乎遇到了这篇博文中提到的关于会话锁定的相同问题:http://blog.schmichael.com/2007/09/20/session-locking-and-performance-in-cherrypy/

基本上,解决方案是将会话显式锁定在代码中不会阻止所有其他请求的不同点。

cherrypy.session.acquire_lock()
cherrypy.session.release_lock()

【讨论】:

  • 谢谢。这就是问题所在。哇,这是多么微妙的事情,它产生了多么大的影响!
【解决方案2】:

我看到你在静态文件上禁用了会话工具,所以我假设阻塞是由阻塞会话引起的,查看documentation related to session locking

这个例子可能是说明性的:

"""Show the difference between explicit and implicit locking
on the cherrypy sessions.

To see the effects make sure your client can handle cookies,
for example any conventional web browser or curl with
a cookie jar.

The exposed routes are:

   /e/
   /e/block/[minutes]
   /e/blocked_hi/
   /e/unblocked_hi
   /i/
   /i/block/[minutes]
   /i/blocked_hi/
   /i/unblocked_hi

The application mounted on /e/ has the sessions *explicitly* locked and
the applicaiton mounted on /i/ has the sessions *implicitly* locked.

You can make any concurrent request on the /e branch and you
will not have any blocking.

If you do the requests on the following steps:
  1. /i/
  2. /i/block
  3. /i/blocked_hi

The step 3 is going to be blocked because of the step 2, you can wait a minute
and when the request on step 2 ends it will inmediatly complete the step 3.
Also any request that you do to /i/unblocked_hi will respond immediately regardless
of any blocking.

In general if you call:

 1. /i/ or /e/ and then
 2. /i/block
 3. Any request to:
        /i/
        /i/blocked_hi
        /e/
    are going to be blocked in until /i/block finish.
"""
import time

import cherrypy as cp


class _App:

    @cp.expose
    def block(self, m=1):
        """Sleep for `m` minutes and return."""
        time.sleep(float(m) * 60)
        return "I have blocked this request {}".format(m)

    @cp.expose
    def blocked_hi(self):
        """It can be blocked if the blocked method is executing,
        the session have content and is locked.
        """
        return """Hi, I could have been blocked by a session.
        Session content: {}\n""".format(dict(cp.session))

    @cp.expose
    def unblocked_hi(self):
        return "Hi, I'm not blocked!"


class ImplicitlyLockedApp(_App):

    @cp.expose
    def index(self):
        cp.session['foo'] =  'bar'
        return "I've just set the session content to {}".format(dict(cp.session))


class ExplicitlyLockedApp(_App):

    @cp.expose
    def index(self):
        # This method can be blocked by /i/block because of the
        # acquire_lock/release_lock calls.
        cp.session.acquire_lock()
        cp.session['foo'] =  'bar'
        cp.session.release_lock()
        return "I've just set the session content to {}".format(dict(cp.session))


if __name__ == '__main__':
    cp.tree.mount(ImplicitlyLockedApp(), '/i', config={
        '/': {
            'tools.sessions.on': True
        },
        '/unblocked_hi': { # Disable the session tool to avoid any locking
            'tools.sessions.on': False
        }
    })
    cp.tree.mount(ExplicitlyLockedApp(), '/e', config={
        '/': {
            'tools.sessions.on': True,
            'tools.sessions.locking': 'explicit' # This is the magic bit.
        },
        '/unblocked_hi': { # Rather irrelevant on this case
            'tools.sessions.on': False
        }
    })
    cp.engine.start()
    cp.engine.block()

【讨论】:

  • 谢谢,将禁用会话,看看效果如何。
猜你喜欢
  • 2014-09-01
  • 1970-01-01
  • 2012-12-27
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
  • 1970-01-01
  • 2016-06-18
  • 1970-01-01
相关资源
最近更新 更多