Socket+Select 實現單執行緒併發請求
阿新 • • 發佈:2020-08-02
Socket+Select 實現單執行緒併發請求
涉及知識點:
- socket 程式設計;
- http 協議中的請求和響應的基本格式;
- select
目的:
- 理解單執行緒併發請求的基本實現方式;
程式碼
import socket from urllib.parse import urlparse from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE class Connection: def __init__(self, url, loop): self.loop = loop self.selector = loop.selector self.loop.task_count += 1 url = urlparse(url) host_and_port = url.netloc.split(":") self.host = host_and_port[0] if len(host_and_port) == 2: self.port = int(host_and_port[1]) else: self.port = 80 self.path = url.path if self.path == "": self.path = "/" self.data = b"" # 建立socket連線 self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 連線建立成功後呼叫self.connected self.selector.register(self.client.fileno(), EVENT_WRITE, self.connected) self.client.connect((self.host, self.port)) self.client.setblocking(False) def connected(self, key): self.selector.unregister(key.fd) self.client.send( "GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(self.path, self.host).encode("utf8")) # 有資料返回時呼叫self.readable self.selector.register(self.client.fileno(), EVENT_READ, self.readable) def readable(self, key): d = self.client.recv(1024) if d: self.data += d else: self.selector.unregister(key.fd) resp_body = self.data.decode("utf8").split("\r\n\r\n")[1] self.loop.results.append(resp_body) self.client.close() class Loop: def __init__(self): # 任務數量 self.task_count = 0 self.stop = False self.selector = DefaultSelector() self.results = [] def run(self): while self.task_count > len(self.results): ready = self.selector.select() for key, mask in ready: key.data(key) return self.results if __name__ == "__main__": loop = Loop() base_url = "http://127.0.0.1:5001/items/{}" for i in range(1, 5): con = Connection(base_url.format(i), loop) res = loop.run() print(res) # 假設一個請求需要一秒, 如果使用傳統同步請求的方式,那麼5個請求就需要5秒中; # 使用程式碼中這種方式一共只需要1秒時間, 因為5個請求是同時發出去的.