瀏覽器/爬蟲都是socket 客戶端python
如何提升併發? 多線程 :IO 多進程:計算git
一.IO多路複用程序員
做用:檢測多個socket 是否發生變化(是否鏈接成功/是否獲取數據) (可讀/可寫)github
regests 模塊web
ret=requests.get('https://www.baidu.com/s?wd=alex') #DNS解析,根據域名解析出IP 後面的爲搜索關鍵字
socket面試
1 #socket 發送請求 2 import socket 3 cilent=socket.socket() 4 cilent.connect(("www.baidui.com",80)) 5 cilent.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') 6 data=cilent.recv(8096) 7 lst=[] 8 while 1: 9 if not data: 10 break 11 lst.append(data) 12 list=b"".join(lst) 13 list.decode("utf8")
b"".join(lst) 使列表結成字符串瀏覽器
2.多線程解決併發 多線程
1 import socket 2 import requests 3 def func(key): 4 cilent=socket.socket() 5 cilent.connect(("www.baidui.com",80)) 6 cilent.sendall(b'GET /s?wd=%s HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n' % key) 7 data=cilent.recv(8096) 8 lst=[] 9 while 1: 10 if not data: 11 break 12 lst.append(data) 13 list=b"".join(lst) 14 list.decode("utf8") 15 import threading 16 l=["zq","wa","wd"] 17 18 for item in l: 19 t1=threading.Thread(target=func,args=(item)) 20 t1.start()
缺點: 這個須要等待,由於有阻塞,會在客戶端connect,recv 時刻須要等待客戶端 ,怎樣才能不等待呢???併發
IO多路複用+socket 實現併發請求 (一個線程100個請求)app
這裏用到 client.setblocking(False)
1 client = socket.socket() 2 client.setblocking(False) # 將原來阻塞的位置變成非阻塞(報錯) 3 # 百度建立鏈接: 阻塞 4 5 try:#須要 避免 6 client.connect(('www.baidu.com',80)) # 執行了但報錯了 7 except BlockingIOError as e: 8 pass
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IO多路複用+socket 實現併發請求
1 import socket 2 import select 3 4 client1 = socket.socket() 5 client1.setblocking(False) # 百度建立鏈接: 非阻塞 6 7 try: 8 client1.connect(('www.baidu.com',80)) 9 except BlockingIOError as e: 10 pass 11 12 client2 = socket.socket() 13 client2.setblocking(False) # 百度建立鏈接: 非阻塞 14 try: 15 client2.connect(('www.sogou.com',80)) 16 except BlockingIOError as e: 17 pass 18 19 client3 = socket.socket() 20 client3.setblocking(False) # 百度建立鏈接: 非阻塞 21 try: 22 client3.connect(('www.oldboyedu.com',80)) 23 except BlockingIOError as e: 24 pass 25 26 socket_list = [client1,client2,client3]#建立鏈接列表,有三個 27 conn_list = [client1,client2,client3]#再建立 28 29 while True: 30 rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)#rlist 31 # wlist中表示是否已經和socket創建鏈接對象,有返回值,可寫 返回cilent 32 #rlist中表示是否已經和socket有返回數據,有返回值,可讀 返回cilent 33 #[] 中 將錯誤信息返回空列表 34 #0.005 最大0.005秒檢測錯誤 35 for sk in wlist: 36 if sk == client1: 37 sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') 38 elif sk==client2: 39 sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n') 40 else: 41 sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n') 42 conn_list.remove(sk) 43 for sk in rlist: 44 chunk_list = [] 45 while True: 46 try: 47 chunk = sk.recv(8096) 48 if not chunk:# 49 break 50 chunk_list.append(chunk) 51 except BlockingIOError as e: 52 break 53 body = b''.join(chunk_list) 54 # print(body.decode('utf-8')) 55 print('------------>',body) 56 sk.close() 57 socket_list.remove(sk) 58 if not socket_list:#這裏指當列表中的cilent被取到時,把他們移出做爲判斷 59 #直到把最後都取出 60 break
用面向對象作IO多路複用
import socket import select class Req(object): def __init__(self,sk,func): self.sock = sk self.func = func def fileno(self): return self.sock.fileno() class Nb(object): def __init__(self): self.conn_list = [] self.socket_list = [] def add(self,url,func): client = socket.socket() client.setblocking(False) # 非阻塞 try: client.connect((url, 80)) except BlockingIOError as e: pass obj = Req(client,func) self.conn_list.append(obj) self.socket_list.append(obj) def run(self): while True: rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005) # wlist中表示已經鏈接成功的req對象 for sk in wlist: # 發生變換的req對象 sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') self.conn_list.remove(sk) for sk in rlist: chunk_list = [] while True: try: chunk = sk.sock.recv(8096) if not chunk: break chunk_list.append(chunk) except BlockingIOError as e: break body = b''.join(chunk_list) # print(body.decode('utf-8')) sk.func(body) sk.sock.close() self.socket_list.remove(sk) if not self.socket_list: break def baidu_repsonse(body): print('百度下載結果:',body) def sogou_repsonse(body): print('搜狗下載結果:', body) def oldboyedu_repsonse(body): print('老男孩下載結果:', body) t1 = Nb() t1.add('www.baidu.com',baidu_repsonse) t1.add('www.sogou.com',sogou_repsonse) t1.add('www.oldboyedu.com',oldboyedu_repsonse) t1.run()
3.協程
概念:
進程,線程 操做系統中存在
協程,是由程序員創造出來的,不是一個單獨存在的東西
協程:是爲線程,對一個線程進行分片,使得線程在代碼塊之間來回切換執行,而不是在原來逐行執行
1 import greenlet 2 def func1(): 3 print(11) 4 f2.switch() 5 print(22) 6 f2.switch() 7 def func2(): 8 print(33) 9 f1.switch() 10 print(44) 11 f1=greenlet.greenlet(func1) 12 f2=greenlet.greenlet(func2) 13 f1.switch()
可是單獨的協程沒有什麼做用
協程只是單純的分片,可是用在線程上就能夠節省寶貴的時間,
可是沒法判斷是不是IO進行分片,這時候就須要 pip3 install gevent
1 from gevent import monkey 2 monkey.patch_all()#之後代碼中遇到IO都會自動執行greenlet的switch進行切換 3 import requests 4 import gevent 5 def f1(url): 6 ret=requests.get(url) 7 print(url,ret.content) 8 def f2(url): 9 ret=requests.get(url) 10 print(url,ret.content) 11 def f3(url): 12 ret=requests.get(url) 13 print(url,ret.content) 14 gevent.joinall([ 15 gevent.spawn(f1,'https://www.python.org/'),#協程1 16 gevent.spawn(f2, 'https://www.yahoo.com/'), 17 gevent.spawn(f3, 'https://www.github.com/'), 18 ])
總結:
1. 什麼是協程?
協程也能夠稱爲「微線程」,就是開發者控制線程執行流程,控制先執行某段代碼而後再切換到另外函執行代碼...來回切換。
2. 協程能夠提升併發嗎?
協程本身自己沒法實現併發(甚至性能會下降)。
協程+IO切換性能提升。
3. 進程、線程、協程的區別?
4. 單線程提供併發:
- 協程+IO切換:gevent
- 基於事件循環的異步非阻塞框架:Twisted
手動實現協程:yield關鍵字生成器
1 def f1(): 2 print(11) 3 yield 4 print(22) 5 yield 6 print(33) 7 8 def f2(): 9 print(55) 10 yield 11 print(66) 12 yield 13 print(77) 14 15 v1 = f1() 16 v2 = f2() 17 18 next(v1) # v1.send(None) 19 next(v2) # v1.send(None) 20 next(v1) # v1.send(None) 21 next(v2) # v1.send(None) 22 next(v1) # v1.send(None) 23 next(v2) # v1.send(None)
整體總結
1 ''' 2 1.本質上socket 3 2.如何提升併發 ? 4 多進程:計算 多線程:io 5 一. 6 1.經過客戶端協議也能夠瀏覽網頁 7 2.協議 遵循 8 3.間接能夠用requests 模塊 或者用 socket 模塊來引用 9 4.cilent.setblocking(False)將原來阻塞的位置變成非阻塞(報錯) 10 報錯捕獲一下 try except BlockingIOError as e: 11 做用:單線程遇到io 不等待 12 須要檢測是否鏈接 13 *** io多路複用做用: 檢測socket是否已將發生變化 14 (是否已經鏈接成功/是否已經獲取數據) (可度/可寫) 15 *** 16 5. select.select()[] >>報錯寫入 0.005 >內部最多0.005檢測錯誤 17 6.已經實現的模塊 Twisted 基於事件循環實現的異步非阻塞框架 18 還用封裝實現 (沒聽好) 19 7.總結; 20 1.socket默認阻塞,阻塞體如今 21 connect recv 22 2. 如何讓socket變成非阻塞 23 setblocking(False) 24 3. io多路複用 25 檢測多個socket是否發生變化 26 4. 系統檢測socket是否發生變化,三種模式 27 select "最多1024個socket;循環檢測 28 poil: (主動查詢)不限制監聽sockrt個數;循環檢測(慢)水平觸發 29 epoil : (舉手,本身完成主動通知) 回調方式 邊緣觸發 30 Python模塊: 31 select.select 32 select.epoll 33 34 5.提升併發方案: 35 多進程 36 多線程 37 異步非阻塞模塊(Twisted) scrapy框架(單線程完成併發) 38 5.提升併發方案: 39 控制好線程個數 40 生產者消費者模型 41 6.什麼是 異步非阻塞? 42 -非阻塞 ,不等待 43 好比建立socket對某個地址進行connect、獲取接收數據recv時默認都會等待(鏈接成功或接收到數據),才執行後續操做。 44 若是設置setblocking(False),以上兩個過程就再也不等待,可是會報BlockingIOError的錯誤,只要捕獲便可。 45 異步: 46 返回一個函數繼續調用, 47 通知,執行完成後自動執行回調函數局或自動執行某種操做 48 7.什麼是同步阻塞? 49 阻塞 ,等 50 同步,按照順序逐步執行 51 52 不如建立socket對某個地址 53 54 55 三. 56 協程,是由程序員創造出來的不是真實存在的東西 57 由用戶控制 58 協程,就是微線程,對一個線程進程分片,使得線程在代碼塊之間來回切換執行, 59 而不是在原來逐行執行 60 單純的協程無用 61 協程的存在乎義:相似於單線程實現併發:遇到IO + 協程的切換能夠提升性能 62 3.1 63 協程+IO切換>>站起來了 pip3 install gevent 64 from gevent import monkey 65 monkey.path_all() #默認將代碼遇到的IO 66 總結: 67 1.什麼是協程? 68 2.協程能夠提升併發麼? 69 3.進程,線程,協程的區別面試題? 70 4.單線程提供併發 71 -協程+IO切換 :gevent 72 -基於事件循環的異步非阻塞框架: Twisted 73 74 也能夠經過yield 75 76 之後gevent Twisted 寫代碼 77 三種模式: 78 select 79 poil 80 epoil