Как я могу использовать argon2 асинхронно с python торнадо? - PullRequest
0 голосов
/ 09 апреля 2020

Вот обработчик для моей страницы входа, который я намерен использовать с помощью 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().

I ' я получаю следующую ошибку TypeError: object bool can't be used in 'await' expression, я знаю, что это потому, что функция, которую я вызываю, возвращает не будущий объект, а логический.

Мой вопрос: как я могу использовать библиотеку хеширования асинхронно без торнадо в течение полного времени процесса хеширования, который, как я знаю, по дизайну занимает много времени.

1 Ответ

2 голосов
/ 13 апреля 2020

Вы можете использовать ThreadPoolExecutor или ProcessPoolExecutor для запуска трудоемкого кода в отдельных потоках / процессах:

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 для моделирования нагрузки на процессор Задача и тестирование выше, используя 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, но вы все равно должны его протестировать .

...