Python - Execução Autobahn|Python / asyncio servidor websocket em separado subprocesso ou segmento

StackOverflow https://stackoverflow.com//questions/25063403

Pergunta

Eu tenho um tkinter GUI baseado em Python 3.4.1.Eu tenho várias threads em execução em um programa para obter dados JSON a partir de vários urls.Estou querendo adicionar alguns WebSocket funcionalidade para ser capaz de permitir que o programa de agir como um servidor e vários clientes para se conectar a ele através de uma WebSocket e troca de outros dados JSON.

Estou tentando usar o Autobahn|Python / servidor WebSocket para asyncio.

Eu tentei executar o asyncio ciclo de eventos em uma thread separada sob o programa GUI.No entanto, a cada tentativa dá 'AssertionError:Não há corrente de loop de eventos no segmento de 'Thread-1'.

Então eu tentei a geração de um processo com a biblioteca padrão de multiprocessamento pacote que executou o asyncio ciclo de eventos em outro Processo.Quando eu tentar isso eu não obter qualquer exceção, mas o servidor WebSocket não iniciar.

É possível até mesmo executar um asyncio ciclo de eventos em um subprocesso de outro programa Python?

Existe mesmo uma maneira de integrar uma asyncio ciclo de eventos em um multithread/tkinter programa?

ATUALIZAÇÃO Abaixo está o código real, eu estou tentando executar para um teste inicial.

from autobahn.asyncio.websocket import WebSocketServerProtocol
from autobahn.asyncio.websocket import WebSocketServerFactory
import asyncio
from multiprocessing import Process

class MyServerProtocol(WebSocketServerProtocol):

   def onConnect(self, request):
      print("Client connecting: {0}".format(request.peer))

   def onOpen(self):
      print("WebSocket connection open.")

   def onMessage(self, payload, isBinary):
      if isBinary:
         print("Binary message received: {0} bytes".format(len(payload)))

      else:
         print("Text message received: {0}".format(payload.decode('utf8')))

      ## echo back message verbatim
      self.sendMessage(payload, isBinary)

   def onClose(self, wasClean, code, reason):
      print("WebSocket connection closed: {0}".format(reason))

def start_server():
   factory = WebSocketServerFactory("ws://10.241.142.27:6900", debug = False)
   factory.protocol = MyServerProtocol
   loop = asyncio.get_event_loop()
   coro = loop.create_server(factory, '10.241.142.27', 6900)
   server = loop.run_until_complete(coro)
   loop.run_forever()
   server.close()
   loop.close()


websocket_server_process = Process(target = start_server)
websocket_server_process.start()

A maioria é em linha reta a partir do Autobahn|Python de código de exemplo para asyncio.Se eu tentar executá-lo como um Processo de não fazer nada, nenhum cliente pode se conectar a ele, se eu executar o comando netstat-um não existe porta 6900 sendo usado.Se usar apenas start_server() no programa principal cria o Servidor WebSocket.

Foi útil?

Solução

Primeiro, você está recebendo AssertionError: There is no current event loop in thread 'Thread-1'. porque asyncio exigir que cada thread no programa para ter o seu próprio ciclo de eventos, mas ele só irá criar automaticamente um loop de eventos para você na thread principal.Então, se você chamar asyncio.get_event_loop uma vez na thread principal, ele irá automaticamente criar um loop de objeto e defina-o como padrão para você, mas se você chamá-lo novamente em um segmento filho, você vai ter que erro.Em vez disso, você precisa criar explicitamente/definir o ciclo de eventos quando o thread inicia:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

Uma vez que você tiver feito isso, você deve ser capaz de usar get_event_loop() nesse segmento específico.

É possível iniciar um asyncio ciclo de eventos em um subprocesso iniciado através multiprocessing:

import asyncio
from multiprocessing import Process 

@asyncio.coroutine
def coro():
    print("hi")

def worker():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(coro())

if __name__ == "__main__":
    p = Process(target=worker)
    p.start()
    p.join()

Saída:

hi

A única ressalva é que, se você iniciar um ciclo de eventos no processo principal, bem como a sua criança, você precisa explicitamente criar/definir um novo ciclo de eventos em que a criança se você estiver em uma plataforma Unix (devido a uma bug em Python).Ele deve funcionar bem no Windows, ou se você usar o 'spawn' multiprocessing contexto.

Eu acho que deve ser possível iniciar uma asyncio ciclo de eventos em um segmento de plano de fundo (ou processo) de seu Tkinter aplicativo e tem tanto a tkinter e asyncio evento laço é executado lado a lado.Você só vai encontrar problemas se você tentar atualizar a interface de usuário do segmento de plano de fundo/processo.

Outras dicas

A resposta por @dano pode ser correta, mas cria um novo processo que é unnessesary na maioria das situações.

Eu achei essa pergunta no Google, porque eu tinha o mesmo problema sozinho.Eu tenho escrito um aplicativo onde eu queria uma api para websocket não ser executado no thread principal e isso causou o problema.

Eu encontrei a minha alternativo sollution simplesmente lendo sobre o evento de loops no python documentação e encontrada a asyncio.new_event_loop e asyncio.set_event_loop funções que resolveu esse problema.

Eu não uso AutoBahn, mas o pypi websockets biblioteca, e aqui está a minha solução

import websockets
import asyncio
import threading

class WebSocket(threading.Thread):    
    @asyncio.coroutine
    def handler(self, websocket, path):
        name = yield from websocket.recv()
        print("< {}".format(name))
        greeting = "Hello {}!".format(name)
        yield from websocket.send(greeting)
        print("> {}".format(greeting))

    def run(self):
        start_server = websockets.serve(self.handler, '127.0.0.1', 9091)
        eventloop = asyncio.new_event_loop()
        asyncio.set_event_loop(eventloop)
        eventloop.run_until_complete(start_server)
        eventloop.run_forever()

if __name__ == "__main__":
    ws = WebSocket()
    ws.start()

"Existe mesmo uma maneira de integrar uma asyncio ciclo de eventos em um multithread/tkinter programa?"

Sim, executar o tkinter programa com um asyncio loop de eventos.Prova de conceito.

'''Proof of concept integrating asyncio and tk loops.

Terry Jan Reedy
Run with 'python -i' or from IDLE editor to keep tk window alive.
'''

import asyncio
import datetime as dt
import tkinter as tk

loop = asyncio.get_event_loop()
root = tk.Tk()

# Combine 2 event loop examples from BaseEventLoop doc.
# Add button to prove that gui remain responsive between time updates.
# Prints statements are only for testing.

def flipbg(widget, color):
    bg = widget['bg']
    print('click', bg, loop.time())
    widget['bg'] = color if bg == 'white' else 'white'

hello = tk.Label(root)
flipper = tk.Button(root, text='Change hello background', bg='yellow',
                    command=lambda: flipbg(hello, 'red'))
time = tk.Label(root)
hello.pack()
flipper.pack()
time.pack()

def hello_world(loop):
    hello['text'] = 'Hello World'
loop.call_soon(hello_world, loop)

def display_date(end_time, loop):
    print(dt.datetime.now())
    time['text'] = dt.datetime.now()
    if (loop.time() + 1.0) < end_time:
        loop.call_later(1, display_date, end_time, loop)
    else:
        loop.stop()

end_time = loop.time() + 10.1
loop.call_soon(display_date, end_time, loop)

# Replace root.mainloop with these 4 lines.
def tk_update():
    root.update()
    loop.call_soon(tk_update)  # or loop.call_later(delay, tk_update)
# Initialize loop before each run_forever or run_until_complete call    
tk_update() 
loop.run_forever()

Eu tenho experimentalmente executar OCIOSO com essas 4 linhas extra, com uma lentidão só perceptível quando o realce de sintaxe de 1000 linhas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top