Tornado学习记录五

因为目前是运维岗, tornado 主要是用来接收请求并执行检测任务,比如说,我起一个服务,用户将服务器IP或者主机名 Post 到 tornado, 然后 tornado 执行检测,比如就是 Ping,然后返回给用户结果。 假设是 ping -c 10 url,这个还是挺耗时的,在mac上测试大概 9s 左右,如果不是异步,这 9s 的时间服务器就不能响应其他请求,那这个服务显然是没法用的, 这时候就需要 tornado 的异步非阻塞了。

而执行 bash shell 这种,是需要开启多线程或多进程的,Ping 这种操作, 直觉上是多进程好一些, 因为可以利用多核,多线程的版本没有测试过。有时间会补上的,tornado.process.Subprocess 封装了原生的 subprocess.Popen, 增加了对 IOStream 的支持。

torando with subprocess PIPE

import tornado.ioloop
import tornado.web
from subprocess32 import PIPE
from tornado.gen import coroutine, Return
from tornado.process import Subprocess

class MainHandler(tornado.web.RequestHandler):
    @coroutine
    def get(self):
        res = yield self._work()
        self.write(res)

    @coroutine
    def _work(self):
        command = ‘ping -c 10 www.cnode.com‘
        p = Subprocess([command], stdout=PIPE, shell=True)
        yield p.wait_for_exit()
        raise Return(p.stdout.read())

if __name__ == "__main__":
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    application.listen(6000)
    tornado.ioloop.IOLoop.instance().start()

tornado with subprocess STREAM

import tornado.ioloop
import tornado.web
from tornado.gen import coroutine, Return
from tornado.process import Subprocess

class MainHandler(tornado.web.RequestHandler):
    @coroutine
    def get(self):
        res = yield self._work()
        self.write(res)

    @coroutine
    def _work(self):
        command = ‘ping -c 10 www.cnode.com‘
        p = Subprocess([command], stdout=Subprocess.STREAM, stderr=Subprocess.STREAM, shell=True)
        out = yield p.stdout.read_until_close()
        raise Return(out)

if __name__ == "__main__":
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    application.listen(6000)
    tornado.ioloop.IOLoop.instance().start()

In addition to using tornado.process.Subprocess as dano suggests, you should use stdout=tornado.process.Subprocess.STREAM instead of PIPE, and read from stdout/stderr asynchronously. Using PIPE will work for small amounts of output, but you will deadlock in wait_for_exit() if you use PIPE and the subprocess tries to write too much data (used to be 4KB but the limit is higher in most modern linux systems).

stackoverflow 上老外对于 tornado.process.Subprocess 的使用建议,PIPE在处理大量的数据时会造成死锁,所以推荐使用用STREAM。

nodejs express child_process.exec

"use strict";

var express = require(‘express‘);
var request = require(‘superagent‘);
var app = express();
var exec = require(‘child_process‘).exec;

app.get("/", function (req, res) {
    exec("ping -c 10 www.cnode.com", function (err, stdout, stderr) {
        if(err) throw err;

        res.send(stdout);
    });
});

var server = app.listen(5000);

不论怎么看都是 nodejs 更舒服吧, 要不收拾收拾东西离职算了, tornado 这货真的写不来啊。

一般来说,会使用 ab 进行压力测试。 leader 一般会问,你写的这东西并发能有多少啊?性能怎样啊,如何如何?我们需要回答这个问题。

ping -c 10 www.cnode.com 的耗时,9.6s

time ping -c 10 www.cnode.com
...
--- www.cnode.com ping statistics ---
10 packets transmitted, 10 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 271.707/276.759/282.695/3.782 ms
ping -c 10 www.cnode.com  0.00s user 0.00s system 0% cpu 9.604 total

ab test 很简单,就是按照设定好的并发数目,发送给定数量的请求,接收请求,并计算相关的指标。

测试之前,先想象一下,我们对结果的预期是啥? 因为是异步的,并发的请求会同时得到处理,所以处理1个请求应该跟处理多个请求响应时间相同,直到达到系统能力极限,即系统无法同时处理那么多 Ping。 所以,测试的方案如下, 并发(-c)从1开始,依次 1,5,10,20,30,总共处理(-n)30个请求。

ab -c 1 -n 30 http://127.0.0.1:6000/
...
Requests per second:    0.11 [#/sec] (mean)
Time per request:       9506.393 [ms] (mean)
Time per request:       9506.393 [ms] (mean, across all concurrent requests)

ab -c 5 -n 30 http://127.0.0.1:6000/
...
Requests per second:    0.35 [#/sec] (mean)
Time per request:       14476.120 [ms] (mean)
Time per request:       2895.224 [ms] (mean, across all concurrent requests)

ab -c 10 -n 30 http://127.0.0.1:6000/
...
Requests per second:    0.62 [#/sec] (mean)
Time per request:       16117.301 [ms] (mean)
Time per request:       1611.730 [ms] (mean, across all concurrent requests)

ab -c 20 -n 30 http://127.0.0.1:6000/
...
Requests per second:    1.60 [#/sec] (mean)
Time per request:       12496.166 [ms] (mean)
Time per request:       624.808 [ms] (mean, across all concurrent requests)

ab -c 30 -n 30 http://127.0.0.1:6000/
...
Requests per second:    3.16 [#/sec] (mean)
Time per request:       9485.194 [ms] (mean)
Time per request:       316.173 [ms] (mean, across all concurrent requests)

最先看到这结果的时候,我很诧异,觉得完全没有异步么,5并发的时候才 qps 才 0.35. 正常 100+ 没有任何问题吧?

Requests per second

This is the number of requests per second. This value is the result of dividing the number of requests by the total time taken

这个值实际上是  完成的请求总数 / 总测试时间

Time per request

The average time spent per request. The first value is calculated with the formula concurrency * timetaken * 1000 / done while the second value is calculated with the formula timetaken * 1000 / done

第一个 Time per request: 并发数 * 耗时 * 1000 / 完成的请求总数

第二个 Time per request: 耗时 * 1000 / 完成的请求总数

ab test 是测试网站性能的,不假,但是设计上,一般的网站,是绝不会 9s 才返回的, 除了高峰时段的12306。换句话说,如果我们想看到的是 server 在检测性能下降之前能承担多少并发,-c 和 -n 的参数应该相等, 或者 -n 参数值是 -c 参数值的倍数是一定的。

ab -c 30 -n 30 http://127.0.0.1:6000/
...
Requests per second:    3.16 [#/sec] (mean)
Time per request:       9485.194 [ms] (mean)
Time per request:       316.173 [ms] (mean, across all concurrent requests)

ab -c 40 -n 40 http://127.0.0.1:6000/
...
Requests per second:    3.60 [#/sec] (mean)
Time per request:       11122.398 [ms] (mean)
Time per request:       278.060 [ms] (mean, across all concurrent requests)

ab -c 50 -n 50 http://127.0.0.1:6000/
...
Requests per second:    5.26 [#/sec] (mean)
Time per request:       9513.345 [ms] (mean)
Time per request:       190.267 [ms] (mean, across all concurrent requests)

ab -c 100 -n 100 http://127.0.0.1:6000/
...
Requests per second:    10.30 [#/sec] (mean)
Time per request:       9705.259 [ms] (mean)
Time per request:       97.053 [ms] (mean, across all concurrent requests)

ab -c 200 -n 200 http://127.0.0.1:6000/
...
Requests per second:    19.23 [#/sec] (mean)
Time per request:       10402.855 [ms] (mean)
Time per request:       52.014 [ms] (mean, across all concurrent requests)

并发数 * 第二个 Time per request 的值应该等于 ping -c 10 的总时间,9.7s 左右。

可以看到,在200的时候,这个值大于10s,说明达到了服务器 Ping 的极限。所以,可以跟 leader 汇报说,单台服务器同时相应 200 检测请求问题不大。

如果机器配置高一些,核心数,内存,带宽,那么并发还可以开到更高,当然,这只是个简陋的测试。

坚持不下去了,有 nodejs 为什么我要用 tornado 啊,python 对我最大的价值应该是抓取数据和与 bash 配合,而不是写什么 web 服务,退一万步说,有 php 在, 轮得到你 python 么?

时间: 2024-11-10 16:10:20

Tornado学习记录五的相关文章

Tornado学习记录二

Coroutines Coroutines are the recommended way to write asynchronous code in Tornado. Coroutines use the Python yield keyword to suspend and resume execution instead of a chain of callbacks (cooperative lightweight threads as seen in frameworks like g

Linux 学习记录 五(软件的安装升级)

一.gcc gcc是Linux上面最标准的C语言的编译程序,用来源代码的编译链接. gcc -c hello.c 编译产生目标文件hello.o gcc -O hello.c 编译产生目标文件,并进行优化 gcc -o hello hello.c 生成hello这个可执行的二进制文件 缺点:命令的冗余性,如果 C 语言程序中包含对其他函数或者程序的引用,那么其他程序也要编译成目标文件,然后一起编译成可执行文件,才能运行成功.一个大的程序引用是非常多的,所以用gcc来编译,显得很冗余. 二.mak

Ansible学习记录五:PlayBook学习

0.介绍 Playbooks 是 Ansible 管理配置.部署应用和编排的语言,可以使用 Playbooks 来描述你想在远程主机执行的策略或者执行的一组步骤过程等 类似于一组任务集,定义好像项目,组织结构,配置文件等信息,通过task将所要做的事情一步一步的组织在一起就是完整的Playbook 官方Demo资料:https://github.com/ansible/ansible-examples Playbooks是采用YMAL语言结构,基础语法请参考:http://docs.ansibl

产品需求文档的学习记录(五)

在产品和技术领域里都有UML的技能知识,而对于产品人员的UML则更多的是指用例图,也就是我所称呼的用户流程图.在讲PRD文档写作的第二篇文章里,我提到了用户流程图的制作,实际上用户流程图是我在产品规则的初期对用例图的一种结构化的表达方式,由于以结构化的方式描述用例太抽象,缺少逻辑性表达,并且那篇文章更偏向于功能性用户流程,还不是实际意义上的用例,因此今天我补文一篇,细讲一下UML用例图和用例文档. 用例文档是由多个用例组成的一份文档,主要用于技术开发与测试使用,他是PRD中的重要辅助文档,用于讲

Tornado学习记录四

Structure of a Tornado web application A Tornado web application generally consists of one or more RequestHandler subclasses, an Application object which routes incoming requests to handlers, and a main() function to start the server. tornado 应用通常包括至

Oracle学习记录 五 Centos6.4 64bit下安装oracle

错误记录: Invalid source path '../stage/Components/oracle.jdk/1.5.0.17.0/1/DataFiles' specified for unzip. 这个错误应该是我只解压了第一个压缩包,没有解压第二个. 2. 还有一个问题就是按照开始安装的时候,说什么color的问题,这个我在网上看了些,不过,我直接把系统登出一次,重新用oracle登录就没事了. 3. [INS-32021] Insufficient disk space on thi

loadrunner学习记录五

1.HTTP的GET请求 这里以访问百度为例,地址http://www.baidu.com/s?wd=mobile,表示在百度上搜索mobile.具体脚本如下(有注释) Lr script代码   Action() { int status; lr_start_transaction("send"); web_reg_find("Search=Body",//这里说明在Body的范围内查找 "SaveCount=ret_Count",//这里表示

Spring学习记录(五)---bean的作用域scope

作用域:singleton:单例,整个应用中只创建一个实例(默认) prototype:原型,每次注入时都新建一个实例 session:会话,每个会话创建一个实例 request:请求,每个请求创建一个实例 默认情况下,bean都是单例的,在下面这样时初始化,调用各个bean的构造函数 1 <bean id="person" class="com.guigu.spring.autowire.Person" 2 p:name="Tom" &g

SpringMVC学习记录(五)--表单标签

在使用SpringMVC的时候我们可以使用Spring封装的一系列表单标签,这些标签都可以访问到ModelMap中的内容.下面将对这些标签一一介绍. 1.引入标签头文件 在正式介绍SpringMVC的表单标签之前,我们需要先在JSP中声明使用的标签,具体做法是在JSP文件的顶部加入以下指令: <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %> 2.form标签 使