Cherrypy:处理时间大的页面的解决方案 [英] Cherrypy : which solutions for pages with large processing time

查看:176
本文介绍了Cherrypy:处理时间大的页面的解决方案的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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

解决方案

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



可怜的人的解决方案



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



以下解决方案将需要您重新设计您的网站。特别要使其异步,客户端代码发送任务,然后使用pull或push获取其结果。



CherryPy BackgroundTask



您可以使用 cherrypy.process.plugins .BackgroundTask 和服务器端的一些中间存储(例如数据库中的新表)。用于pull或WebSockets的XmlHttpRequest推送到客户端。请注意,由于CherryPy在单个Python进程中运行,因此后台任务的线程也将在其中运行。



如果您执行一些SQL结果集后处理,您将受到 GIL 的影响。所以你可能需要重写它来使用进程,这是一个更复杂的一个。



工业解决方案



如果您的网站运行或被视为规模经营,您最好考虑一个分布式任务队列,如 Rq 芹菜。它使服务器端有所不同。客户端是相同的拉或推。



示例



以下是 BackgroundTags 与XHR轮询。

 #!/ usr / bin / env python 
# - * - 编码: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,
}
}


类App:

_taskResultMap =无


def __init __(self):
self._taskResultMap = {}

def _task(self, id,arg):
time.sleep(10)#long one,right?
self._taskResultMap [id] = 42 + arg

@ cherrypy.expose
@ cherrypy.tools.json_out()
def schedule(self,arg)
id = str(uuid.uuid1())
self._taskResultMap [id] =无
BackgroundTask(1,self._task,(id,int(arg)),bus = cherrypy .engine).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演示< / title>
< script type ='text / javascript'src ='http://cdnjs.cloudflare.com/ajax/libs/qooxdoo/3.5.1/q.min.js'>< / script>
< script type ='text / javascript'>
//不要以这种方式构建你真正的JavaScript应用程序。
//这个回调意大利面只是为了简洁。

函数sendSchedule(arg,callback)
{
var xhr = q.io.xhr('/ schedule?arg ='+ arg);
xhr.on('loadend',function(xhr)
{
if(xhr.status == 200)
{
回调(JSON.parse(xhr) .responseText))
}
});
xhr.send();
};

函数sendPoll(id,callback)
{
var xhr = q.io.xhr('/ poll?id ='+ id);
xhr.on('loadend',function(xhr)
{
if(xhr.status == 200)
{
回调(JSON.parse(xhr) .responseText))
}
});
xhr.send();
}

函数start(event)
{
event.preventDefault();

//传递给任务的示例参数
var arg = Math.round(Math.random()* 100);

sendSchedule(arg,function(id)
{
console.log('scheduled(',arg,')as',id);
q。 create('< li />')
.setAttribute('id',id)
.append(id +':< img src =http://sstatic.net/Img /progress-dots.gif/>')
.appendTo('#result-list');

var poll = function()
{
console.log('polling',id);
sendPoll(id,function(response)
{
console.log('polled',id''''''' ');
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'>运行长任务< / a>,浏览器控制台。< / p>
< ul id ='result-list'>< / ul>
< / body>
< / html>
''


如果__name__ =='__main__':
cherrypy.quickstart(App(),'/',config)


I have a website powered by cherrypy. For some pages, I need quite a long processing time (a multi-join SQL request on a several-million-row DB). The processing needs sometimes 20 seconds or more, and the browser get crashed because it is too long.

I'm wondering what would be a nice solution here.

解决方案

Everything here depends on a volume of the website. CherryPy is a threaded server and once every thread is waiting for database, new requests won't be processed. There's also aspect of request queue, but in general it is so.

Poor man's solution

If you know that you have small traffic you can try to workaround. Increase response.timeout if needed (default is 300 seconds). Increase server.thread_pool (defaults to 10). If you use reserve proxy, like nginx, in front of CherryPy application, increase proxy timeout there as well.

The following solutions will require you to redesign your website. Specifically to make it asynchronous, where client code sends a task, and then uses pull or push to get its result. It will require changes on both sides of the wire.

CherryPy BackgroundTask

You can make use of cherrypy.process.plugins.BackgroundTask and some intermediary storage (e.g. new table in your database) at server side. XmlHttpRequest for pull or WebSockets for push to client side. CherryPy can handle both.

Note that because CherryPy is run in single Python process, the background task's thread will run within it too. If you do some SQL result set post-processing, you will be affected by GIL. So you may want rewrite it to use processes instead, which is a little more complicated.

Industrial solution

If your website operates or is deemed to operate at scale, you are better to consider a distributed task queue like Rq or Celery. It makes server-side difference. Client side is the same pull or push.

Example

Here follows a toy implementation for BackgroundTags with XHR polling.

#!/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 _task(self, id, arg):
    time.sleep(10) # long one, right?
    self._taskResultMap[id] = 42 + arg

  @cherrypy.expose
  @cherrypy.tools.json_out()
  def schedule(self, arg):
    id = str(uuid.uuid1())
    self._taskResultMap[id] = None
    BackgroundTask(1, self._task, (id, int(arg)), bus = cherrypy.engine).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(id +  ': <img src="http://sstatic.net/Img/progress-dots.gif" />')
                .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:处理时间大的页面的解决方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆