Можно ли улучшить время отклика этого приложения-службы входа в систему, сделав его части асинхронными? - PullRequest
0 голосов
/ 08 февраля 2020

Я только что написал имя пользователя и выход из системы, но я пытаюсь выяснить, каков наиболее правильный способ сделать это. Судя по документации, есть способы сделать часть кода асинхронной, нужно ли мне это делать? Я включил функции хеширования (которые я получил из stackoverflow), так что это завершено и может быть построено для очень простых приложений.

import os
import sqlite3
import hashlib
import binascii
from tornado.ioloop import IOLoop
from tornado.web import Application, RequestHandler
from tornado.options import define, options


define('port', default=80, help='port to listen on')

settings = dict(
    template_path=os.path.join(os.path.dirname(__file__), "templates"),
    static_path=os.path.join(os.path.dirname(__file__), "static"),
    debug=True,
    cookie_secret="changethis",
    login_url="/login",
    # xsrf_cookies=True,
)

def hash_password(password):
    """Hash a password for storing."""
    salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
    pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), 
                                salt, 100000)
    pwdhash = binascii.hexlify(pwdhash)
    return (salt + pwdhash).decode('ascii')

def verify_password(stored_password, provided_password):
    """Verify a stored password against one provided by user"""
    salt = stored_password[:64]
    stored_password = stored_password[64:]
    pwdhash = hashlib.pbkdf2_hmac('sha512', 
                    provided_password.encode('utf-8'), 
                    salt.encode('ascii'), 100000)
    pwdhash = binascii.hexlify(pwdhash).decode('ascii')
    return pwdhash == stored_password

try:
    db = sqlite3.connect('file:aaa.db?mode=rw', uri=True)
except sqlite3.OperationalError:
    db = sqlite3.connect("aaa.db")
    db.execute("CREATE TABLE Users (id INTEGER PRIMARY KEY, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL);")

class BaseHandler(RequestHandler):
    def get_current_user(self):
        return self.get_secure_cookie("session")

class IndexHandler(BaseHandler):
    def get(self):
        if not self.current_user:
            self.write("not logged in")
            return
        count = db.execute("SELECT COUNT(*) FROM Users;").fetchone()
        self.write('{} users so far!'.format(count[0]))

class LoginHandler(BaseHandler):
    def get(self):
        if self.current_user:
            self.write("already logged in")
            return
        self.render("login.html")
    def post(self):
        if self.current_user:
            self.write("already logged in")
            return
        name=self.get_body_argument("username")
        query= db.execute("SELECT COUNT(*) FROM Users WHERE username = ?;", (name,)).fetchone()
        if query[0] == 0:
            self.write("user does not exist")
        else:
            hashed_password = db.execute("SELECT (password) FROM Users WHERE username = ?;", (name,)).fetchone()[0]
            if verify_password(hashed_password, self.get_body_argument("password")):
                self.set_secure_cookie("session", name)
                self.write("cookie set, logged in")
            else:
                self.write("wrong password")

class SignupHandler(BaseHandler):
    def get(self):
        if self.current_user:
            self.write("already logged in")
            return
        self.render("signup.html")
    def post(self):
        if self.current_user:
            self.write("already logged in")
            return
        name=self.get_body_argument("username")
        password=self.get_body_argument("password")
        try:
            with db:
                db.execute("INSERT INTO Users(username,password) VALUES (?,?);", (name, hash_password(password)))
        except sqlite3.IntegrityError:
            self.write("user exists")
            return
        self.write("user added")

class LogoutHandler(BaseHandler):
    def get(self):
        self.clear_cookie("session")
        self.write("logged out")

def main():
    routes=(
            (r'/', IndexHandler),
            (r'/login', LoginHandler),
            (r'/logout', LogoutHandler),
            (r'/signup', SignupHandler),
        )
    app = Application(routes, **settings)
    app.listen(options.port)
    IOLoop.current().start()

if __name__=="__main__":
    main()

1 Ответ

1 голос
/ 09 февраля 2020

Краткий ответ: Нет.

Asyn c код не делает вещи "быстрее". Код Asyn c - это обычный код syn c с некоторыми дополнительными возможностями для приостановки / возобновления операций. Там нет увеличения скорости. Назначение асин c кода не в скорости, а в достижении параллелизма без дополнительных потоков.


См. Эти две функции в следующем коде:

def func1():
    data = get_data_from_database()
    return data


async def func2():
    data = await get_data_from_database()
    return data

func1 является синхронным, а func2 является асинхронным. Обе функции будут иметь одинаковую скорость, потому что они обе должны ждать, пока база данных вернет данные.

Итак, вы можете сделать свой код асинхронным c, но это не приведет к увеличению скорости, потому что база данных будет возвращать данные с обычной скоростью, и только после этого ваш код сможет выполнять дальнейшие действия.


И не используйте SQLite с Tornado. Он работает в том же Python процессе, что и ваш код Tornado. А поскольку он будет считывать и записывать данные на диск / с диска, это приведет к более медленной блокировке кода, который заблокирует весь сервер Tornado и приведет к снижению производительности. Ниже приведено объяснение «Код блокировки».

Теперь, да, вы можете сделать его асинхронным, запустив его в отдельном потоке, но тогда почему бы просто не использовать автономную базу данных, такую ​​как PostgreSQL или MySQL, во-первых?


Код блокировки

Код, который не позволяет программе двигаться дальше или делает что-либо еще, называется кодом блокировки.

Код блокировки может быть любым из следующие типы:

  1. Связанные с сетью операции. Например, вы делаете запрос http или запрос к базе данных, это операция, связанная с сетью, и она медленнее. И это приводит к блокировке кода, поскольку код не может двигаться вперед, пока не получит ответ.
  2. Операции с диском. Например, если вы читаете файл с диска, это приводит к блокировке кода, потому что, если диск занят или работает медленно, ваш код не может двигаться вперед, пока не получит данные с диска.
  3. Операции с привязкой к процессору , Например, делать действительно тяжелые вычисления, которые занимают значительное время процессора. Это приведет к блокировке кода, поскольку код не может двигаться вперед, пока не получит результат вычисления от ЦП.

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

...