Cherrypy:处理时间长的页面都有哪些解决方案

Posted

技术标签:

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

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

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

【问题讨论】:

【参考方案1】:

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

穷人的解决方案

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

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

CherryPy 后台任务

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

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

工业解决方案

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

示例

下面是带有 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)

【讨论】:

以上是关于Cherrypy:处理时间长的页面都有哪些解决方案的主要内容,如果未能解决你的问题,请参考以下文章

网页中元素都有哪些定位方式,各自的含义是啥

处理 Mozilla Firefox 中的“可见性:折叠”错误都有哪些好的解决方法?

Cherrypy 如何处理用户线程?

大数据分析工具都有哪些,有啥特点?

Sitemesh 都有哪些替代方案来帮助在 Spring MVC 应用程序中布局 JSP/JSTL 页面页脚/页眉?

如何从运行尽可能快的 CherryPy BackgroundTask 返回数据