2015-08-17 21 views
20

Python 3.4'te bir websocket sunucusu oluşturmak için websockets kütüphanesini kullanıyorum. İşte size basit bir yankı sunucusu var: Python - eşzamanlı olarak asyncio kullanarak birden fazla koroutin nasıl çalıştırılır?

import asyncio 
import websockets 

@asyncio.coroutine 
def connection_handler(websocket, path): 
    while True: 
     msg = yield from websocket.recv() 
     if msg is None: # connection lost 
      break 
     yield from websocket.send(msg) 

start_server = websockets.serve(connection_handler, 'localhost', 8000) 
asyncio.get_event_loop().run_until_complete(start_server) 
asyncio.get_event_loop().run_forever() 

en biz diyelim - ek - bazı olay yaşandığında müşteriye mesaj göndermek istedik. Basitlik için, her 60 saniyede bir periyodik olarak bir mesaj gönderelim. Bunu nasıl yapalım? Yani, connection_handler sürekli gelen mesajlar için beklediği için, sunucu sadece dan sonra eylemi yapabilir, istemciden bir mesaj aldı, değil mi? Burada neyi özlüyorum?

Belki de bu senaryoda, koroutine dayalı bir olay yerine olaylara/geri çağrılara dayalı bir çerçeve gerekir. Tornado?

cevap

22

TL; DR Kullanım asyncio.ensure_future() aynı anda birden fazla coroutines çalıştırmak için.


Belki bu senaryo olaylar/geri aramaları yerine değiş tokuş eden kavramlar dayalı birine dayanarak bir çerçeve gerektirir? Kasırga?

Hayır, bunun için başka bir çerçeveye ihtiyacınız yoktur. Senkronize olan senkronize olmayan uygulamaların tümü, sonuç beklenirken engellenmemesidir. Koroutinler veya geri aramalar kullanılarak nasıl uygulandığı önemli değildir.

Yani, connection_handler sürekli gelen iletileri beklediği için, sunucu yalnızca istemciden bir mesaj aldıktan sonra işlem yapabilir, değil mi? Burada neyi özlüyorum? Eşzamanlı uygulamada, (aldığınız gibi) ileti alıncaya kadar tüm uygulamayı engelleyecek olan msg = websocket.recv() gibi bir şey yazacaksınız. Ancak asenkron uygulamada tamamen farklı.

msg = yield from websocket.recv()'u yaptığınızda şöyle bir şey söyleyebilirsiniz: connection_handler()'un yürütmesini askıya al, websocket.recv() bir şey üretene kadar. yield from'un coroutine içinde kullanılması, denetim döngüsünü olay döngüsüne geri döndürür; bu nedenle, websocket.recv() sonucunu beklerken diğer bazı kodlar çalıştırılabilir. Koroutinin nasıl çalıştığını daha iyi anlamak için lütfen documentation'a bakın.

Diyelim ki - ek olarak - bir olay meydana geldiğinde müşteriye bir mesaj göndermek istedik. Basitlik için, her 60 saniyede bir periyodik olarak bir mesaj gönderelim. Bunu nasıl yapalım?

İstediğiniz starting event loop için engelleme çağrısı yürütmeden önce, birçok coroutines çalıştırmak için asyncio.async() kullanabilirsiniz.

import asyncio 

import websockets 

# here we'll store all active connections to use for sending periodic messages 
connections = [] 


@asyncio.coroutine 
def connection_handler(connection, path): 
    connections.append(connection) # add connection to pool 
    while True: 
     msg = yield from connection.recv() 
     if msg is None: # connection lost 
      connections.remove(connection) # remove connection from pool, when client disconnects 
      break 
     else: 
      print('< {}'.format(msg)) 
     yield from connection.send(msg) 
     print('> {}'.format(msg)) 


@asyncio.coroutine 
def send_periodically(): 
    while True: 
     yield from asyncio.sleep(5) # switch to other code and continue execution in 5 seconds 
     for connection in connections: 
      print('> Periodic event happened.') 
      yield from connection.send('Periodic event happened.') # send message to each connected client 


start_server = websockets.serve(connection_handler, 'localhost', 8000) 
asyncio.get_event_loop().run_until_complete(start_server) 
asyncio.async(send_periodically()) # before blocking call we schedule our coroutine for sending periodic messages 
asyncio.get_event_loop().run_forever() 

Örnek bir istemci uygulamasıdır. Ad girmenizi, eko sunucusundan geri almanızı ister, sunucudan iki ileti daha bekler (periyodik mesajlarımız) ve bağlantıyı kapatır.

import asyncio 

import websockets 


@asyncio.coroutine 
def hello(): 
    connection = yield from websockets.connect('ws://localhost:8000/') 
    name = input("What's your name? ") 
    yield from connection.send(name) 
    print("> {}".format(name)) 
    for _ in range(3): 
     msg = yield from connection.recv() 
     print("< {}".format(msg)) 

    yield from connection.close() 


asyncio.get_event_loop().run_until_complete(hello()) 

Önemli noktalar: Python 3.4.4 asyncio.async() yılında

  1. asyncio.ensure_future() olarak yeniden adlandırıldı.
  2. delayed calls zaman planlaması için özel yöntemler vardır, ancak bunlar koroutinler ile çalışmazlar. Burada mükemmel örneğini gördüğümüz kadar
+2

Harika cevap, teşekkürler! Koroutinlerin ne olduğunu anlıyorum, ama hala başımı asyncio çerçevesinin etrafında tutmaya çalışıyorum. Cevabınız çok yardımcı oldu. – weatherfrog

İlgili konular