2015-04-20 16 views
12

Tôi muốn kết nối với một danh sách nhiều trang web khác nhau rất nhanh. Im sử dụng asyncio để thực hiện việc này theo cách không đồng bộ và bây giờ muốn thêm thời gian chờ cho thời điểm kết nối sẽ bị bỏ qua nếu mất quá nhiều thời gian để phản hồi.Làm cách nào để thêm thời gian chờ kết nối với asyncio?

Làm cách nào để triển khai tính năng này?

import ssl 
import asyncio 
from contextlib import suppress 
from concurrent.futures import ThreadPoolExecutor 
import time 


@asyncio.coroutine 
def run(): 
    while True: 
     host = yield from q.get() 
     if not host: 
      break 

     with suppress(ssl.CertificateError): 
      reader, writer = yield from asyncio.open_connection(host[1], 443, ssl=True) #timout option? 
      reader.close() 
      writer.close() 


@asyncio.coroutine 
def load_q(): 
    # only 3 entries for debugging reasons 
    for host in [[1, 'python.org'], [2, 'qq.com'], [3, 'google.com']]: 
     yield from q.put(host) 
    for _ in range(NUM): 
     q.put(None) 


if __name__ == "__main__": 
    NUM = 1000 
    q = asyncio.Queue() 

    loop = asyncio.get_event_loop() 
    loop.set_default_executor(ThreadPoolExecutor(NUM)) 

    start = time.time() 
    coros = [asyncio.async(run()) for i in range(NUM)] 
    loop.run_until_complete(load_q()) 
    loop.run_until_complete(asyncio.wait(coros)) 
    end = time.time() 
    print(end-start) 

(Trên một sidenote: Có ai đó một ý tưởng làm thế nào để tối ưu hóa này?)

+0

Bạn quên 'thu hút từ' các lệnh gọi đến 'q.put (None)' bên trong 'load_q', vì vậy mã này sẽ không hoạt động như hiện được viết. – dano

+0

bạn không cần người đọc, nhà văn ở đây. Bạn có thể sử dụng 'asyncio.create_connection' với 'Protocol' mà không làm gì cả (nó đóng kết nối mạng ngay sau khi nó được thiết lập). Đây là [ví dụ mã mà tôi đã thử trên top triệu danh sách trang web Alexa] (http://stackoverflow.com/a/20722204/4279) (có thể hơi lỗi thời ví dụ, nó không sử dụng một số chức năng đối kháng như ' asyncio.wait_for() '). Nó sử dụng một luồng đơn và mở tối đa các kết nối ssl 'giới hạn'. – jfs

Trả lời

11

Bạn có thể quấn cuộc gọi đến open_connection trong asyncio.wait_for, cho phép bạn chỉ định một thời gian chờ:

with suppress(ssl.CertificateError): 
     fut = asyncio.open_connection(host[1], 443, ssl=True) 
     try: 
      # Wait for 3 seconds, then raise TimeoutError 
      reader, writer = yield from asyncio.wait_for(fut, timeout=3) 
     except asyncio.TimeoutError: 
      print("Timeout, skipping {}".format(host[1])) 
      continue 

Lưu ý rằng khi TimeoutError được nâng lên, số open_connection coroutine cũng bị hủy. Nếu bạn không muốn nó bị hủy bỏ (mặc dù tôi nghĩ rằng bạn làm muốn nó bị hủy trong trường hợp này), bạn đã kết thúc cuộc gọi theo số asyncio.shield.

+0

nhưng điều này cũng sẽ làm cho nó một cuộc gọi chặn không? Giống như mở các kết nối trong vòng lặp bình thường sau khi khác. – ali

+0

@ali Không, bởi vì tất cả các cuộc gọi đến phương thức 'run' được bao bọc trong một cuộc gọi 'asyncio.async', có nghĩa là tất cả chúng đều chạy đồng thời. – dano

+1

Nếu thời gian chờ kết nối cần phải nằm trong một coroutine khác, hãy xem [https://stackoverflow.com/questions/28609534/python-asyncio-force-timeout/48546189#48546189](Python asyncio force timeout) về việc xếp chồng 'asyncio.ensure_future (asyncio.wait_for (create_connection())) ' –

Các vấn đề liên quan