使用进程池加多线程的情况可以合理利用cpu,但是 会有些问题:
from multiprocessing import Pool
import threading
import requests
def test1():
s=requests.Session()#使用连接池访问,共用io复用
for i in range(10000):
try:
rep=s.get("http://www.163.com/")
print "test1"
except:
continue
def test2():
s=requests.Session()
for i in range(10000):
try:
rep=s.get("http://www.126.com/")
print "test2"
except:
continue
def coroutine():
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join()
if __name__=="__main__":
p=Pool()
for i in range(4):
p.apply_async(coroutine,args=())#使用非阻塞访问,主进程不阻塞,继续执行
p.close()
p.join()
上面有几个坑,
首先看下线程树:
python(16884)─┬─python(16889)─┬─{python}(16896)
│ └─{python}(16898)
├─python(16890)─┬─{python}(16897)
│ └─{python}(16899)
├─python(16891)─┬─{python}(16900)
│ └─{python}(16901)
├─python(16892)─┬─{python}(16902)
│ └─{python}(16903)
├─{python}(16893)
├─{python}(16894)
└─{python}(16895)
第一个是 ,不使用io复用,或者说,不使用requests的seesion,保持长连接的话, 使用strace抓包如下:
root@wan-ThinkPad-E450c:/home/wan/ceshi# strace -p 28501 -e trace=connect
strace: Process 28501 attached
connect(6, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.1.1")}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.132.121")}, 16) = 0
connect(6, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.133.47")}, 16) = 0
connect(6, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.133.48")}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.132.121")}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.1.1")}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.133.48")}, 16) = 0
connect(6, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.132.121")}, 16) = 0
connect(6, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.133.47")}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.133.48")}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.1.1")}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.133.47")}, 16) = 0
connect(6, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.214.133.48")}, 16) = 0
connect(6, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("183.
每个线程会疯狂的创建连接,这里,有可能耗尽连接。
第二个,这里是否有类似pipeline的方式,批量发请求,针对数据库。
第三个,这里ctrl +c 已经不能正常退出了。使用join会挂起主进程,而信号只有主进程能接受,而join后的主进程,根本就接受不到。
有些比较大的程序,比如datadog,通常做法是启动时,将pid写到pid文件中,程序响应stop的时候,去pid文件中取pid,然后kill 掉自身。
还有种思路是:设置子线程为daemon线程,启动子线程后主线程调用is_Alive的方法手动模拟join过程。
最后,其实靠ctrc+c的方式stop,并不是很常见的做法,一般将主进程写成守护进程。
第四个,是没超时控制,比如watchdog的方式,控制重启进程池。
第五个,如果kill掉主进程,会使子进程成为孤儿进程。设置Daemon关键字可以使主进程退出时子进程一起退出。
第六个,这里输出是stdio,而生产中是输出到log,要考虑多进程,线程日志同步的问题。