0
  • 嗨,我正在Klein为我的 Web 服务器使用 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)
4

2 回答 2

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 treq klein 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"}]
...

如您所见,Klein没有阻止请求。

此外,如果将线程池大小减小到 2

reactor.suggestThreadPoolSize(2)

Klein 将执行前 2 个请求并等待,直到再次有空闲线程。

@notorious.no 建议的“异步替代方案”在这里讨论。

于 2019-12-28T18:16:55.377 回答
0

但克莱恩会等到单个请求完成后再处理另一个请求。

这不是真的。事实上,您提供的代码绝对没有问题。简单地运行您的示例服务器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

像这样的代码将按顺序处理请求,并且应该使用异步替代方案进行修改。

于 2016-12-31T07:55:44.703 回答