Одновременная обработка нескольких запросов и возврат результата с помощью модуля Klein Python

  • Привет я пользуюсь Klein Модуль Python для моего веб-сервера.
  • Мне нужно выполнить каждый запрос отдельно как поток, а также необходимо вернуть результат.
  • Но Кляйн ожидает завершения одного запроса, чтобы обработать другой запрос.
  • Я также пытался использовать deferToThread из витого модуля. Но он также обрабатывает запросы только после завершения первого запроса.
  • Точно так же я пытался @inlineCallbacks Метод это также дает тот же результат.

Примечание: этот метод отлично работает, когда нечего возвращать. Но мне нужно вернуть результат.

Здесь я приложил пример кода ниже,

import time
import klein
import requests
from twisted.internet import threads

def test():
    print "started"
    x = requests.get("http://google.com")
    time.sleep(10)
    return x.text

app = klein.Klein()

@app.route('/square/submit',methods = ['GET'])
def square_submit(request):
    return threads.deferToThread(test)

app.run('localhost', 8000)

1 ответ

Как предположил@notorious.no, код действителен и работает. Чтобы доказать это, посмотрите этот код

# app.py
from datetime import datetime
import json
import time
import random
import string
import requests
import treq
from klein import Klein
from twisted.internet import task
from twisted.internet import threads
from twisted.web.server import Site
from twisted.internet import reactor, endpoints

app = Klein()

def test(y):
    print(f"test called at {datetime.now().isoformat()} with arg {y}", )
    x = requests.get("http://www.example.com")
    time.sleep(10)

    return json.dumps([{
        "time": datetime.now().isoformat(),
        "text": x.text[:10],
        "arg": y
    }])

@app.route('/<string:y>',methods = ['GET'])
def index(request, y):
    return threads.deferToThread(test, y)

def send_requests():
    # send 3 concurrent requests
    rand_letter = random.choice(string.ascii_letters)
    for i in range(3):
        y = rand_letter + str(i)
        print(f"request send at {datetime.now().isoformat()} with arg {y}", )
        d = treq.get(f'http://localhost:8080/{y}')
        d.addCallback(treq.content)
        d.addCallback(lambda r: print("response", r.decode()))

loop = task.LoopingCall(send_requests)
loop.start(15) # repeat every 15 seconds

reactor.suggestThreadPoolSize(3)

# disable unwanted logs
# app.run("localhost", 8080)

# this way reactor logs only print calls
web_server = endpoints.serverFromString(reactor, "tcp:8080")
web_server.listen(Site(app.resource()))
reactor.run()

Установите treq и klein и запустите

$ python3.6 -m pip install treqklein requests
$ python3.6 app.py

Результат должен быть

request send at 2019-12-28T13:22:27.771899 with arg S0
request send at 2019-12-28T13:22:27.779702 with arg S1
request send at 2019-12-28T13:22:27.780248 with arg S2
test called at 2019-12-28T13:22:27.785156 with arg S0
test called at 2019-12-28T13:22:27.786230 with arg S1
test called at 2019-12-28T13:22:27.786270 with arg S2
response [{"time": "2019-12-28T13:22:37.853767", "text": "<!doctype ", "arg": "S1"}]
response [{"time": "2019-12-28T13:22:37.854249", "text": "<!doctype ", "arg": "S0"}]
response [{"time": "2019-12-28T13:22:37.859076", "text": "<!doctype ", "arg": "S2"}]
...

Как видите, Кляйн не блокирует запросы.

Кроме того, если вы уменьшите размер пула потоков до 2

reactor.suggestThreadPoolSize(2)

Кляйн выполнит первые 2 запроса и будет ждать, пока снова не появится свободный поток.

А "альтернатива" асинхронная, предложенная @notorious.no обсуждается здесь.

Но Кляйн ожидает завершения одного запроса, чтобы обработать другой запрос.

Это неправда. На самом деле, нет ничего плохого в том коде, который вы предоставили. Просто запустите ваш пример сервера на tcp:localhost:8000 и используя следующее curl команды, аннулирует вашу претензию:

curl http://localhost:8000/square/submit &    # run in background
curl http://localhost:8000/square/submit

Правильно ли я полагаю, что вы тестируете код в веб-браузере? Если да, то вы испытываете "особенность" большинства современных браузеров. Браузер будет делать один запрос на URL в определенный момент времени. Одним из способов решения этой проблемы в браузере было бы добавление фиктивной строки запроса в конце URL-адреса, например, так:

http://localhost:8000/squre/submit
http://localhost:8000/squre/submit?bogus=0
http://localhost:8000/squre/submit?bogus=1
http://localhost:8000/squre/submit?bogus=2

Тем не менее, очень распространенная ошибка, которую допускают новые разработчики Twisted/Klein, - это написание блокирующего кода, полагая, что Twisted волшебным образом сделает его асинхронным. Пример:

@app.route('/square/submit')
def square_submit():
    print("started")
    x = requests.get('https://google.com')    # blocks the reactor
    time.sleep(5)    # blocks the reactor
    return x.text

Подобный код будет обрабатывать запросы последовательно и должен быть изменен с помощью асинхронных альтернатив.

Другие вопросы по тегам