How does a cherry work? it handles requests well compared to tornadoes when the match is low

I tested the cherry test (using web.py as a framework) and the tornado by extracting web pages from the Internet.

I have three test cases using siege to send requests to the server (-c means the number of users, -t means the time of testing). The code is below the test results.

1. web.py (cherrpy)

  siege ip -c20 -t100s server can handle 2747requests siege ip -c200 -t30s server can handle 1361requests siege ip -c500 -t30s server can handle 170requests 

2. tornado synchronously

  siege ip -c20 -t100s server can handle 600requests siege ip -c200 -t30s server can handle 200requests siege ip -c500 -t30s server can handle 116requests 

3. tornado asynchronous

  siege ip -c20 -t100s server can handle 3022requests siege ip -c200 -t30s server can handle 2259requests siege ip -c500 -t30s server can handle 471requests 

performance analysis:

synchronous tornado <web.py (cherrypy) <tornado asynchronous

Question 1:

I know that using asynchronous architecture can significantly improve web server performance.

I am interested to know the difference between asynchronous tornado architecture and web.py (cherry).

I think the synchronous tornado mode handles requests one by one, but how does the cherry work using multiple threads? But I did not see a large increase in memory. Cherrypy can handle multiple requests at once. How does this allow a program lock?

Question 2:

Can I improve the performance of the tornado synchronous mode without using asynchronous methods? I think tornadoes can do better.

Web.py Code:

 import web import tornado.httpclient urls = ( '/(.*)', 'hello' ) app = web.application(urls, globals()) class hello: def GET(self, name): client = tornado.httpclient.HTTPClient() response=client.fetch("http://www.baidu.com/") return response.body if __name__ == "__main__": app.run() 

Tornado synchronously:

 import tornado.ioloop import tornado.options import tornado.web import tornado.httpclient from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class IndexHandler(tornado.web.RequestHandler): def get(self): client = tornado.httpclient.HTTPClient() response = client.fetch("http://www.baidu.com/" ) self.write(response.body) if __name__=='__main__': tornado.options.parse_command_line() app=tornado.web.Application(handlers=[(r'/',IndexHandler)]) http_server=tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start() 

asynchronous tornado:

 import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web import tornado.httpclient from tornado.options import define, options define("port", default=8001, help="run on the given port", type=int) class IndexHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): client = tornado.httpclient.AsyncHTTPClient() response = client.fetch("http://www.baidu.com/" ,callback=self.on_response) def on_response(self,response): self.write(response.body) self.finish() if __name__=='__main__': tornado.options.parse_command_line() app=tornado.web.Application(handlers=[(r'/',IndexHandler)]) http_server=tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start() 
+7
source share
2 answers

To answer question 1 ...

Tornado is single-threaded. If you block the main thread, as in your synchronous example, then this single thread cannot do anything until the lock call returns. This limits the synchronous example to one query at a time.

I am not particularly familiar with web.py, but looking at the source for my HTTP server seems to be using a streaming mixin, which suggests that it is not limited to handling one request at a time. When the first request arrives, it is processed by a single thread. This thread will block until the HTTP client call returns, but other threads can handle further incoming requests. This allows you to process multiple requests at once.

I suspect that you emulate this using Tornado, for example, by sending HTTP client requests to the thread pool, then you will see a similar bandwidth.

+2
source

Most of the processor time in your test code is significantly spent on client.fetch(...) - it effectively expects connections and incoming data to the socket, without blocking potential other Python threads.

Thus, your "performance evaluation" is mainly determined by the maximum number of effective threads of the handlers of the structure in question and the maximum number of parallel connections that the baidu server allows from your IP address.

wep.py a copy of CherryPyWSGIServer web.wsgiserver.CherryPyWSGIServer , which is used by default web.httpserver.runsimple() , uses streams by default - 10.
The thread does not increase memory usage here. Most of the memory is consumed by libraries and the Python interpreter here. And CherryPyWSGIServer (10), which processes worker threads, starts right at the beginning. The alternative web.httpserver.runbasic () also uses threads - via Python the built-in HTTPServer and SocketServer.ThreadingMixIn . This starts a new thread for each request. There is probably an "unlimited" number of threads - but there is overhead for starting a thread for each request.

Astronomical mode

tornado can also use more / unlimited threads (?), which may explain the difference between web.py here.

Your test does not say much about the speed of the servers and the processors themselves. You can simply increase the maximum number of threads in web.py CherryPyWSGIServer. Parallel execution of your client.fetch(...) is somehow necessary to get more "performance" here.

To test only server / framework speed (overhead), simply return a string or database query or a typical full web page displayed from local content.

A multithreaded web server for CPython-based applications in one process, finally, cannot use much more than one processor core (maybe 8 processor cores, usually today on server hardware) - because of the GIL in CPython, which is released only for some I / O. Therefore, if processor load becomes a factor (rather than network or database speed), you can consider the Jython or multiprocessor approach.

0
source

All Articles