这是我的登录页面的处理程序,我打算通过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
,我知道这是因为我调用的函数没有返回future对象,而是返回布尔值。
我的问题是如何在没有龙卷风阻塞的情况下异步使用哈希库来完成整个哈希过程,我知道这在设计上需要很长时间。
您可以使用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
计算来模拟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,但您应该测试它。