【问题标题】:Cherrypy : which solutions for pages with large processing timeCherrypy:处理时间长的页面有哪些解决方案
【发布时间】:2014-05-03 00:49:18
【问题描述】:

我有一个由cherrypy 提供支持的网站。对于某些页面,我需要相当长的处理时间(数百万行数据库上的多连接 SQL 请求)。处理有时需要 20 秒或更长时间,浏览器会因为时间过长而崩溃。

我想知道这里有什么好的解决方案。

【问题讨论】:

    标签: python cherrypy long-running-processes


    【解决方案1】:

    这里的一切都取决于网站的数量。 CherryPy 是一个线程服务器,一旦每个线程都在等待数据库,新的请求将不会被处理。请求队列也有方面,但总的来说是这样。

    穷人的解决方案

    如果您知道您的流量很小,您可以尝试解决方法。如果需要,增加 response.timeout(默认为 300 秒)。增加server.thread_pool(默认为 10)。如果您在 CherryPy 应用程序前使用预留代理,如 nginx,也请增加代理超时时间。

    以下解决方案将要求您重新设计您的网站。特别是让它异步,客户端代码发送一个任务,然后使用拉或推来获得它的结果。这将需要在电线的两侧进行更改。

    CherryPy 后台任务

    您可以在服务器端使用cherrypy.process.plugins.BackgroundTask 和一些中间存储(例如数据库中的新表)。用于拉取的 XmlHttpRequest 或用于推送到客户端的 WebSockets。 CherryPy 可以同时处理这两种情况。

    请注意,由于 CherryPy 在单个 Python 进程中运行,后台任务的线程也会在其中运行。如果你做一些 SQL 结果集后处理,你会受到GIL 的影响。所以你可能想重写它以使用进程,这有点复杂。

    工业解决方案

    如果您的网站运行或被视为大规模运行,您最好考虑使用分布式任务队列,例如 RqCelery。它使服务器端与众不同。客户端是相同的拉或推。

    示例

    下面是带有 XHR 轮询的 BackgroundTags 的玩具实现。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    
    import time
    import uuid
    
    import cherrypy
    from cherrypy.process.plugins import BackgroundTask
    
    
    config = {
      'global' : {
        'server.socket_host' : '127.0.0.1',
        'server.socket_port' : 8080,
        'server.thread_pool' : 8,
      }
    }
    
    
    class App:
    
      _taskResultMap = None
    
    
      def __init__(self):
        self._taskResultMap = {}
    
      def _target(self, task, id, arg):
        time.sleep(10) # long one, right?
        try:
          self._taskResultMap[id] = 42 + arg
        finally:
          task.cancel()
    
      @cherrypy.expose
      @cherrypy.tools.json_out()
      def schedule(self, arg):
        id = str(uuid.uuid1())
        self._taskResultMap[id] = None
        task = BackgroundTask(
          interval = 0, function = self._target, args = [id, int(arg)], 
          bus = cherrypy.engine)
        task.args.insert(0, task)
        task.start()
        return str(id)
    
      @cherrypy.expose
      @cherrypy.tools.json_out()
      def poll(self, id):
        if self._taskResultMap[id] is None:
          return {'id': id, 'status': 'wait', 'result': None}
        else:
          return {
            'id'     : id, 
            'status' : 'ready', 
            'result' : self._taskResultMap.pop(id)
          }
    
      @cherrypy.expose
      def index(self):
        return '''<!DOCTYPE html>
          <html>
          <head>
            <title>CherryPy BackgroundTask demo</title>
            <script type='text/javascript' 
              src='http://cdnjs.cloudflare.com/ajax/libs/qooxdoo/3.5.1/q.min.js'>
            </script>
            <script type='text/javascript'>
              // Do not structure you real JavaScript application this way. 
              // This callback spaghetti is only for brevity.
    
              function sendSchedule(arg, callback)
              {
                var xhr = q.io.xhr('/schedule?arg=' + arg);
                xhr.on('loadend', function(xhr) 
                {
                  if(xhr.status == 200)
                  {
                    callback(JSON.parse(xhr.responseText))
                  }
                });
                xhr.send();
              };
    
              function sendPoll(id, callback)
              {
                var xhr = q.io.xhr('/poll?id=' + id);
                xhr.on('loadend', function(xhr) 
                {
                  if(xhr.status == 200)
                  {
                    callback(JSON.parse(xhr.responseText))
                  }
                });
                xhr.send();
              }
    
              function start(event)
              {
                event.preventDefault();
    
                // example argument to pass to the task
                var arg = Math.round(Math.random() * 100);
    
                sendSchedule(arg, function(id)
                {
                  console.log('scheduled (', arg, ') as', id);
                  q.create('<li/>')
                    .setAttribute('id', id)
                    .append('<span>' + id + ': 42 + ' + arg + 
                      ' = <img src="http://sstatic.net/Img/progress-dots.gif" />' + 
                      '</span>')
                    .appendTo('#result-list');
    
                  var poll = function()
                  {
                    console.log('polling', id);
                    sendPoll(id, function(response)
                    {
                      console.log('polled', id, '(', response, ')');
                      if(response.status == 'wait')
                      {
                        setTimeout(poll, 2500);
                      }
                      else if(response.status == 'ready')
                      {
                        q('#' + id)
                          .empty()
                          .append('<span>' + id + ': 42 + ' + arg + ' = ' + 
                            response.result + '</span>');
                      }
                    });
                  };
                  setTimeout(poll, 2500);
                });
              }
    
              q.ready(function()
              {
                q('#run').on('click', start);
              });
            </script>
          </head>
          <body>
            <p>
              <a href='#' id='run'>Run a long task</a>, look in browser console.
            </p>
            <ul id='result-list'></ul>
          </body>
          </html>
        '''
    
    
    if __name__ == '__main__':
      cherrypy.quickstart(App(), '/', config)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      • 2023-03-03
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多