eventlet是一款使用Python编写的为高并发的网络编程而设计的库。它通过greenlet提供的协程功能,让开发者可以不用将以往的多线程等并发程序的开发方式转变成异步状态机模型,就能直接使用select/epoll/kqueue等操作系统提供的支持高并发IO接口,并且能尽可能地发挥它们在并发上的优势。
我用eventlet写一个服务端的socket接口,客户端的测试使用thread进行连接测试。代码如下。
服务端
#!/bin/python #-*- coding:utf-8 -*- # Filename: teve.py # Revision: 1.0 # Date: 2014-09-13 # Author: simonzhang # web: www.simonzhang.net # Email: simon-zzm@163.com ### END INIT INFO import eventlet from string import strip # 根据客户端的发送进行返回 def welcome(str): _str = str hui = "" if "%s" % _str == "hi": hui = "你好" elif "%s" % _str == "hello": hui = "hi" return hui def handle(client, add): error_count = 0 while True: c = "" c = client.recvfrom(2048)[0].strip() # 如果循环10次没有取到数据则断开 if len(c) == 0: pass error_count += 1 if error_count > 10: client.shutdown(2) break else: _re = welcome(c) error_count = 0 client.sendto(_re, add) # 启动监听1300端口 server = eventlet.listen(('0.0.0.0', 1300)) # 创建5000线程,如果不填默认1000 pool = eventlet.GreenPool(5000) # 循环监听 while True: new_sock, address = server.accept() pool.spawn_n(handle, new_sock, address)
客户端
#!/bin/python #-*- coding:utf-8 -*- # Filename: tcptest.py # Revision: # Date: 2014-09-13 # Author: simonzhang # web: www.simonzhang.net # Email: simon-zzm@163.com ### END INIT INFO ####加载多线程模块 import threading ####需要个随机数和延迟,为测试用 import random from time import sleep from socket import * HOST='192.168.1.112' #HOST='192.168.1.109' PORT=1300 BUFSIZE=1024 ADDR=(HOST, PORT) #### 多线程运行的测试部分。循环3次,每次间隔0到2的随机秒数, #### 等待后打印,运行总次数,线程数和循环值 def test_func(thread_number,sequence): print "thread %s" % thread_number tcpCliSock=socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) tcpCliSock.send("hi") data=tcpCliSock.recv(BUFSIZE) print data sleep(180) #tcpCliSock.send("hello") ##data=tcpCliSock.recv(BUFSIZE) #print data tcpCliSock.close() def main(): #### 定义循环序列,就是一个线程池 threads = [] #### 定义总共运行的次数 all_number = 100000 #### 定义运行所使用的线程数 thread_lines = 300 #### 定义开始线程数 start_line = 0 #### 首先构建线程池 for i in range(0,thread_lines): t = threading.Thread(target=test_func, args=(i,start_line,)) threads.append(t) start_line +=1 #### 运行第一批线程的任务 for t in threads: t.start() #### 循环运行全部任务 for number_line in xrange(start_line,all_number): #### 初始化当前线程的状态 thread_status = False #### 初始化检查循环线程的开始值 loop_line = 0 #### 开始循环检查线程池中的线程状态 while thread_status == False : #### 如果检查当前线程,如果线程停止,代表任务完成,则分配给此线程新任务, #### 如果检查当先线程正在运行,则开始检查下一个线程,直到分配完新任务。 #### 如果线程池中线程全部在运行,则开始从头检查 if threads[loop_line].isAlive() == False : t = threading.Thread(target=test_func, args=(loop_line,number_line,)) threads[loop_line]=t threads[loop_line].start() thread_status = True else: if loop_line >= thread_lines-1 : loop_line=0 else: loop_line+=1 if __name__ == "__main__": main()
测试过程。服务端放在阿里云服务器上,单CPU,内存1G,带宽3M,CentOS6.5 64位。使用pypy启动。操作系统参数如下:
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.all.arp_announce=2
vm.swappiness = 50
net.core.netdev_max_backlog = 2048
net.core.somaxconn = 250000
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2
net.ipv4.conf.lo.arp_announce=2
fs.file-max=12000000
fs.nr_open=11000000
ulimit -n 为 65535
客户端使用自己笔记本的虚拟机、raspberry pi和一台云主机。然后用bash脚本调用多个客户端,虚拟机上python启动10个客户端,在raspberry pi上使用pypy启动6个客户端。云主机上一个200连接。
启动后服务端系统指标如下图。
使用telnet连接1300端口信息回复正常。
查看系统日志如下,被认为是洪水攻击
Sep 13 22:34:06 iZ23i076qv9Z kernel: possible SYN flooding on port 1300. Sending cookies.
修改系统参数如下,问题暂时环节。
net.ipv4.tcp_max_syn_backlog = 4096
总结eventlet的性能确实不一般,因为连接后操作并不多,所以cpu基本没有使用,跑了一会,内存使用也下降到800M。操作系统使用内存300M到400M。
综上所述,当前连接1万连接应该没有问题。实际应用中根据单个务处理需要的CPU和并发数算出CPU需要量,内存为1G可以1到2万并发,C10K问题解决。golang也支持协程,但是不知道能不能高出一个数量级。