【问题标题】:How do i use argon2 asynchronously with python tornado?如何将 argon2 与 python 龙卷风异步使用?
【发布时间】:2020-04-09 05:21:50
【问题描述】:

这是我的登录页面的处理程序,我打算通过 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,我知道这是因为我正在调用的函数不返回未来对象,而是返回布尔值。

我的问题是如何在哈希过程的全部时间内异步使用哈希库而不会发生龙卷风阻塞,我知道这需要很长时间。

【问题讨论】:

  • 如果您的问题得到解答,请将其标记为solved

标签: python asynchronous hash tornado argon2-ffi


【解决方案1】:

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

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 设置。
如果您要使用浏览器进行测试,请注意可能的limitations

编辑

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

【讨论】:

  • 感谢您的努力。我知道龙卷风通常每个核心运行一个进程,如果我创建新线程,我不会撤销龙卷风的努力。我在想我可能必须编写一个与电机类似的工具,使用我认为的龙卷风 ioloop。
  • 我看到你在使用 ioloop 我无论如何都会奖励赏金
  • ioloop.run_in_executor 调用只是调度函数在池执行器中运行(函数本身不会在 ioloop 上运行,也不会阻塞它)。
  • 使用单独线程不影响龙卷风使用得当;当异步库不可用时,实际使用thread/process 池是运行阻塞代码的推荐方式;请参阅 Tornado 上的 第 3 点 FAQ
  • 很高兴听到这个消息;我也对argon2 进行了基准测试,但这有点误导,因为它本身会使用多线程,这意味着executor之间的每秒请求数相似call 和 classic blocking call 但是对于阻塞调用 ioloop 将在大多数时间被阻塞(即使是一个微不足道的请求也需要 2-3 秒),而对于 executor call 它是始终响应(微不足道的请求需要 3-4 毫秒)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多