0

这是我的登录页面的处理程序,我打算通过 ajax 发布请求使用它。

from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError

class AdminLoginHandler(RequestHandler):
    async def post(self):
        username = self.get_argument("username")
        password = self.get_argument("password")
        db_hash =  await self.settings['db'].users.find_one({"username":username}, {"password":1})
        if not db_hash:
            await self.settings['hasher'].verify("","")
            self.write("wrong")
            return
        try:
            print(db_hash)
            pass_correct = await self.settings['hasher'].verify(db_hash['password'], password)
        except VerifyMismatchError:
            pass_correct = False
        if pass_correct:
            self.set_secure_cookie("user", username)
            self.write("set?")
        else:
            self.write("wrong")

设置包括此参数hasher=PasswordHasher()

我收到以下错误TypeError: object bool can't be used in 'await' expression,我知道这是因为我正在调用的函数不返回未来对象而是布尔值。

我的问题是我如何异步使用散列库而不会在散列过程的全部时间内被龙卷风阻塞,我知道这在设计上需要很长时间。

4

1 回答 1

2

您可以使用 aThreadPoolExecutor或 aProcessPoolExecutor在单独的线程/进程中运行耗时的代码:

import math
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor

import tornado.ioloop
import tornado.web


def blocking_task(number):
    return len(str(math.factorial(number)))


class MainHandler(tornado.web.RequestHandler):

    executor = ProcessPoolExecutor(max_workers=4)
    # executor = ThreadPoolExecutor(max_workers=4)

    async def get(self):
        number = 54545  # factorial calculation takes about one second on my machine
        # result = blocking_task(number)  # use this line for classic (non-pool) function call
        result = await tornado.ioloop.IOLoop.current().run_in_executor(self.executor, blocking_task, number)
        self.write("result has %d digits" % result)


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])


if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

我在这里使用了一个简单的factorial计算来模拟 CPU 密集型任务,并使用wrk测试了上述内容:

wrk -t2 -c4 -d30s http://127.0.0.1:8888/
Running 30s test @ http://127.0.0.1:8888/
  2 threads and 4 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.25s    34.16ms   1.37s    72.04%
    Req/Sec     2.54      3.40    10.00     83.75%
  93 requests in 30.04s, 19.89KB read
Requests/sec:      3.10
Transfer/sec:     678.00B

如果没有执行者,我会得到大约 1 个请求/秒;当然,您需要max_workers根据您的设置调整设置。
如果您要使用浏览器进行测试,请注意可能存在的限制

编辑

我修改了代码以轻松允许进程执行器而不是线程执行器,但我怀疑它会对您的情况产生很大影响,主要是因为对 argon2 的调用应该释放 GIL,但您仍然应该对其进行测试。

于 2020-04-13T10:49:58.447 回答