简单的Coder

Code is my life, that's it.

Python线程和gevent简易服务器实现对比

记前

最近项目上线,终于迎来了一个双休日,宅在家里眼瞅着肚子上的赘肉一圈一圈,实在不敢相信当年我是那个“怎么吃都不长肉”的瘦身少年。瘦子没法理解胖子的痛,瘦子终于有了同感。于是乎,待到晚上9点半开始了跑步,拉力器,仰卧起坐等一系列瘦身塑形锻炼。大汗淋漓地冲了一个澡,爽!

正文

用Python也有两年了,很少在生产中用到线程来设计程序,最近看到两篇不错的文章,准备自己记录一下。原文在这里:

插曲

MD,写到这里接到老大地bug报告,修完了一个SB地Bug回来继续。

单进程单线程服务器(无实用价值)

(webserver_single_thread.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import socket
import time

def sequential(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        handle_request(cli, time.sleep)

def handle_request(s, sleep):
    try:
        s.recv(1024)
        sleep(0.1)
        s.send('''http/1.0 200 OK
                  Hello World! ''')
        s.shutdown(socket.SHUT_WR)
        print '.',
    except Exception, ex:
        print ex
    finally:
        sys.stdout.flush()
        s.close()

if __name__ == '__main__':
    sequential(4444)

批注:没有人会用单线程做服务器,除非你是像我现在一样用来练习。

单进程多线程服务器(无实用价值)

(webserver_multi_threads.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import socket
import time
import threading

def threads(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        t = threading.Thread(target=handle_request, args=(cli, time.sleep))
        t.daemon = True
        t.start()

def handle_request(s, sleep):
    try:
        s.recv(1024)
        sleep(0.1)
        s.send('''http/1.0 200 OK
                  Hello World! ''')
        s.shutdown(socket.SHUT_WR)
        print '.',
    except Exception, ex:
        print ex
    finally:
        sys.stdout.flush()
        s.close()

if __name__ == '__main__':
    threads(4444)

批注:多线程之间的切换也是很耗资源的事情,所以就有了下面的线程池。

单进程线程池服务器(无实用价值)

(webserver_thread_pool.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import socket
import time
import threading
import Queue

def server(port, N):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    q = Queue.Queue()
    for x in range(N):
        t = threading.Thread(target=thread_worker, args=(q, ))
        t.daemon = True
        t.start()
    print 'Ready and waiting with %d threads on port %d' % (N, port)
    while True:
        cli, addr = s.accept()
        q.put(cli)

def thread_worker(q):
    sock = q.get()
    handle_request(sock, time.sleep)

def handle_request(s, sleep):
    try:
        s.recv(1024)
        sleep(0.1)
        s.send('''http/1.0 200 OK
                  Hello World! ''')
        s.shutdown(socket.SHUT_WR)
        print '.',
    except Exception, ex:
        print ex
    finally:
        sys.stdout.flush()
        s.close()

if __name__ == '__main__':
    server(4444, 5)

批注:受到Python的GIL的制约。

单进程gevent服务器(无实用价值)

(webserver_gevent.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import socket
import time
import gevent
from gevent import socket

def server(port):
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli, gevent.sleep)

def handle_request(s, sleep):
    try:
        s.recv(1024)
        sleep(0.1)
        s.send('''http/1.0 200 OK
                  Hello World! ''')
        s.shutdown(socket.SHUT_WR)
        print '.',
    except Exception, ex:
        print ex
    finally:
        sys.stdout.flush()
        s.close()

if __name__ == '__main__':
    server(4444)

批注:研究中。

gevent协程池服务器(无实用价值)

(webserver_gevent_pool.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import socket
import time
import gevent
from gevent import socket, sleep
from gevent.pool import Pool

def server(port, N):
    pool = Pool(N)
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        pool.spawn(handle_request, cli, gevent.sleep)

def handle_request(s, sleep):
    try:
        s.recv(1024)
        sleep(0.1)
        s.send('''http/1.0 200 OK
                  Hello World! ''')
        s.shutdown(socket.SHUT_WR)
        print '.',
    except Exception, ex:
        print ex
    finally:
        sys.stdout.flush()
        s.close()

if __name__ == '__main__':
    server(4444, 5)

批注:研究中。

Python性能分析指南

主题

介绍几种用于Python程序性能分析的工具和方法。

思路

一般情况下,要分析一段代码的性能,可以从下面几个问题入手:

  1. 目前执行效率如何?
  2. 效率瓶颈在哪里?
  3. 占用了多少内存?
  4. 内存消耗在哪里?

下面我们用几个工具来一一解答这些疑问。

Unix命令行工具: time

time是几乎所有*nix系统自带的一个工具,我们可以用它来对程序进行初步的分析:

1
2
3
4
5
$ time python yourprogram.py

real    0m1.028s
user    0m0.001s
sys     0m0.003s

输出的三条数据的具体含义可以参考这里:What do ‘real’, ‘user’ and ‘sys’ mean in the output of time(1)?。这里给出简单的说明:

  • real - 时钟时间。从程序开始到结束流逝的时间(actual elapsed time),包括其他进程占用的时间,比如IO等待时间。
  • user - 内核之外(用户空间)该进程占用时间(cpu time)。
  • sys - 该进程的内核执行时间(cpu time)

如果发现sysuser的时间之和远小于real就说明我们的程序很有可能在IO等待上花费了大量时间。

自定义Timer类

可以把时间输出到一个性能日志文件,比如profile.log,方便以后分析我们程序的性能瓶颈。

行执行时间分析:line_profiler

中文日志支持怎么样

很牛的嘛,中文都转换成拼音了。

来一段Gist

测试超链接样式

看图班