前言
在下面这个博客里,介绍了tornado的异步TCPServer和TCPClient。
http://yunjianfei.iteye.com/blog/2186646
那么,tornado的性能到底怎么样呢?让我们来做一个性能测试吧。
这里推荐关于C10K问题的一个文章:http://www.kegel.com/c10k.html
准备工作
在开始性能测试前,需要修改一些相关的系统参数。
一.修改MAX open files
首先运行下面命令,查看单个进程运行时,能够打开的最多文件描述符(即FD)
本人的运行结果为: 65535
这个数值有点小,如果一个进程打开10万个文件或者FD,就会出现以下错误:
那么,我们开始修改。
打开 /etc/security/limits.conf 并添加(如果已经存在,则修改数值)
* hard nofile 1000000
修改完毕后,重新开一个shell窗口,或者重新登录shell就会使设置生效。
再次运行ulimit -n 可以看到已经是1000000
二.解决TCP的TIME_WAIT问题
如果在短时间内,TCPServer收到大量的连接(比如2万以上),并且这些连接迅速关闭,就会在linux系统中,发现大量状态为TIME-WAIT的TCP连接,可以通过以下命令来查看服务器中TCP连接的状态。
服务器中如果出现了大量的TIME-WAIT,导致的直接后果就是,TIME-WAIT状态的TCP连接占用的资源(比如端口号)不能得到及时释放,如果端口号被耗尽,则无法建立新的连接。
在开始测试前,我们修改一些系统参数,来缓解TIME_WAIT问题。
执行以下几个命令:
sysctl -w net.ipv4.tcp_tw_recycle=1
sysctl -w net.ipv4.tcp_max_tw_buckets=5000
sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.ip_local_port_range="1024 65000"
具体的我这里就不解释了,有兴趣的同学可以自行去查找资料。
测试代码
tcp_server.py
#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- from tornado import ioloop, httpclient, gen from tornado.gen import Task from tornado.tcpserver import TCPServer import pdb, time, logging from tornado import stack_context from tornado.escape import native_str #Init logging def init_logging(): logger = logging.getLogger() logger.setLevel(logging.DEBUG) sh = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s -%(module)s:%(filename)s-L%(lineno)d-%(levelname)s: %(message)s') sh.setFormatter(formatter) logger.addHandler(sh) logging.info("Current log level is : %s", logging.getLevelName(logger.getEffectiveLevel())) class MyServer(TCPServer): def __init__(self, io_loop=None, **kwargs): TCPServer.__init__(self, io_loop=io_loop, **kwargs) def handle_stream(self, stream, address): TCPConnection(stream, address, io_loop=self.io_loop) class TCPConnection(object): count = 0 def __init__(self, stream, address, io_loop): self.io_loop = io_loop self.stream = stream self.address = address self.address_family = stream.socket.family self.EOF = b' END' self._clear_request_state() self._message_callback = stack_context.wrap(self._on_message) self.stream.set_close_callback(self._on_connection_close) self.stream.read_until(self.EOF, self._message_callback) def _on_timeout(self): #logging.info("Send message..") self.write("Hello client!" + self.EOF) TCPConnection.count += 1 #logging.info("Has treated : %s", TCPConnection.count) if TCPConnection.count == 0 or TCPConnection.count % 2000 == 0: logging.info("Has treated : %s", TCPConnection.count) def _on_message(self, data): try: timeout = 5 data = native_str(data.decode('latin1')) #logging.info("Received: %s", data) #self.io_loop.add_timeout(self.io_loop.time() + timeout, self._on_timeout) self._on_timeout() except Exception, ex: logging.error("Exception: %s", str(ex)) def _clear_request_state(self): """Clears the per-request state. """ self._write_callback = None self._close_callback = None def set_close_callback(self, callback): """Sets a callback that will be run when the connection is closed. """ self._close_callback = stack_context.wrap(callback) def _on_connection_close(self): if self._close_callback is not None: callback = self._close_callback self._close_callback = None callback() self._clear_request_state() def close(self): self.stream.close() # Remove this reference to self, which would otherwise cause a self._clear_request_state() def write(self, chunk, callback=None): """Writes a chunk of output to the stream.""" if not self.stream.closed(): self._write_callback = stack_context.wrap(callback) self.stream.write(chunk, self._on_write_complete) def _on_write_complete(self): if self._write_callback is not None: callback = self._write_callback self._write_callback = None callback() def main(): init_logging() server = MyServer() server.listen(8001) ioloop.IOLoop.instance().start() if __name__ == "__main__": try: main() except Exception, ex: print "Ocurred Exception: %s" % str(ex) quit()
tcp_client.py
#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- from tornado import ioloop, httpclient, gen from tornado.gen import Task import pdb, time, logging import tornado.ioloop import tornado.iostream import socket from tornado import stack_context #Init logging def init_logging(): logger = logging.getLogger() logger.setLevel(logging.DEBUG) sh = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s -%(module)s:%(filename)s-L%(lineno)d-%(levelname)s: %(message)s') sh.setFormatter(formatter) logger.addHandler(sh) logging.info("Current log level is : %s", logging.getLevelName(logger.getEffectiveLevel())) class TCPClient(object): test_start = False max_connected = 20000 test_num = 20000 test_remain = test_num test_count = 0 total_count = 0 shutdown = False def __init__(self, host, port, io_loop=None): self.host = host self.port = port self.io_loop = io_loop self.shutdown = False self.stream = None self.sock_fd = None self.EOF = b' END' def get_stream(self): self.sock_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) self.stream = tornado.iostream.IOStream(self.sock_fd) self.stream.set_close_callback(self.on_close) def connect(self): self.get_stream() self.stream.connect((self.host, self.port), self.send_message) def on_receive(self, data): #logging.info("Received: %s", data) self.stream.close() self.stream = None self.sock_fd = None TCPClient.test_count += 1 try_testing(self.io_loop) def on_close(self): TCPClient.total_count += 1 if TCPClient.total_count % 2000 == 0: logging.info("Has treat: %s", TCPClient.total_count) #logging.info("----count: %s %s %s", TCPClient.total_count, TCPClient.shutdown, TCPClient.test_num) if TCPClient.shutdown and TCPClient.total_count == TCPClient.test_num: logging.info("shutdown") self.io_loop.stop() def send_message(self): #logging.info("Send message....") self.stream.write(b"Hello Server!" + self.EOF) self.stream.read_until(self.EOF, self.on_receive) #logging.info("After send....") def set_shutdown(self): TCPClient.shutdown = True def _handle_exception(typ, value, tb): logging.info("%s %s %s", typ, value, tb) return True def try_testing(io_loop): #logging.info("test_count: %s, test_remain: %s", TCPClient.test_count, TCPClient.test_remain) if TCPClient.test_start and TCPClient.test_count >= TCPClient.max_connected and TCPClient.test_remain > 0: TCPClient.test_start = False if not TCPClient.test_start: TCPClient.test_count = 0 TCPClient.test_start = True gen = None if TCPClient.test_remain >= TCPClient.max_connected: gen = start_test(io_loop, TCPClient.max_connected) elif TCPClient.test_remain > 0: gen = start_test(io_loop, TCPClient.test_remain) if gen: with stack_context.ExceptionStackContext(_handle_exception): c = gen.next() while True: c = gen.send(c) def start_test(io_loop, count): TCPClient.test_remain = TCPClient.test_remain - count logging.info("Will start %s testing! Remain: %s", count, TCPClient.test_remain) for i in range(count): c = yield TCPClient("127.0.0.1", 8001, io_loop) if i == (count-1) and TCPClient.test_remain <= 0: c.set_shutdown() c.connect() def main(): init_logging() io_loop = tornado.ioloop.IOLoop.instance() try_testing(io_loop) logging.info("**********************start ioloop******************") io_loop.start() if __name__ == "__main__": try: main() except Exception, ex: print "Ocurred Exception: %s" % str(ex) quit()
上面的client代码中,可以修改test_num这个数值,来改变测试case的数目,上面设置成了20000
测试结果
tcp_server:
2015-02-27 17:01:30,544 -server:server.py-L55-INFO: Has treated : 2000
2015-02-27 17:01:30,807 -server:server.py-L55-INFO: Has treated : 4000
2015-02-27 17:01:31,242 -server:server.py-L55-INFO: Has treated : 6000
2015-02-27 17:01:31,510 -server:server.py-L55-INFO: Has treated : 8000
2015-02-27 17:01:31,738 -server:server.py-L55-INFO: Has treated : 10000
2015-02-27 17:01:32,220 -server:server.py-L55-INFO: Has treated : 12000
2015-02-27 17:01:32,452 -server:server.py-L55-INFO: Has treated : 14000
2015-02-27 17:01:32,743 -server:server.py-L55-INFO: Has treated : 16000
2015-02-27 17:01:33,146 -server:server.py-L55-INFO: Has treated : 18000
2015-02-27 17:01:38,342 -server:server.py-L55-INFO: Has treated : 20000
可以看到,总体来说,server的性能还是不错的,总共8s时间,处理了2万个请求+响应。
上面的测试结果中有个地方让我非常疑惑,最后2000个请求,中间居然耗费了5s,我在tornado的ioloop.py中加了一些debug语句跟踪后,发现是在33s的时候,卡在了ioloop的下面这一行
也就是说,epoll在等待IO事件到来,有点想不明白为什么在这里等了5s。经过多次测试,这种情况是完全可以复现的,还有待进一步调查。
相关推荐
`tornado.tcpserver` 模块提供了一个简单的 TCP 服务器实现,它可以使用 `IOStream` 来处理每个连接,非常适合构建实时通信服务。 #### 与其他服务的集成 **4.1 tornado.auth — 使用 OpenID 和 OAuth 进行第三方...
- **导入必要的类**:在`socket_server.py`中,我们导入`TCPServer`类,它是Tornado处理TCP连接的基础。 - **定义Connecter类**:这是处理客户端连接的核心类。它维护了一个客户端集合,实现了连接建立、消息接收...
1. **HTTP服务器(Tornado HTTPServer)**:Tornado HTTPServer基于epoll或kqueue事件模型,能够处理大量并发请求,提供高性能的服务。 2. **Web服务器网关接口(WSGI)**:Tornado支持WSGI,这使得它可以与其他...
龙卷风-测试-c10m 支持 TCP/WebSocket 的简单回显服务器和客户端。 使用龙卷风。 应该在一台主机上运行。目标有很多关于设置 C10M(10,000,000 个并发连接)服务器的文章,您可以找到代码示例,通常支持 WebSocket。...
在这个项目中,我们关注的是一个由我开发的"Python Game Server",它专为个人使用而设计,未经测试和运行。尽管如此,这个项目仍能为我们提供关于构建游戏服务器的宝贵见解。 1. **Python作为游戏服务器语言** ...
- **TCP测试程序**: - 检查TCP协议下的数据包传递情况。 #### LCD实验 - **ugldemo**: - 展示图形用户界面的基本功能。 - **winHello**: - 一个简单的欢迎界面展示。 - **wexbasic**: - 提供基本的窗口操作...
7. **测试和调试**:在实际开发中,对SIP功能的测试和调试是非常关键的步骤。这包括模拟不同网络条件、模拟不同SIP服务器的响应,以及使用调试工具检查程序运行状态。 8. **应用案例**:SIP在物联网、智能家居、...
为了评估 Node.js 的性能,这里提供了一组基准测试结果,将其与 nginx、Thin 和 Tornado 等其他流行的服务端框架进行比较。测试环境为 Linux,使用 Intel Core 2 Duo 2.53GHz 处理器和 4GB 内存。 - **标准的...
- **Node.js**:根据示例测试,Node.js在30线程并发下的平均响应速度约为7000 rps左右。 - **Tornado**:同样条件下,Tornado的平均响应速度约为3000 rps。 综上所述,Node.js以其独特的非阻塞I/O模型、高效的数据...
- **配置 Target Server:** 在 Tornado 中设置 Target Server 的信息,以便能够与目标硬件进行通信。 **3.4.4 工程运行** - **运行:** 在目标硬件上运行 DAM 项目,验证其功能。 #### 四、系统接口实验 **4.1 任务...
在实现"python http挡板"时,应根据业务需求选择合适的Web框架和并发模型,并进行适当的性能测试和调优,以确保系统能够稳定且高效地运行。文件名"moniqi"可能是该项目的源代码文件,里面可能包含了具体的实现细节,...
1. **Tornado开发平台**:Tornado是Wind River提供的一个集成开发环境(IDE),用于VxWorks应用程序的开发、调试和测试。通过Tornado,用户可以方便地创建、编译和调试VxWorks项目。 2. **Builder工具**:用于项目的...
实际项目中,"texas-holdem-server"可能包含更多的设计细节和技术实现,例如使用特定的框架(如Node.js的Socket.IO或Python的Tornado)、游戏逻辑的具体算法等。对于深入理解,需要查阅项目的源代码和文档。
自1981年成立以来,风河系统公司已经成为嵌入式行业的领头羊之一。 **1.2 实时操作系统VxWorks简介** VxWorks是由风河系统公司开发的一款高性能、高可靠性的实时操作系统(RTOS)。它支持多种微处理器架构,并以其...
Python异步学习是一个重要的主题,尤其对于开发高性能网络服务或者处理大量并发操作的开发者来说,理解和掌握异步编程是至关重要的。在这个压缩包文件“python 异步学习.zip”中,可能包含了关于Python异步编程的...
1. **TCP/IP 协议**:理解 TCP/IP 协议栈的工作原理对于网络通信的调试和优化非常重要。 2. **HTTP 协议**:HTTP 是 Web 开发中最常用的协议之一,掌握 HTTP 协议的工作原理,包括状态码、请求方法等,对于开发 ...