Python Tornado: использование внешней очереди из не сопрограмм - PullRequest
0 голосов
/ 30 октября 2018

У меня следующая ситуация: Использование python 3.6 и Tornado 5.1 для получения клиентских запросов через веб-сокет. Некоторые из этих запросов требуют, чтобы вы вызывали внешнюю обработку, которая возвращает очередь, а затем периодически помещает в нее результаты. Эти результаты передаются через websocket клиентам.

Внешняя обработка НЕ ​​является сопрограммой, поэтому я вызываю ее с помощью run_in_executor.

Моя проблема: Когда время отклика внешней обработки очень велико, run_in_executor достигает максимального числа рабочих (по умолчанию: число процессоров х 5) !

Безопасно ли увеличивать максимальное количество работников? Или другое решение рекомендуется? !!

Ниже упрощенный код.

От уже большое спасибо !!!!

#########################
##    SERVER CODE      ##
#########################


from random import randint
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
from random import randint
from tornado import gen
import threading
import asyncio
import queue
import time


class WSHandler(tornado.websocket.WebSocketHandler):
    """entry point for all WS request"""

    def open(self):
        print('new connection. Request: ' + str(self.request))

    async def on_message(self, message):

        # Emulates the subscription to an external object
        # that returns a queue to listen
        producer = Producer()
        q = producer.q

        while True:
            rta = await tornado.ioloop.IOLoop.current().run_in_executor(None, self.loop_on_q, q)
            if rta != None:
                await self.write_message(str(rta))
            else:
                break


    def on_close(self):
        print('connection closed. Request: ' + str(self.request) +
              '. close_reason: ' + str(self.close_reason) +
              '. close_code: ' + str(self.close_code) +
              '. get_status: ' + str(self.get_status()))


    def loop_on_q(self, q):
        rta = q.get()
        return rta

class Producer:

    def __init__(self):
        self.q = queue.Queue()
        t = threading.Thread(target=self.start)
        t.daemon = True
        t.start()

    def start(self):
        count = 1
        while True:
            # time.sleep(randint(1,5))
            if count < 100:
                self.q.put(count)
            else:
                self.q.put(None)
                break
            time.sleep(50)
            count += 1



application = tornado.web.Application([
    (r'/ws', WSHandler),
])

if __name__ == "__main__":
    asyncio.set_event_loop(asyncio.new_event_loop())
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    print('SRV START')
    tornado.ioloop.IOLoop.instance().instance().start()


#########################
##    CLIENT CODE      ##
#########################

# If you run it more than 20 times in less than 50 seconds ==> Block
# (number of processors x 5), I have 4 cores
from websocket import create_connection

def conect():
    url = 'ws://localhost:8888/ws'
    ws = create_connection(url)
    print('Conecting')
    return ws

print('Conecting to srv')
con_ws = conect()
print('Established connection. Sending msg ...')
msj = '{"type":"Socket"}'
con_ws.send(msj)
print('Package sent. Waiting answer...')

while True:
    result = con_ws.recv()
    print('Answer: ' + str(result))

1 Ответ

0 голосов
/ 30 октября 2018

Безопасно ли увеличивать максимальное количество работников Да, до определенного фиксированного количества, которое можно рассчитать с помощью нагрузочного тестирования.

Или другое решение рекомендуется? Если вы достигнете предела работников, вы можете переместить работников на несколько отдельных серверов (этот подход называется горизонтальным масштабированием) и передать им задания с помощью очереди сообщений. Если вы предпочитаете писать все самостоятельно, посмотрите на Celery как на батарейку или RabbitMQ, Kafka и т. Д. *

...